#include <linux/pid_namespace.h>
#include <linux/file.h>
#include <linux/ctype.h>
+#include <linux/netdevice.h>
+#include <linux/slab.h>
#ifdef CONFIG_SYSCTL_SYSCALL
static const struct bin_table *get_sysctl(const int *name, int nlen, char *path)
{
const struct bin_table *table = &bin_root_table[0];
- struct net *net = current->nsproxy->net_ns;
int ctl_name;
+ /* The binary sysctl tables have a small maximum depth so
+ * there is no danger of overflowing our path as it PATH_MAX
+ * bytes long.
+ */
memcpy(path, "sys/", 4);
path += 4;
name++;
nlen--;
for ( ; table->convert; table++) {
- struct net_device *dev = NULL;
- const char *procname = NULL;
-
- /* Use the well known sysctl number to proc name mapping */
- if (ctl_name == table->ctl_name)
- procname = table->procname;
+ int len = 0;
/*
* For a wild card entry map from ifindex to network
* device name.
*/
- else if (!table->ctl_name) {
+ if (!table->ctl_name) {
+#ifdef CONFIG_NET
+ struct net *net = current->nsproxy->net_ns;
+ struct net_device *dev;
dev = dev_get_by_index(net, ctl_name);
- if (dev)
- procname = dev->name;
+ if (dev) {
+ len = strlen(dev->name);
+ memcpy(path, dev->name, len);
+ dev_put(dev);
+ }
+#endif
+ /* Use the well known sysctl number to proc name mapping */
+ } else if (ctl_name == table->ctl_name) {
+ len = strlen(table->procname);
+ memcpy(path, table->procname, len);
}
- if (procname) {
- int len;
-
- len = strlen(procname);
- memcpy(path, procname, len);
+ if (len) {
path += len;
- if (dev)
- dev_put(dev);
if (table->child) {
*path++ = '/';
table = table->child;
ssize_t result;
char *pathname;
int flags;
- int acc_mode, fmode;
+ int acc_mode;
pathname = sysctl_getname(name, nlen, &table);
result = PTR_ERR(pathname);
if (oldval && oldlen && newval && newlen) {
flags = O_RDWR;
acc_mode = MAY_READ | MAY_WRITE;
- fmode = FMODE_READ | FMODE_WRITE;
} else if (newval && newlen) {
flags = O_WRONLY;
acc_mode = MAY_WRITE;
- fmode = FMODE_WRITE;
} else if (oldval && oldlen) {
flags = O_RDONLY;
acc_mode = MAY_READ;
- fmode = FMODE_READ;
} else {
result = 0;
goto out_putname;
if (result)
goto out_putname;
- result = may_open(&nd.path, acc_mode, fmode);
+ result = may_open(&nd.path, acc_mode, flags);
if (result)
goto out_putpath;
static void deprecated_sysctl_warning(const int *name, int nlen)
{
- static int msg_count;
int i;
- /* Ignore accesses to kernel.version */
- if ((nlen == 2) && (name[0] == CTL_KERN) && (name[1] == KERN_VERSION))
+ /*
+ * CTL_KERN/KERN_VERSION is used by older glibc and cannot
+ * ever go away.
+ */
+ if (name[0] == CTL_KERN && name[1] == KERN_VERSION)
return;
- if (msg_count < 5) {
- msg_count++;
+ if (printk_ratelimit()) {
printk(KERN_INFO
"warning: process `%s' used the deprecated sysctl "
"system call with ", current->comm);
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)
{
if (get_user(name[i], args_name + i))
return -EFAULT;
- deprecated_sysctl_warning(name, nlen);
+ warn_on_bintable(name, nlen);
return binary_sysctl(name, nlen, oldval, oldlen, newval, newlen);
}