netfilter: xtables: compat out of scope fix
[safe/jmp/linux-2.6] / net / ipv4 / netfilter / arp_tables.c
index b9f7243..90203e1 100644 (file)
@@ -8,7 +8,7 @@
  * Copyright (C) 2002 David S. Miller (davem@redhat.com)
  *
  */
-
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 #include <linux/kernel.h>
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
@@ -384,11 +384,11 @@ static int mark_source_chains(struct xt_table_info *newinfo,
                                |= ((1 << hook) | (1 << NF_ARP_NUMHOOKS));
 
                        /* Unconditional return/END. */
-                       if ((e->target_offset == sizeof(struct arpt_entry)
-                           && (strcmp(t->target.u.user.name,
-                                      ARPT_STANDARD_TARGET) == 0)
-                           && t->verdict < 0
-                           && unconditional(&e->arp)) || visited) {
+                       if ((e->target_offset == sizeof(struct arpt_entry) &&
+                            (strcmp(t->target.u.user.name,
+                                    ARPT_STANDARD_TARGET) == 0) &&
+                            t->verdict < 0 && unconditional(&e->arp)) ||
+                           visited) {
                                unsigned int oldpos, size;
 
                                if ((strcmp(t->target.u.user.name,
@@ -427,8 +427,8 @@ static int mark_source_chains(struct xt_table_info *newinfo,
                                int newpos = t->verdict;
 
                                if (strcmp(t->target.u.user.name,
-                                          ARPT_STANDARD_TARGET) == 0
-                                   && newpos >= 0) {
+                                          ARPT_STANDARD_TARGET) == 0 &&
+                                   newpos >= 0) {
                                        if (newpos > newinfo->size -
                                                sizeof(struct arpt_entry)) {
                                                duprintf("mark_source_chains: "
@@ -533,18 +533,34 @@ out:
        return ret;
 }
 
+static bool check_underflow(struct arpt_entry *e)
+{
+       const struct arpt_entry_target *t;
+       unsigned int verdict;
+
+       if (!unconditional(&e->arp))
+               return false;
+       t = arpt_get_target(e);
+       if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
+               return false;
+       verdict = ((struct arpt_standard_target *)t)->verdict;
+       verdict = -verdict - 1;
+       return verdict == NF_DROP || verdict == NF_ACCEPT;
+}
+
 static inline int check_entry_size_and_hooks(struct arpt_entry *e,
                                             struct xt_table_info *newinfo,
                                             unsigned char *base,
                                             unsigned char *limit,
                                             const unsigned int *hook_entries,
                                             const unsigned int *underflows,
+                                            unsigned int valid_hooks,
                                             unsigned int *i)
 {
        unsigned int h;
 
-       if ((unsigned long)e % __alignof__(struct arpt_entry) != 0
-           || (unsigned char *)e + sizeof(struct arpt_entry) >= limit) {
+       if ((unsigned long)e % __alignof__(struct arpt_entry) != 0 ||
+           (unsigned char *)e + sizeof(struct arpt_entry) >= limit) {
                duprintf("Bad offset %p\n", e);
                return -EINVAL;
        }
@@ -558,15 +574,21 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
 
        /* Check hooks & underflows */
        for (h = 0; h < NF_ARP_NUMHOOKS; h++) {
+               if (!(valid_hooks & (1 << h)))
+                       continue;
                if ((unsigned char *)e - base == hook_entries[h])
                        newinfo->hook_entry[h] = hook_entries[h];
-               if ((unsigned char *)e - base == underflows[h])
+               if ((unsigned char *)e - base == underflows[h]) {
+                       if (!check_underflow(e)) {
+                               pr_err("Underflows must be unconditional and "
+                                      "use the STANDARD target with "
+                                      "ACCEPT/DROP\n");
+                               return -EINVAL;
+                       }
                        newinfo->underflow[h] = underflows[h];
+               }
        }
 
-       /* FIXME: underflows must be unconditional, standard verdicts
-          < 0 (not ARPT_RETURN). --RR */
-
        /* Clear counters and comefrom */
        e->counters = ((struct xt_counters) { 0, 0 });
        e->comefrom = 0;
@@ -626,7 +648,7 @@ static int translate_table(const char *name,
                                 newinfo,
                                 entry0,
                                 entry0 + size,
-                                hook_entries, underflows, &i);
+                                hook_entries, underflows, valid_hooks, &i);
        duprintf("translate_table: ARPT_ENTRY_ITERATE gives %d\n", ret);
        if (ret != 0)
                return ret;
@@ -903,10 +925,10 @@ static int get_info(struct net *net, void __user *user, int *len, int compat)
        if (t && !IS_ERR(t)) {
                struct arpt_getinfo info;
                const struct xt_table_info *private = t->private;
-
 #ifdef CONFIG_COMPAT
+               struct xt_table_info tmp;
+
                if (compat) {
-                       struct xt_table_info tmp;
                        ret = compat_table_info(private, &tmp);
                        xt_compat_flush_offsets(NFPROTO_ARP);
                        private = &tmp;
@@ -1229,8 +1251,8 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
        int ret, off, h;
 
        duprintf("check_compat_entry_size_and_hooks %p\n", e);
-       if ((unsigned long)e % __alignof__(struct compat_arpt_entry) != 0
-           || (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit) {
+       if ((unsigned long)e % __alignof__(struct compat_arpt_entry) != 0 ||
+           (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit) {
                duprintf("Bad offset %p, limit = %p\n", e, limit);
                return -EINVAL;
        }
@@ -1756,7 +1778,8 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len
        return ret;
 }
 
-struct xt_table *arpt_register_table(struct net *net, struct xt_table *table,
+struct xt_table *arpt_register_table(struct net *net,
+                                    const struct xt_table *table,
                                     const struct arpt_replace *repl)
 {
        int ret;