| // 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 */ |