]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/ipv4/netfilter/ip_tables.c
[NETFILTER]: ip_tables: compat code module refcounting fix
[karo-tx-linux.git] / net / ipv4 / netfilter / ip_tables.c
index 78a44b01c03516bf18aecd65b7191800c42da947..8a455439b128258de3e42926640834975579d5af 100644 (file)
@@ -547,12 +547,18 @@ check_entry(struct ipt_entry *e, const char *name, unsigned int size,
                return -EINVAL;
        }
 
+       if (e->target_offset + sizeof(struct ipt_entry_target) > e->next_offset)
+               return -EINVAL;
+
        j = 0;
        ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom, &j);
        if (ret != 0)
                goto cleanup_matches;
 
        t = ipt_get_target(e);
+       ret = -EINVAL;
+       if (e->target_offset + t->u.target_size > e->next_offset)
+                       goto cleanup_matches;
        target = try_then_request_module(xt_find_target(AF_INET,
                                                     t->u.user.name,
                                                     t->u.user.revision),
@@ -712,19 +718,17 @@ translate_table(const char *name,
                }
        }
 
-       if (!mark_source_chains(newinfo, valid_hooks, entry0))
-               return -ELOOP;
-
        /* Finally, each sanity check must pass */
        i = 0;
        ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
                                check_entry, name, size, &i);
 
-       if (ret != 0) {
-               IPT_ENTRY_ITERATE(entry0, newinfo->size,
-                                 cleanup_entry, &i);
-               return ret;
-       }
+       if (ret != 0)
+               goto cleanup;
+
+       ret = -ELOOP;
+       if (!mark_source_chains(newinfo, valid_hooks, entry0))
+               goto cleanup;
 
        /* And one copy for every other CPU */
        for_each_possible_cpu(i) {
@@ -732,6 +736,9 @@ translate_table(const char *name,
                        memcpy(newinfo->entries[i], entry0, newinfo->size);
        }
 
+       return 0;
+cleanup:
+       IPT_ENTRY_ITERATE(entry0, newinfo->size, cleanup_entry, &i);
        return ret;
 }
 
@@ -1463,6 +1470,10 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e,
                return -EINVAL;
        }
 
+       if (e->target_offset + sizeof(struct compat_xt_entry_target) >
+                                                               e->next_offset)
+               return -EINVAL;
+
        off = 0;
        entry_offset = (void *)e - (void *)base;
        j = 0;
@@ -1472,6 +1483,9 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e,
                goto cleanup_matches;
 
        t = ipt_get_target(e);
+       ret = -EINVAL;
+       if (e->target_offset + t->u.target_size > e->next_offset)
+                       goto cleanup_matches;
        target = try_then_request_module(xt_find_target(AF_INET,
                                                     t->u.user.name,
                                                     t->u.user.revision),
@@ -1513,7 +1527,7 @@ cleanup_matches:
 
 static inline int compat_copy_match_from_user(struct ipt_entry_match *m,
        void **dstptr, compat_uint_t *size, const char *name,
-       const struct ipt_ip *ip, unsigned int hookmask, int *i)
+       const struct ipt_ip *ip, unsigned int hookmask)
 {
        struct ipt_entry_match *dm;
        struct ipt_match *match;
@@ -1526,22 +1540,13 @@ static inline int compat_copy_match_from_user(struct ipt_entry_match *m,
        ret = xt_check_match(match, AF_INET, dm->u.match_size - sizeof(*dm),
                             name, hookmask, ip->proto,
                             ip->invflags & IPT_INV_PROTO);
-       if (ret)
-               goto err;
-
-       if (m->u.kernel.match->checkentry
+       if (!ret && m->u.kernel.match->checkentry
            && !m->u.kernel.match->checkentry(name, ip, match, dm->data,
                                              hookmask)) {
                duprintf("ip_tables: check failed for `%s'.\n",
                         m->u.kernel.match->name);
                ret = -EINVAL;
-               goto err;
        }
-       (*i)++;
-       return 0;
-
-err:
-       module_put(m->u.kernel.match->me);
        return ret;
 }
 
@@ -1553,19 +1558,18 @@ static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr,
        struct ipt_target *target;
        struct ipt_entry *de;
        unsigned int origsize;
-       int ret, h, j;
+       int ret, h;
 
        ret = 0;
        origsize = *size;
        de = (struct ipt_entry *)*dstptr;
        memcpy(de, e, sizeof(struct ipt_entry));
 
-       j = 0;
        *dstptr += sizeof(struct compat_ipt_entry);
        ret = IPT_MATCH_ITERATE(e, compat_copy_match_from_user, dstptr, size,
-                       name, &de->ip, de->comefrom, &j);
+                       name, &de->ip, de->comefrom);
        if (ret)
-               goto cleanup_matches;
+               goto err;
        de->target_offset = e->target_offset - (origsize - *size);
        t = ipt_get_target(e);
        target = t->u.kernel.target;
@@ -1599,12 +1603,7 @@ static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr,
                goto err;
        }
        ret = 0;
-       return ret;
-
 err:
-       module_put(t->u.kernel.target->me);
-cleanup_matches:
-       IPT_MATCH_ITERATE(e, cleanup_match, &j);
        return ret;
 }
 
@@ -1618,7 +1617,7 @@ translate_compat_table(const char *name,
                unsigned int *hook_entries,
                unsigned int *underflows)
 {
-       unsigned int i;
+       unsigned int i, j;
        struct xt_table_info *newinfo, *info;
        void *pos, *entry0, *entry1;
        unsigned int size;
@@ -1636,21 +1635,21 @@ translate_compat_table(const char *name,
        }
 
        duprintf("translate_compat_table: size %u\n", info->size);
-       i = 0;
+       j = 0;
        xt_compat_lock(AF_INET);
        /* Walk through entries, checking offsets. */
        ret = IPT_ENTRY_ITERATE(entry0, total_size,
                                check_compat_entry_size_and_hooks,
                                info, &size, entry0,
                                entry0 + total_size,
-                               hook_entries, underflows, &i, name);
+                               hook_entries, underflows, &j, name);
        if (ret != 0)
                goto out_unlock;
 
        ret = -EINVAL;
-       if (i != number) {
+       if (j != number) {
                duprintf("translate_compat_table: %u not %u entries\n",
-                        i, number);
+                        j, number);
                goto out_unlock;
        }
 
@@ -1709,8 +1708,10 @@ translate_compat_table(const char *name,
 free_newinfo:
        xt_free_table_info(newinfo);
 out:
+       IPT_ENTRY_ITERATE(entry0, total_size, cleanup_entry, &j);
        return ret;
 out_unlock:
+       compat_flush_offsets();
        xt_compat_unlock(AF_INET);
        goto out;
 }
@@ -1932,6 +1933,9 @@ compat_do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
 {
        int ret;
 
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
        switch (cmd) {
        case IPT_SO_GET_INFO:
                ret = get_info(user, len, 1);