Linux 6.9-rc5
[sfrench/cifs-2.6.git] / kernel / sysctl_binary.c
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
21 static 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
27 static 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
52 static 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  */
65 static 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
78 static 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
97 SYSCALL_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
129 struct 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
139 COMPAT_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 */