171 lines · 3.8 KB · C
RawDownload
// 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 */