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