linux/kernel/sysctl_check.c
<<
>>
Prefs
   1#include <linux/stat.h>
   2#include <linux/sysctl.h>
   3#include "../fs/xfs/linux-2.6/xfs_sysctl.h"
   4#include <linux/sunrpc/debug.h>
   5#include <linux/string.h>
   6#include <net/ip_vs.h>
   7
   8
   9static int sysctl_depth(struct ctl_table *table)
  10{
  11        struct ctl_table *tmp;
  12        int depth;
  13
  14        depth = 0;
  15        for (tmp = table; tmp->parent; tmp = tmp->parent)
  16                depth++;
  17
  18        return depth;
  19}
  20
  21static struct ctl_table *sysctl_parent(struct ctl_table *table, int n)
  22{
  23        int i;
  24
  25        for (i = 0; table && i < n; i++)
  26                table = table->parent;
  27
  28        return table;
  29}
  30
  31
  32static void sysctl_print_path(struct ctl_table *table)
  33{
  34        struct ctl_table *tmp;
  35        int depth, i;
  36        depth = sysctl_depth(table);
  37        if (table->procname) {
  38                for (i = depth; i >= 0; i--) {
  39                        tmp = sysctl_parent(table, i);
  40                        printk("/%s", tmp->procname?tmp->procname:"");
  41                }
  42        }
  43        printk(" ");
  44}
  45
  46static struct ctl_table *sysctl_check_lookup(struct nsproxy *namespaces,
  47                                                struct ctl_table *table)
  48{
  49        struct ctl_table_header *head;
  50        struct ctl_table *ref, *test;
  51        int depth, cur_depth;
  52
  53        depth = sysctl_depth(table);
  54
  55        for (head = __sysctl_head_next(namespaces, NULL); head;
  56             head = __sysctl_head_next(namespaces, head)) {
  57                cur_depth = depth;
  58                ref = head->ctl_table;
  59repeat:
  60                test = sysctl_parent(table, cur_depth);
  61                for (; ref->procname; ref++) {
  62                        int match = 0;
  63                        if (cur_depth && !ref->child)
  64                                continue;
  65
  66                        if (test->procname && ref->procname &&
  67                            (strcmp(test->procname, ref->procname) == 0))
  68                                        match++;
  69
  70                        if (match) {
  71                                if (cur_depth != 0) {
  72                                        cur_depth--;
  73                                        ref = ref->child;
  74                                        goto repeat;
  75                                }
  76                                goto out;
  77                        }
  78                }
  79        }
  80        ref = NULL;
  81out:
  82        sysctl_head_finish(head);
  83        return ref;
  84}
  85
  86static void set_fail(const char **fail, struct ctl_table *table, const char *str)
  87{
  88        if (*fail) {
  89                printk(KERN_ERR "sysctl table check failed: ");
  90                sysctl_print_path(table);
  91                printk(" %s\n", *fail);
  92                dump_stack();
  93        }
  94        *fail = str;
  95}
  96
  97static void sysctl_check_leaf(struct nsproxy *namespaces,
  98                                struct ctl_table *table, const char **fail)
  99{
 100        struct ctl_table *ref;
 101
 102        ref = sysctl_check_lookup(namespaces, table);
 103        if (ref && (ref != table))
 104                set_fail(fail, table, "Sysctl already exists");
 105}
 106
 107int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table)
 108{
 109        int error = 0;
 110        for (; table->procname; table++) {
 111                const char *fail = NULL;
 112
 113                if (table->parent) {
 114                        if (table->procname && !table->parent->procname)
 115                                set_fail(&fail, table, "Parent without procname");
 116                }
 117                if (!table->procname)
 118                        set_fail(&fail, table, "No procname");
 119                if (table->child) {
 120                        if (table->data)
 121                                set_fail(&fail, table, "Directory with data?");
 122                        if (table->maxlen)
 123                                set_fail(&fail, table, "Directory with maxlen?");
 124                        if ((table->mode & (S_IRUGO|S_IXUGO)) != table->mode)
 125                                set_fail(&fail, table, "Writable sysctl directory");
 126                        if (table->proc_handler)
 127                                set_fail(&fail, table, "Directory with proc_handler");
 128                        if (table->extra1)
 129                                set_fail(&fail, table, "Directory with extra1");
 130                        if (table->extra2)
 131                                set_fail(&fail, table, "Directory with extra2");
 132                } else {
 133                        if ((table->proc_handler == proc_dostring) ||
 134                            (table->proc_handler == proc_dointvec) ||
 135                            (table->proc_handler == proc_dointvec_minmax) ||
 136                            (table->proc_handler == proc_dointvec_jiffies) ||
 137                            (table->proc_handler == proc_dointvec_userhz_jiffies) ||
 138                            (table->proc_handler == proc_dointvec_ms_jiffies) ||
 139                            (table->proc_handler == proc_doulongvec_minmax) ||
 140                            (table->proc_handler == proc_doulongvec_ms_jiffies_minmax)) {
 141                                if (!table->data)
 142                                        set_fail(&fail, table, "No data");
 143                                if (!table->maxlen)
 144                                        set_fail(&fail, table, "No maxlen");
 145                        }
 146#ifdef CONFIG_PROC_SYSCTL
 147                        if (table->procname && !table->proc_handler)
 148                                set_fail(&fail, table, "No proc_handler");
 149#endif
 150#if 0
 151                        if (!table->procname && table->proc_handler)
 152                                set_fail(&fail, table, "proc_handler without procname");
 153#endif
 154                        sysctl_check_leaf(namespaces, table, &fail);
 155                }
 156                if (table->mode > 0777)
 157                        set_fail(&fail, table, "bogus .mode");
 158                if (fail) {
 159                        set_fail(&fail, table, NULL);
 160                        error = -EINVAL;
 161                }
 162                if (table->child)
 163                        error |= sysctl_check_table(namespaces, table->child);
 164        }
 165        return error;
 166}
 167