]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/ipv4/netfilter/arp_tables.c
netfilter: arp_tables: simplify translate_compat_table args
[karo-tx-linux.git] / net / ipv4 / netfilter / arp_tables.c
index 11dccba474b7964fe7d9ee472c48d5eca435ae41..fc52763830854609359009190766c9fde5eb6c34 100644 (file)
@@ -359,11 +359,24 @@ unsigned int arpt_do_table(struct sk_buff *skb,
 }
 
 /* All zeroes == unconditional rule. */
-static inline bool unconditional(const struct arpt_arp *arp)
+static inline bool unconditional(const struct arpt_entry *e)
 {
        static const struct arpt_arp uncond;
 
-       return memcmp(arp, &uncond, sizeof(uncond)) == 0;
+       return e->target_offset == sizeof(struct arpt_entry) &&
+              memcmp(&e->arp, &uncond, sizeof(uncond)) == 0;
+}
+
+static bool find_jump_target(const struct xt_table_info *t,
+                            const struct arpt_entry *target)
+{
+       struct arpt_entry *iter;
+
+       xt_entry_foreach(iter, t->entries, t->size) {
+                if (iter == target)
+                       return true;
+       }
+       return false;
 }
 
 /* Figures out from what hook each rule can be called: returns 0 if
@@ -402,11 +415,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
                                |= ((1 << hook) | (1 << NF_ARP_NUMHOOKS));
 
                        /* Unconditional return/END. */
