2017-11-01 15:07:57 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2009-04-02 23:44:59 -07:00
|
|
|
#include <linux/stat.h>
|
|
|
|
|
#include <linux/sysctl.h>
|
2011-08-12 16:21:35 -05:00
|
|
|
#include "../fs/xfs/xfs_sysctl.h"
|
2009-04-02 23:44:59 -07:00
|
|
|
#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>
|
2009-11-12 00:35:55 -08:00
|
|
|
#include <linux/netdevice.h>
|
2010-05-24 14:33:26 -07:00
|
|
|
#include <linux/kernel.h>
|
2016-05-20 17:01:10 -07:00
|
|
|
#include <linux/uuid.h>
|
2010-03-24 17:04:11 +09:00
|
|
|
#include <linux/slab.h>
|
2013-03-19 14:25:51 -04:00
|
|
|
#include <linux/compat.h>
|
2009-04-02 23:44:59 -07:00
|
|
|
|
2009-11-05 05:26:41 -08:00
|
|
|
static ssize_t binary_sysctl(const int *name, int nlen,
|
|
|
|
|
void __user *oldval, size_t oldlen, void __user *newval, size_t newlen)
|
2009-04-02 23:44:59 -07:00
|
|
|
{
|
2009-04-03 00:09:33 -07:00
|
|
|
return -ENOSYS;
|
2009-04-02 23:44:59 -07:00
|
|
|
}
|
|
|
|
|
|
2009-04-03 00:09:33 -07:00
|
|
|
static void deprecated_sysctl_warning(const int *name, int nlen)
|
2009-04-02 23:44:59 -07:00
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
2009-12-16 12:28:44 +01:00
|
|
|
/*
|
|
|
|
|
* CTL_KERN/KERN_VERSION is used by older glibc and cannot
|
|
|
|
|
* ever go away.
|
|
|
|
|
*/
|
2017-07-12 14:34:01 -07:00
|
|
|
if (nlen >= 2 && name[0] == CTL_KERN && name[1] == KERN_VERSION)
|
2009-12-16 12:28:44 +01:00
|
|
|
return;
|
|
|
|
|
|
2009-11-11 19:32:48 -08:00
|
|
|
if (printk_ratelimit()) {
|
2009-04-02 23:44:59 -07:00
|
|
|
printk(KERN_INFO
|
|
|
|
|
"warning: process `%s' used the deprecated sysctl "
|
|
|
|
|
"system call with ", current->comm);
|
2009-04-03 00:09:33 -07:00
|
|
|
for (i = 0; i < nlen; i++)
|
2016-12-14 15:04:26 -08:00
|
|
|
printk(KERN_CONT "%d.", name[i]);
|
|
|
|
|
printk(KERN_CONT "\n");
|
2009-04-02 23:44:59 -07:00
|
|
|
}
|
2009-04-03 00:09:33 -07:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-23 21:00:20 +01:00
|
|
|
#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);
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-05 05:26:41 -08:00
|
|
|
static ssize_t do_sysctl(int __user *args_name, int nlen,
|
|
|
|
|
void __user *oldval, size_t oldlen, void __user *newval, size_t newlen)
|
2009-04-03 00:09:33 -07:00
|
|
|
{
|
|
|
|
|
int name[CTL_MAXNAME];
|
|
|
|
|
int i;
|
|
|
|
|
|
2009-11-05 05:26:41 -08:00
|
|
|
/* Check args->nlen. */
|
|
|
|
|
if (nlen < 0 || nlen > CTL_MAXNAME)
|
2009-04-03 00:09:33 -07:00
|
|
|
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;
|
|
|
|
|
|
2009-12-23 21:00:20 +01:00
|
|
|
warn_on_bintable(name, nlen);
|
2009-04-03 00:09:33 -07:00
|
|
|
|
2009-11-05 05:26:41 -08:00
|
|
|
return binary_sysctl(name, nlen, oldval, oldlen, newval, newlen);
|
2009-04-03 00:09:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SYSCALL_DEFINE1(sysctl, struct __sysctl_args __user *, args)
|
|
|
|
|
{
|
|
|
|
|
struct __sysctl_args tmp;
|
2009-11-05 05:26:41 -08:00
|
|
|
size_t oldlen = 0;
|
|
|
|
|
ssize_t result;
|
2009-04-03 00:09:33 -07:00
|
|
|
|
|
|
|
|
if (copy_from_user(&tmp, args, sizeof(tmp)))
|
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
2009-11-05 05:26:41 -08:00
|
|
|
if (tmp.oldval && !tmp.oldlenp)
|
|
|
|
|
return -EFAULT;
|
2009-04-03 00:09:33 -07:00
|
|
|
|
2009-11-05 05:26:41 -08:00
|
|
|
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;
|
2009-04-02 23:44:59 -07:00
|
|
|
}
|
2009-04-03 00:36:27 -07:00
|
|
|
|
2009-11-05 05:26:41 -08:00
|
|
|
|
2009-04-03 00:36:27 -07:00
|
|
|
#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];
|
|
|
|
|
};
|
|
|
|
|
|
2013-03-19 14:25:51 -04:00
|
|
|
COMPAT_SYSCALL_DEFINE1(sysctl, struct compat_sysctl_args __user *, args)
|
2009-04-03 00:36:27 -07:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
|
2009-11-05 05:26:41 -08:00
|
|
|
if (tmp.oldval && !tmp.oldlenp)
|
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
2009-04-03 00:36:27 -07:00
|
|
|
compat_oldlenp = compat_ptr(tmp.oldlenp);
|
2009-11-05 05:26:41 -08:00
|
|
|
if (compat_oldlenp && get_user(oldlen, compat_oldlenp))
|
|
|
|
|
return -EFAULT;
|
2009-04-03 00:36:27 -07:00
|
|
|
|
|
|
|
|
result = do_sysctl(compat_ptr(tmp.name), tmp.nlen,
|
2009-11-05 05:26:41 -08:00
|
|
|
compat_ptr(tmp.oldval), oldlen,
|
2009-04-03 00:36:27 -07:00
|
|
|
compat_ptr(tmp.newval), tmp.newlen);
|
|
|
|
|
|
2009-11-05 05:26:41 -08:00
|
|
|
if (result >= 0) {
|
|
|
|
|
oldlen = result;
|
|
|
|
|
result = 0;
|
2009-04-03 00:36:27 -07:00
|
|
|
}
|
|
|
|
|
|
2009-11-05 05:26:41 -08:00
|
|
|
if (compat_oldlenp && put_user(oldlen, compat_oldlenp))
|
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
2009-04-03 00:36:27 -07:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* CONFIG_COMPAT */
|