]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/ipv4/netfilter/ip_tables.c
netfilter: ip_tables: simplify translate_compat_table args
[karo-tx-linux.git] / net / ipv4 / netfilter / ip_tables.c
index ae55f23a851ce731499a210e277c6d4282c43d08..242bf9a6799ccc473a0efaf136bd07cffe11cd14 100644 (file)
@@ -168,11 +168,12 @@ get_entry(const void *base, unsigned int offset)
 
 /* All zeroes == unconditional rule. */
 /* Mildly perf critical (only if packet tracing is on) */
-static inline bool unconditional(const struct ipt_ip *ip)
+static inline bool unconditional(const struct ipt_entry *e)
 {
        static const struct ipt_ip uncond;
 
-       return memcmp(ip, &uncond, sizeof(uncond)) == 0;
+       return e->target_offset == sizeof(struct ipt_entry) &&
+              memcmp(&e->ip, &uncond, sizeof(uncond)) == 0;
 #undef FWINV
 }
 
@@ -229,11 +230,10 @@ get_chainname_rulenum(const struct ipt_entry *s, const struct ipt_entry *e,
        } else if (s == e) {
                (*rulenum)++;
 
-               if (s->target_offset == sizeof(struct ipt_entry) &&
+               if (unconditional(s) &&
                    strcmp(t->target.u.kernel.target->name,
                           XT_STANDARD_TARGET) == 0 &&
-                  t->verdict < 0 &&
-                  unconditional(&s->ip)) {
+                  t->verdict < 0) {
                        /* Tail of chains: STANDARD target (return/policy) */
                        *comment = *chainname == hookname
                                ? comments[NF_IP_TRACE_COMMENT_POLICY]
@@ -443,6 +443,18 @@ ipt_do_table(struct sk_buff *skb,
 #endif
 }
 
+static bool find_jump_target(const struct xt_table_info *t,
+                            const struct ipt_entry *target)
+{
+       struct ipt_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
    there are loops.  Puts hook bitmask in comefrom. */
 static int
@@ -476,11 +488,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
                        e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
 
                        /* Unconditional return/END. */
-                       if ((e->target_offset == sizeof(struct ipt_entry) &&
+                       if ((unconditional(e) &&
                             (strcmp(t->target.u.user.name,
                                     XT_STANDARD_TARGET) == 0) &&
-                            t->verdict < 0 && unconditional(&e->ip)) ||
-                           visited) {
+                            t->verdict < 0) || visited) {
                                unsigned int oldpos, size;
 
                                if ((strcmp(t->target.u.user.name,
@@ -521,6 +532,8 @@ mark_source_chains(const struct xt_table_info *newinfo,
                                size = e->next_offset;
                                e = (struct ipt_entry *)
                                        (entry0 + pos + size);
+                               if (pos + size >= newinfo->size)
+                                       return 0;
                                e->counters.pcnt = pos;
                                pos += size;
                        } else {
@@ -539,9 +552,15 @@ mark_source_chains(const struct xt_table_info *newinfo,
                                        /* This a jump; chase it. */
                                        duprintf("Jump rule %u -> %u\n",
                                                 pos, newpos);
+                                       e = (struct ipt_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 ipt_entry *)
                                        (entry0 + newpos);
@@ -568,25 +587,6 @@ static void cleanup_match(struct xt_entry_match *m, struct net *net)
        module_put(par.match->me);
 }
 
-static int
-check_entry(const struct ipt_entry *e)
-{
-       const struct xt_entry_target *t;
-
-       if (!ip_checkentry(&e->ip))
-               return -EINVAL;
-
-       if (e->target_offset + sizeof(struct xt_entry_target) >
-           e->next_offset)
-               return -EINVAL;
-
-       t = ipt_get_target_c(e);
-       if (e->target_offset + t->u.target_size > e->next_offset)
-               return -EINVAL;
-
-       return 0;
-}
-
 static int
 check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
 {
@@ -715,7 +715,7 @@ static bool check_underflow(const struct ipt_entry *e)
        const struct xt_entry_target *t;
        unsigned int verdict;
 
-       if (!unconditional(&e->ip))
+       if (!unconditional(e))
                return false;
        t = ipt_get_target_c(e);
        if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
@@ -738,7 +738,8 @@ check_entry_size_and_hooks(struct ipt_entry *e,
        int err;
 
        if ((unsigned long)e % __alignof__(struct ipt_entry) != 0 ||
-           (unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
+           (unsigned char *)e + sizeof(struct ipt_entry) >= limit ||
+           (unsigned char *)e + e->next_offset > limit) {
                duprintf("Bad offset %p\n", e);
                return -EINVAL;
        }
@@ -750,7 +751,11 @@ check_entry_size_and_hooks(struct ipt_entry *e,
                return -EINVAL;
        }
 
-       err = check_entry(e);
+       if (!ip_checkentry(&e->ip))
+               return -EINVAL;
+
+       err = xt_check_entry_offsets(e, e->elems, e->target_offset,
+                                    e->next_offset);
        if (err)
                return err;
 
@@ -762,9 +767,9 @@ check_entry_size_and_hooks(struct ipt_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];
@@ -1443,7 +1448,6 @@ compat_copy_entry_to_user(struct ipt_entry *e, void __user **dstptr,
 
 static int
 compat_find_calc_match(struct xt_entry_match *m,
-                      const char *name,
                       const struct ipt_ip *ip,
                       int *size)
 {
@@ -1480,8 +1484,7 @@ check_compat_entry_size_and_hooks(struct compat_ipt_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_match *ematch;
        struct xt_entry_target *t;
@@ -1492,7 +1495,8 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
 
        duprintf("check_compat_entry_size_and_hooks %p\n", e);
        if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 ||
-           (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) {
+           (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit ||
+           (unsigned char *)e + e->next_offset > limit) {
                duprintf("Bad offset %p, limit = %p\n", e, limit);
                return -EINVAL;
        }
@@ -1504,8 +1508,11 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
                return -EINVAL;
        }
 
-       /* For purposes of check_entry casting the compat entry is fine */
-       ret = check_entry((struct ipt_entry *)e);
+       if (!ip_checkentry(&e->ip))
+               return -EINVAL;
+
+       ret = xt_compat_check_entry_offsets(e, e->elems,
+                                           e->target_offset, e->next_offset);
        if (ret)
                return ret;
 
@@ -1513,7 +1520,7 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
        entry_offset = (void *)e - (void *)base;
        j = 0;
        xt_ematch_foreach(ematch, e) {
-               ret = compat_find_calc_match(ematch, name, &e->ip, &off);
+               ret = compat_find_calc_match(ematch, &e->ip, &off);
                if (ret != 0)
                        goto release_matches;
                ++j;
@@ -1562,7 +1569,7 @@ release_matches:
 
 static int
 compat_copy_entry_from_user(struct compat_ipt_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;
@@ -1645,14 +1652,9 @@ compat_check_entry(struct ipt_entry *e, struct net *net, const char *name)
 
 static int
 translate_compat_table(struct net *net,
-                      const char *name,
-                      unsigned int valid_hooks,
                       struct xt_table_info **pinfo,
                       void **pentry0,
-                      unsigned int total_size,
-                      unsigned int number,
-                      unsigned int *hook_entries,
-                      unsigned int *underflows)
+                      const struct compat_ipt_replace *compatr)
 {
        unsigned int i, j;
        struct xt_table_info *newinfo, *info;
@@ -1664,8 +1666,8 @@ translate_compat_table(struct net *net,
 
        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_INET_NUMHOOKS; i++) {
@@ -1676,40 +1678,39 @@ translate_compat_table(struct net *net,
        duprintf("translate_compat_table: size %u\n", info->size);
        j = 0;
        xt_compat_lock(AF_INET);
-       xt_compat_init_offsets(AF_INET, number);
+       xt_compat_init_offsets(AF_INET, 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_INET_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;
                }
        }
@@ -1719,17 +1720,17 @@ translate_compat_table(struct net *net,
        if (!newinfo)
                goto out_unlock;
 
-       newinfo->number = number;
+       newinfo->number = compatr->num_entries;
        for (i = 0; i < NF_INET_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;
        }
@@ -1739,12 +1740,12 @@ translate_compat_table(struct net *net,
                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;
        xt_entry_foreach(iter1, entry1, newinfo->size) {
-               ret = compat_check_entry(iter1, net, name);
+               ret = compat_check_entry(iter1, net, compatr->name);
                if (ret != 0)
                        break;
                ++i;
@@ -1784,7 +1785,7 @@ translate_compat_table(struct net *net,
 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);
@@ -1829,10 +1830,7 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len)
                goto free_newinfo;
        }
 
-       ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
-                                    &newinfo, &loc_cpu_entry, tmp.size,
-                                    tmp.num_entries, tmp.hook_entry,
-                                    tmp.underflow);
+       ret = translate_compat_table(net, &newinfo, &loc_cpu_entry, &tmp);
        if (ret != 0)
                goto free_newinfo;