Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | // SPDX-License-Identifier: GPL-2.0 #include <linux/stat.h> #include <linux/sysctl.h> #include "../fs/xfs/xfs_sysctl.h" #include <linux/sunrpc/debug.h> #include <linux/string.h> #include <linux/syscalls.h> #include <linux/namei.h> #include <linux/mount.h> #include <linux/fs.h> #include <linux/nsproxy.h> #include <linux/pid_namespace.h> #include <linux/file.h> #include <linux/ctype.h> #include <linux/netdevice.h> #include <linux/kernel.h> #include <linux/uuid.h> #include <linux/slab.h> #include <linux/compat.h> static ssize_t binary_sysctl(const int *name, int nlen, void __user *oldval, size_t oldlen, void __user *newval, size_t newlen) { return -ENOSYS; } static void deprecated_sysctl_warning(const int *name, int nlen) { int i; /* * CTL_KERN/KERN_VERSION is used by older glibc and cannot * ever go away. */ if (nlen >= 2 && name[0] == CTL_KERN && name[1] == KERN_VERSION) return; if (printk_ratelimit()) { printk(KERN_INFO "warning: process `%s' used the deprecated sysctl " "system call with ", current->comm); for (i = 0; i < nlen; i++) printk(KERN_CONT "%d.", name[i]); printk(KERN_CONT "\n"); } return; } #define WARN_ONCE_HASH_BITS 8 #define WARN_ONCE_HASH_SIZE (1<<WARN_ONCE_HASH_BITS) static DECLARE_BITMAP(warn_once_bitmap, WARN_ONCE_HASH_SIZE); #define FNV32_OFFSET 2166136261U #define FNV32_PRIME 0x01000193 /* * Print each legacy sysctl (approximately) only once. * To avoid making the tables non-const use a external * hash-table instead. * Worst case hash collision: 6, but very rarely. * NOTE! We don't use the SMP-safe bit tests. We simply * don't care enough. */ static void warn_on_bintable(const int *name, int nlen) { int i; u32 hash = FNV32_OFFSET; for (i = 0; i < nlen; i++) hash = (hash ^ name[i]) * FNV32_PRIME; hash %= WARN_ONCE_HASH_SIZE; if (__test_and_set_bit(hash, warn_once_bitmap)) return; deprecated_sysctl_warning(name, nlen); } static ssize_t do_sysctl(int __user *args_name, int nlen, void __user *oldval, size_t oldlen, void __user *newval, size_t newlen) { int name[CTL_MAXNAME]; int i; /* Check args->nlen. */ if (nlen < 0 || nlen > CTL_MAXNAME) return -ENOTDIR; /* Read in the sysctl name for simplicity */ for (i = 0; i < nlen; i++) if (get_user(name[i], args_name + i)) return -EFAULT; warn_on_bintable(name, nlen); return binary_sysctl(name, nlen, oldval, oldlen, newval, newlen); } SYSCALL_DEFINE1(sysctl, struct __sysctl_args __user *, args) { struct __sysctl_args tmp; size_t oldlen = 0; ssize_t result; if (copy_from_user(&tmp, args, sizeof(tmp))) return -EFAULT; if (tmp.oldval && !tmp.oldlenp) return -EFAULT; if (tmp.oldlenp && get_user(oldlen, tmp.oldlenp)) return -EFAULT; result = do_sysctl(tmp.name, tmp.nlen, tmp.oldval, oldlen, tmp.newval, tmp.newlen); if (result >= 0) { oldlen = result; result = 0; } if (tmp.oldlenp && put_user(oldlen, tmp.oldlenp)) return -EFAULT; return result; } #ifdef CONFIG_COMPAT struct compat_sysctl_args { compat_uptr_t name; int nlen; compat_uptr_t oldval; compat_uptr_t oldlenp; compat_uptr_t newval; compat_size_t newlen; compat_ulong_t __unused[4]; }; COMPAT_SYSCALL_DEFINE1(sysctl, struct compat_sysctl_args __user *, args) { struct compat_sysctl_args tmp; compat_size_t __user *compat_oldlenp; size_t oldlen = 0; ssize_t result; if (copy_from_user(&tmp, args, sizeof(tmp))) return -EFAULT; if (tmp.oldval && !tmp.oldlenp) return -EFAULT; compat_oldlenp = compat_ptr(tmp.oldlenp); if (compat_oldlenp && get_user(oldlen, compat_oldlenp)) return -EFAULT; result = do_sysctl(compat_ptr(tmp.name), tmp.nlen, compat_ptr(tmp.oldval), oldlen, compat_ptr(tmp.newval), tmp.newlen); if (result >= 0) { oldlen = result; result = 0; } if (compat_oldlenp && put_user(oldlen, compat_oldlenp)) return -EFAULT; return result; } #endif /* CONFIG_COMPAT */ |