-                       if ((e->target_offset == sizeof(struct arpt_entry) &&
+                       if ((unconditional(e) &&
                             (strcmp(t->target.u.user.name,
                                     XT_STANDARD_TARGET) == 0) &&
-                            t->verdict < 0 && unconditional(&e->arp)) ||
-                           visited) {
+                            t->verdict < 0) || visited) {
                                unsigned int oldpos, size;
 
                                if ((strcmp(t->target.u.user.name,
@@ -439,6 +451,8 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
                                size = e->next_offset;
                                e = (struct arpt_entry *)
                                        (entry0 + pos + size);
+                               if (pos + size >= newinfo->size)
+                                       return 0;
                                e->counters.pcnt = pos;
                                pos += size;
                        } else {
@@ -458,9 +472,15 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
                                        /* This a jump; chase it. */
                                        duprintf("Jump rule %u -> %u\n",
                                                 pos, newpos);
+                                       e = (struct arpt_entry *)
+                                               (entry0 + newpos);
+                                       if (!find_jump_target(newinfo, e))
+                                               return 0;
                                } else {
                                        /* ... this is a fallthru */
                                        newpos = pos + e->next_offset;
+                                       if (newpos >= newinfo->size)
+                                               return 0;
                                }
                                e = (struct arpt_entry *)
                                        (entry0 + newpos);
@@ -474,25 +494,6 @@ next:
        return 1;
 }
 
-static inline int check_entry(const struct arpt_entry *e, const char *name)
-{
-       const struct xt_entry_target *t;
-
-       if (!arp_checkentry(&e->arp)) {
-               duprintf("arp_tables: arp check failed %p %s.\n", e, name);
-               return -EINVAL;
-       }
-
-       if (e->target_offset + sizeof(struct xt_entry_target) > e->next_offset)
-               return -EINVAL;
-
-       t = arpt_get_target_c(e);
-       if (e->target_offset + t->u.target_size > e->next_offset)
-               return -EINVAL;
-
-       return 0;
-}
-
 static inline int check_target(struct arpt_entry *e, const char *name)
 {
        struct xt_entry_target *t = arpt_get_target(e);
@@ -522,10 +523,6 @@ find_check_entry(struct arpt_entry *e, const char *name, unsigned int size)
        struct xt_target *target;
        int ret;
 
-       ret = check_entry(e, name);
-       if (ret)
-               return ret;
-
        e->counters.pcnt = xt_percpu_counter_alloc();
        if (IS_ERR_VALUE(e->counters.pcnt))
                return -ENOMEM;
@@ -557,7 +554,7 @@ static bool check_underflow(const struct arpt_entry *e)
        const struct xt_entry_target *t;
        unsigned int verdict;
 
-       if (!unconditional(&e->arp))
+       if (!unconditional(e))
                return false;
        t = arpt_get_target_c(e);
        if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
@@ -576,9 +573,11 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
                                             unsigned int valid_hooks)
 {
        unsigned int h;
+       int err;
 
        if ((unsigned long)e % __alignof__(struct arpt_entry) != 0 ||
-           (unsigned char *)e + sizeof(struct arpt_entry) >= limit) {
+           (unsigned char *)e + sizeof(struct arpt_entry) >= limit ||
+           (unsigned char *)e + e->next_offset > limit) {
                duprintf("Bad offset %p\n", e);
                return -EINVAL;
        }
@@ -590,6 +589,14 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
                return -EINVAL;
        }
 
+       if (!arp_checkentry(&e->arp))
+               return -EINVAL;
+
+       err = xt_check_entry_offsets(e, e->elems, e->target_offset,
+                                    e->next_offset);
+       if (err)
+               return err;
+
        /* Check hooks & underflows */
        for (h = 0; h < NF_ARP_NUMHOOKS; h++) {
                if (!(valid_hooks & (1 << h)))
@@ -598,9 +605,9 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
                        newinfo->hook_entry[h] = hook_entries[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");
+                               pr_debug("Underflows must be unconditional and "
+                                        "use the STANDARD target with "
+                                        "ACCEPT/DROP\n");
                                return -EINVAL;
                        }
                        newinfo->underflow[h] = underflows[h];
@@ -691,10 +698,8 @@ static int translate_table(struct xt_table_info *newinfo, void *entry0,
                }
        }
 
-       if (!mark_source_chains(newinfo, repl->valid_hooks, entry0)) {
-               duprintf("Looping hook\n");
+       if (!mark_source_chains(newinfo, repl->valid_hooks, entry0))
                return -ELOOP;
-       }
 
        /* Finally, each sanity check must pass */
        i = 0;
@@ -1208,6 +1213,18 @@ static int do_add_counters(struct net *net, const void __user *user,
 }
 
 #ifdef CONFIG_COMPAT
+struct compat_arpt_replace {
+       char                            name[XT_TABLE_MAXNAMELEN];
+       u32                             valid_hooks;
+       u32                             num_entries;
+       u32                             size;
+       u32                             hook_entry[NF_ARP_NUMHOOKS];
+       u32                             underflow[NF_ARP_NUMHOOKS];
+       u32                             num_counters;
+       compat_uptr_t                   counters;
+       struct compat_arpt_entry        entries[0];
+};
+
 static inline void compat_release_entry(struct compat_arpt_entry *e)
 {
        struct xt_entry_target *t;
@@ -1223,8 +1240,7 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
                                  const unsigned char *base,
                                  const unsigned char *limit,
                                  const unsigned int *hook_entries,
-                                 const unsigned int *underflows,
-                                 const char *name)
+                                 const unsigned int *underflows)
 {
        struct xt_entry_target *t;
        struct xt_target *target;
@@ -1233,7 +1249,8 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
 
        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) {
+           (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit ||
+           (unsigned char *)e + e->next_offset > limit) {
                duprintf("Bad offset %p, limit = %p\n", e, limit);
                return -EINVAL;
        }
@@ -1245,8 +1262,11 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
                return -EINVAL;
        }
 
-       /* For purposes of check_entry casting the compat entry is fine */
-       ret = check_entry((struct arpt_entry *)e, name);
+       if (!arp_checkentry(&e->arp))
+               return -EINVAL;
+
+       ret = xt_compat_check_entry_offsets(e, e->elems, e->target_offset,
+                                           e->next_offset);
        if (ret)
                return ret;
 
@@ -1291,7 +1311,7 @@ out:
 
 static int
 compat_copy_entry_from_user(struct compat_arpt_entry *e, void **dstptr,
-                           unsigned int *size, const char *name,
+                           unsigned int *size,
                            struct xt_table_info *newinfo, unsigned char *base)
 {
        struct xt_entry_target *t;
@@ -1324,14 +1344,9 @@ compat_copy_entry_from_user(struct compat_arpt_entry *e, void **dstptr,
        return ret;
 }
 
-static int translate_compat_table(const char *name,
-                                 unsigned int valid_hooks,
-                                 struct xt_table_info **pinfo,
+static int translate_compat_table(struct xt_table_info **pinfo,
                                  void **pentry0,
-                                 unsigned int total_size,
-                                 unsigned int number,
-                                 unsigned int *hook_entries,
-                                 unsigned int *underflows)
+                                 const struct compat_arpt_replace *compatr)
 {
        unsigned int i, j;
        struct xt_table_info *newinfo, *info;
@@ -1343,8 +1358,8 @@ static int translate_compat_table(const char *name,
 
        info = *pinfo;
        entry0 = *pentry0;
-       size = total_size;
-       info->number = number;
+       size = compatr->size;
+       info->number = compatr->num_entries;
 
        /* Init all hooks to impossible value. */
        for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
@@ -1355,40 +1370,39 @@ static int translate_compat_table(const char *name,
        duprintf("translate_compat_table: size %u\n", info->size);
        j = 0;
        xt_compat_lock(NFPROTO_ARP);
-       xt_compat_init_offsets(NFPROTO_ARP, number);
+       xt_compat_init_offsets(NFPROTO_ARP, compatr->num_entries);
        /* Walk through entries, checking offsets. */
-       xt_entry_foreach(iter0, entry0, total_size) {
+       xt_entry_foreach(iter0, entry0, compatr->size) {
                ret = check_compat_entry_size_and_hooks(iter0, info, &size,
                                                        entry0,
-                                                       entry0 + total_size,
-                                                       hook_entries,
-                                                       underflows,
-                                                       name);
+                                                       entry0 + compatr->size,
+                                                       compatr->hook_entry,
+                                                       compatr->underflow);
                if (ret != 0)
                        goto out_unlock;
                ++j;
        }
 
        ret = -EINVAL;
-       if (j != number) {
+       if (j != compatr->num_entries) {
                duprintf("translate_compat_table: %u not %u entries\n",
-                        j, number);
+                        j, compatr->num_entries);
                goto out_unlock;
        }
 
        /* Check hooks all assigned */
        for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
                /* Only hooks which are valid */
-               if (!(valid_hooks & (1 << i)))
+               if (!(compatr->valid_hooks & (1 << i)))
                        continue;
                if (info->hook_entry[i] == 0xFFFFFFFF) {
                        duprintf("Invalid hook entry %u %u\n",
-                                i, hook_entries[i]);
+                                i, info->hook_entry[i]);
                        goto out_unlock;
                }
                if (info->underflow[i] == 0xFFFFFFFF) {
                        duprintf("Invalid underflow %u %u\n",
-                                i, underflows[i]);
+                                i, info->underflow[i]);
                        goto out_unlock;
                }
        }
@@ -1398,17 +1412,17 @@ static int translate_compat_table(const char *name,
        if (!newinfo)
                goto out_unlock;
 
-       newinfo->number = number;
+       newinfo->number = compatr->num_entries;
        for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
                newinfo->hook_entry[i] = info->hook_entry[i];
                newinfo->underflow[i] = info->underflow[i];
        }
        entry1 = newinfo->entries;
        pos = entry1;
-       size = total_size;
-       xt_entry_foreach(iter0, entry0, total_size) {
+       size = compatr->size;
+       xt_entry_foreach(iter0, entry0, compatr->size) {
                ret = compat_copy_entry_from_user(iter0, &pos, &size,
-                                                 name, newinfo, entry1);
+                                                 newinfo, entry1);
                if (ret != 0)
                        break;
        }
@@ -1418,7 +1432,7 @@ static int translate_compat_table(const char *name,
                goto free_newinfo;
 
        ret = -ELOOP;
-       if (!mark_source_chains(newinfo, valid_hooks, entry1))
+       if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
                goto free_newinfo;
 
        i = 0;
@@ -1429,7 +1443,7 @@ static int translate_compat_table(const char *name,
                        break;
                }
 
-               ret = check_target(iter1, name);
+               ret = check_target(iter1, compatr->name);
                if (ret != 0) {
                        xt_percpu_counter_free(iter1->counters.pcnt);
                        break;
@@ -1471,7 +1485,7 @@ static int translate_compat_table(const char *name,
 free_newinfo:
        xt_free_table_info(newinfo);
 out:
-       xt_entry_foreach(iter0, entry0, total_size) {
+       xt_entry_foreach(iter0, entry0, compatr->size) {
                if (j-- == 0)
                        break;
                compat_release_entry(iter0);
@@ -1483,18 +1497,6 @@ out_unlock:
        goto out;
 }
 
-struct compat_arpt_replace {
-       char                            name[XT_TABLE_MAXNAMELEN];
-       u32                             valid_hooks;
-       u32                             num_entries;
-       u32                             size;
-       u32                             hook_entry[NF_ARP_NUMHOOKS];
-       u32                             underflow[NF_ARP_NUMHOOKS];
-       u32                             num_counters;
-       compat_uptr_t                   counters;
-       struct compat_arpt_entry        entries[0];
-};
-
 static int compat_do_replace(struct net *net, void __user *user,
                             unsigned int len)
 {
@@ -1527,10 +1529,7 @@ static int compat_do_replace(struct net *net, void __user *user,
                goto free_newinfo;
        }
 
-       ret = translate_compat_table(tmp.name, tmp.valid_hooks,
-                                    &newinfo, &loc_cpu_entry, tmp.size,
-                                    tmp.num_entries, tmp.hook_entry,
-                                    tmp.underflow);
+       ret = translate_compat_table(&newinfo, &loc_cpu_entry, &tmp);
        if (ret != 0)
                goto free_newinfo;