Merge branch 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus
[linux-drm-fsl-dcu.git] / net / bridge / netfilter / ebtables.c
index 9f85666f29f75dd322c6672f7e72b8433fbc52d2..5818d70b8e8d3bec53c5d6696beed715439d770d 100644 (file)
 #include "../br_private.h"
 
 #define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
-                                         "report to author: "format, ## args)
+                                        "report to author: "format, ## args)
 /* #define BUGPRINT(format, args...) */
 #define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
-                                         ": out of memory: "format, ## args)
+                                        ": out of memory: "format, ## args)
 /* #define MEMPRINT(format, args...) */
 
 
@@ -338,10 +338,11 @@ ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
    const char *name, unsigned int hookmask, unsigned int *cnt)
 {
        struct ebt_match *match;
+       size_t left = ((char *)e + e->watchers_offset) - (char *)m;
        int ret;
 
-       if (((char *)m) + m->match_size + sizeof(struct ebt_entry_match) >
-          ((char *)e) + e->watchers_offset)
+       if (left < sizeof(struct ebt_entry_match) ||
+           left - sizeof(struct ebt_entry_match) < m->match_size)
                return -EINVAL;
        match = find_match_lock(m->u.name, &ret, &ebt_mutex);
        if (!match)
@@ -367,10 +368,11 @@ ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
    const char *name, unsigned int hookmask, unsigned int *cnt)
 {
        struct ebt_watcher *watcher;
+       size_t left = ((char *)e + e->target_offset) - (char *)w;
        int ret;
 
-       if (((char *)w) + w->watcher_size + sizeof(struct ebt_entry_watcher) >
-          ((char *)e) + e->target_offset)
+       if (left < sizeof(struct ebt_entry_watcher) ||
+          left - sizeof(struct ebt_entry_watcher) < w->watcher_size)
                return -EINVAL;
        watcher = find_watcher_lock(w->u.name, &ret, &ebt_mutex);
        if (!watcher)
@@ -391,46 +393,96 @@ ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
        return 0;
 }
 
+static int ebt_verify_pointers(struct ebt_replace *repl,
+                              struct ebt_table_info *newinfo)
+{
+       unsigned int limit = repl->entries_size;
+       unsigned int valid_hooks = repl->valid_hooks;
+       unsigned int offset = 0;
+       int i;
+
+       for (i = 0; i < NF_BR_NUMHOOKS; i++)
+               newinfo->hook_entry[i] = NULL;
+
+       newinfo->entries_size = repl->entries_size;
+       newinfo->nentries = repl->nentries;
+
+       while (offset < limit) {
+               size_t left = limit - offset;
+               struct ebt_entry *e = (void *)newinfo->entries + offset;
+
+               if (left < sizeof(unsigned int))
+                       break;
+
+               for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+                       if ((valid_hooks & (1 << i)) == 0)
+                               continue;
+                       if ((char __user *)repl->hook_entry[i] ==
+                            repl->entries + offset)
+                               break;
+               }
+
+               if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
+                       if (e->bitmask != 0) {
+                               /* we make userspace set this right,
+                                  so there is no misunderstanding */
+                               BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
+                                        "in distinguisher\n");
+                               return -EINVAL;
+                       }
+                       if (i != NF_BR_NUMHOOKS)
+                               newinfo->hook_entry[i] = (struct ebt_entries *)e;
+                       if (left < sizeof(struct ebt_entries))
+                               break;
+                       offset += sizeof(struct ebt_entries);
+               } else {
+                       if (left < sizeof(struct ebt_entry))
+                               break;
+                       if (left < e->next_offset)
+                               break;
+                       offset += e->next_offset;
+               }
+       }
+       if (offset != limit) {
+               BUGPRINT("entries_size too small\n");
+               return -EINVAL;
+       }
+
+       /* check if all valid hooks have a chain */
+       for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+               if (!newinfo->hook_entry[i] &&
+                  (valid_hooks & (1 << i))) {
+                       BUGPRINT("Valid hook without chain\n");
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+
 /*
  * this one is very careful, as it is the first function
  * to parse the userspace data
  */
 static inline int
 ebt_check_entry_size_and_hooks(struct ebt_entry *e,
-   struct ebt_table_info *newinfo, char *base, char *limit,
-   struct ebt_entries **hook_entries, unsigned int *n, unsigned int *cnt,
-   unsigned int *totalcnt, unsigned int *udc_cnt, unsigned int valid_hooks)
+   struct ebt_table_info *newinfo,
+   unsigned int *n, unsigned int *cnt,
+   unsigned int *totalcnt, unsigned int *udc_cnt)
 {
        int i;
 
        for (i = 0; i < NF_BR_NUMHOOKS; i++) {
-               if ((valid_hooks & (1 << i)) == 0)
-                       continue;
-               if ( (char *)hook_entries[i] - base ==
-                  (char *)e - newinfo->entries)
+               if ((void *)e == (void *)newinfo->hook_entry[i])
                        break;
        }
        /* beginning of a new chain
           if i == NF_BR_NUMHOOKS it must be a user defined chain */
-       if (i != NF_BR_NUMHOOKS || !(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
-               if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) != 0) {
-                       /* we make userspace set this right,
-                          so there is no misunderstanding */
-                       BUGPRINT("EBT_ENTRY_OR_ENTRIES shouldn't be set "
-                                "in distinguisher\n");
-                       return -EINVAL;
-               }
+       if (i != NF_BR_NUMHOOKS || !e->bitmask) {
                /* this checks if the previous chain has as many entries
                   as it said it has */
                if (*n != *cnt) {
                        BUGPRINT("nentries does not equal the nr of entries "
-                                "in the chain\n");
-                       return -EINVAL;
-               }
-               /* before we look at the struct, be sure it is not too big */
-               if ((char *)hook_entries[i] + sizeof(struct ebt_entries)
-                  > limit) {
-                       BUGPRINT("entries_size too small\n");
+                                "in the chain\n");
                        return -EINVAL;
                }
                if (((struct ebt_entries *)e)->policy != EBT_DROP &&
@@ -444,8 +496,6 @@ ebt_check_entry_size_and_hooks(struct ebt_entry *e,
                }
                if (i == NF_BR_NUMHOOKS) /* it's a user defined chain */
                        (*udc_cnt)++;
-               else
-                       newinfo->hook_entry[i] = (struct ebt_entries *)e;
                if (((struct ebt_entries *)e)->counter_offset != *totalcnt) {
                        BUGPRINT("counter_offset != totalcnt");
                        return -EINVAL;
@@ -466,7 +516,6 @@ ebt_check_entry_size_and_hooks(struct ebt_entry *e,
                BUGPRINT("target size too small\n");
                return -EINVAL;
        }
-
        (*cnt)++;
        (*totalcnt)++;
        return 0;
@@ -485,17 +534,14 @@ struct ebt_cl_stack
  */
 static inline int
 ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
-   struct ebt_entries **hook_entries, unsigned int *n, unsigned int valid_hooks,
-   struct ebt_cl_stack *udc)
+   unsigned int *n, struct ebt_cl_stack *udc)
 {
        int i;
 
        /* we're only interested in chain starts */
-       if (e->bitmask & EBT_ENTRY_OR_ENTRIES)
+       if (e->bitmask)
                return 0;
        for (i = 0; i < NF_BR_NUMHOOKS; i++) {
-               if ((valid_hooks & (1 << i)) == 0)
-                       continue;
                if (newinfo->hook_entry[i] == (struct ebt_entries *)e)
                        break;
        }
@@ -541,7 +587,7 @@ ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
 {
        struct ebt_entry_target *t;
 
-       if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+       if (e->bitmask == 0)
                return 0;
        /* we're done */
        if (cnt && (*cnt)-- == 0)
@@ -558,16 +604,17 @@ ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
 
 static inline int
 ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
-   const char *name, unsigned int *cnt, unsigned int valid_hooks,
+   const char *name, unsigned int *cnt,
    struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
 {
        struct ebt_entry_target *t;
        struct ebt_target *target;
        unsigned int i, j, hook = 0, hookmask = 0;
+       size_t gap;
        int ret;
 
        /* don't mess with the struct ebt_entries */
-       if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+       if (e->bitmask == 0)
                return 0;
 
        if (e->bitmask & ~EBT_F_MASK) {
@@ -584,7 +631,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
        }
        /* what hook do we belong to? */
        for (i = 0; i < NF_BR_NUMHOOKS; i++) {
-               if ((valid_hooks & (1 << i)) == 0)
+               if (!newinfo->hook_entry[i])
                        continue;
                if ((char *)newinfo->hook_entry[i] < (char *)e)
                        hook = i;
@@ -613,6 +660,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
        if (ret != 0)
                goto cleanup_watchers;
        t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+       gap = e->next_offset - e->target_offset;
        target = find_target_lock(t->u.name, &ret, &ebt_mutex);
        if (!target)
                goto cleanup_watchers;
@@ -625,8 +673,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
 
        t->u.target = target;
        if (t->u.target == &ebt_standard_target) {
-               if (e->target_offset + sizeof(struct ebt_standard_target) >
-                  e->next_offset) {
+               if (gap < sizeof(struct ebt_standard_target)) {
                        BUGPRINT("Standard target size too big\n");
                        ret = -EFAULT;
                        goto cleanup_watchers;
@@ -637,8 +684,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
                        ret = -EFAULT;
                        goto cleanup_watchers;
                }
-       } else if ((e->target_offset + t->target_size +
-          sizeof(struct ebt_entry_target) > e->next_offset) ||
+       } else if (t->target_size > gap - sizeof(struct ebt_entry_target) ||
           (t->u.target->check &&
           t->u.target->check(name, hookmask, e, t->data, t->target_size) != 0)){
                module_put(t->u.target->me);
@@ -708,7 +754,9 @@ static int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s
                                BUGPRINT("loop\n");
                                return -1;
                        }
-                       /* this can't be 0, so the above test is correct */
+                       if (cl_s[i].hookmask & (1 << hooknr))
+                               goto letscontinue;
+                       /* this can't be 0, so the loop test is correct */
                        cl_s[i].cs.n = pos + 1;
                        pos = 0;
                        cl_s[i].cs.e = ((void *)e + e->next_offset);
@@ -728,59 +776,51 @@ letscontinue:
 }
 
 /* do the parsing of the table/chains/entries/matches/watchers/targets, heh */
-static int translate_table(struct ebt_replace *repl,
-   struct ebt_table_info *newinfo)
+static int translate_table(char *name, struct ebt_table_info *newinfo)
 {
        unsigned int i, j, k, udc_cnt;
        int ret;
        struct ebt_cl_stack *cl_s = NULL; /* used in the checking for chain loops */
 
        i = 0;
-       while (i < NF_BR_NUMHOOKS && !(repl->valid_hooks & (1 << i)))
+       while (i < NF_BR_NUMHOOKS && !newinfo->hook_entry[i])
                i++;
        if (i == NF_BR_NUMHOOKS) {
                BUGPRINT("No valid hooks specified\n");
                return -EINVAL;
        }
-       if (repl->hook_entry[i] != (struct ebt_entries *)repl->entries) {
+       if (newinfo->hook_entry[i] != (struct ebt_entries *)newinfo->entries) {
                BUGPRINT("Chains don't start at beginning\n");
                return -EINVAL;
        }
        /* make sure chains are ordered after each other in same order
           as their corresponding hooks */
        for (j = i + 1; j < NF_BR_NUMHOOKS; j++) {
-               if (!(repl->valid_hooks & (1 << j)))
+               if (!newinfo->hook_entry[j])
                        continue;
-               if ( repl->hook_entry[j] <= repl->hook_entry[i] ) {
+               if (newinfo->hook_entry[j] <= newinfo->hook_entry[i]) {
                        BUGPRINT("Hook order must be followed\n");
                        return -EINVAL;
                }
                i = j;
        }
 
-       for (i = 0; i < NF_BR_NUMHOOKS; i++)
-               newinfo->hook_entry[i] = NULL;
-
-       newinfo->entries_size = repl->entries_size;
-       newinfo->nentries = repl->nentries;
-
        /* do some early checkings and initialize some things */
        i = 0; /* holds the expected nr. of entries for the chain */
        j = 0; /* holds the up to now counted entries for the chain */
        k = 0; /* holds the total nr. of entries, should equal
-                 newinfo->nentries afterwards */
+                 newinfo->nentries afterwards */
        udc_cnt = 0; /* will hold the nr. of user defined chains (udc) */
        ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
-          ebt_check_entry_size_and_hooks, newinfo, repl->entries,
-          repl->entries + repl->entries_size, repl->hook_entry, &i, &j, &k,
-          &udc_cnt, repl->valid_hooks);
+          ebt_check_entry_size_and_hooks, newinfo,
+          &i, &j, &k, &udc_cnt);
 
        if (ret != 0)
                return ret;
 
        if (i != j) {
                BUGPRINT("nentries does not equal the nr of entries in the "
-                        "(last) chain\n");
+                        "(last) chain\n");
                return -EINVAL;
        }
        if (k != newinfo->nentries) {
@@ -788,15 +828,6 @@ static int translate_table(struct ebt_replace *repl,
                return -EINVAL;
        }
 
-       /* check if all valid hooks have a chain */
-       for (i = 0; i < NF_BR_NUMHOOKS; i++) {
-               if (newinfo->hook_entry[i] == NULL &&
-                  (repl->valid_hooks & (1 << i))) {
-                       BUGPRINT("Valid hook without chain\n");
-                       return -EINVAL;
-               }
-       }
-
        /* get the location of the udc, put them in an array
           while we're at it, allocate the chainstack */
        if (udc_cnt) {
@@ -804,7 +835,7 @@ static int translate_table(struct ebt_replace *repl,
                   if an error occurs */
                newinfo->chainstack =
                        vmalloc((highest_possible_processor_id()+1)
-                                       * sizeof(*(newinfo->chainstack)));
+                                       * sizeof(*(newinfo->chainstack)));
                if (!newinfo->chainstack)
                        return -ENOMEM;
                for_each_possible_cpu(i) {
@@ -824,8 +855,7 @@ static int translate_table(struct ebt_replace *repl,
                        return -ENOMEM;
                i = 0; /* the i'th udc */
                EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
-                  ebt_get_udc_positions, newinfo, repl->hook_entry, &i,
-                  repl->valid_hooks, cl_s);
+                  ebt_get_udc_positions, newinfo, &i, cl_s);
                /* sanity check */
                if (i != udc_cnt) {
                        BUGPRINT("i != udc_cnt\n");
@@ -836,7 +866,7 @@ static int translate_table(struct ebt_replace *repl,
 
        /* Check for loops */
        for (i = 0; i < NF_BR_NUMHOOKS; i++)
-               if (repl->valid_hooks & (1 << i))
+               if (newinfo->hook_entry[i])
                        if (check_chainloops(newinfo->hook_entry[i],
                           cl_s, udc_cnt, i, newinfo->entries)) {
                                vfree(cl_s);
@@ -856,8 +886,7 @@ static int translate_table(struct ebt_replace *repl,
        /* used to know what we need to clean up if something goes wrong */
        i = 0;
        ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
-          ebt_check_entry, newinfo, repl->name, &i, repl->valid_hooks,
-          cl_s, udc_cnt);
+          ebt_check_entry, newinfo, name, &i, cl_s, udc_cnt);
        if (ret != 0) {
                EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
                   ebt_cleanup_entry, &i);
@@ -919,7 +948,7 @@ static int do_replace(void __user *user, unsigned int len)
        if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
                return -ENOMEM;
 
-       countersize = COUNTER_OFFSET(tmp.nentries) * 
+       countersize = COUNTER_OFFSET(tmp.nentries) *
                                        (highest_possible_processor_id()+1);
        newinfo = vmalloc(sizeof(*newinfo) + countersize);
        if (!newinfo)
@@ -954,7 +983,11 @@ static int do_replace(void __user *user, unsigned int len)
 
        /* this can get initialized by translate_table() */
        newinfo->chainstack = NULL;
-       ret = translate_table(&tmp, newinfo);
+       ret = ebt_verify_pointers(&tmp, newinfo);
+       if (ret != 0)
+               goto free_counterstmp;
+
+       ret = translate_table(tmp.name, newinfo);
 
        if (ret != 0)
                goto free_counterstmp;
@@ -1125,35 +1158,47 @@ int ebt_register_table(struct ebt_table *table)
 {
        struct ebt_table_info *newinfo;
        struct ebt_table *t;
+       struct ebt_replace_kernel *repl;
        int ret, i, countersize;
+       void *p;
 
-       if (!table || !table->table ||!table->table->entries ||
-           table->table->entries_size == 0 ||
-           table->table->counters || table->private) {
+       if (!table || !(repl = table->table) || !repl->entries ||
+           repl->entries_size == 0 ||
+           repl->counters || table->private) {
                BUGPRINT("Bad table data for ebt_register_table!!!\n");
                return -EINVAL;
        }
 
-       countersize = COUNTER_OFFSET(table->table->nentries) *
+       countersize = COUNTER_OFFSET(repl->nentries) *
                                        (highest_possible_processor_id()+1);
        newinfo = vmalloc(sizeof(*newinfo) + countersize);
        ret = -ENOMEM;
        if (!newinfo)
                return -ENOMEM;
 
-       newinfo->entries = vmalloc(table->table->entries_size);
-       if (!(newinfo->entries))
+       p = vmalloc(repl->entries_size);
+       if (!p)
                goto free_newinfo;
 
-       memcpy(newinfo->entries, table->table->entries,
-          table->table->entries_size);
+       memcpy(p, repl->entries, repl->entries_size);
+       newinfo->entries = p;
+
+       newinfo->entries_size = repl->entries_size;
+       newinfo->nentries = repl->nentries;
 
        if (countersize)
                memset(newinfo->counters, 0, countersize);
 
        /* fill in newinfo and parse the entries */
        newinfo->chainstack = NULL;
-       ret = translate_table(table->table, newinfo);
+       for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+               if ((repl->valid_hooks & (1 << i)) == 0)
+                       newinfo->hook_entry[i] = NULL;
+               else
+                       newinfo->hook_entry[i] = p +
+                               ((char *)repl->hook_entry[i] - repl->entries);
+       }
+       ret = translate_table(repl->name, newinfo);
        if (ret != 0) {
                BUGPRINT("Translate_table failed\n");
                goto free_chainstack;
@@ -1277,35 +1322,35 @@ free_tmp:
 }
 
 static inline int ebt_make_matchname(struct ebt_entry_match *m,
-   char *base, char *ubase)
+   char *base, char __user *ubase)
 {
-       char *hlp = ubase - base + (char *)m;
+       char __user *hlp = ubase + ((char *)m - base);
        if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
                return -EFAULT;
        return 0;
 }
 
 static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
-   char *base, char *ubase)
+   char *base, char __user *ubase)
 {
-       char *hlp = ubase - base + (char *)w;
+       char __user *hlp = ubase + ((char *)w - base);
        if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
                return -EFAULT;
        return 0;
 }
 
-static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
+static inline int ebt_make_names(struct ebt_entry *e, char *base, char __user *ubase)
 {
        int ret;
-       char *hlp;
+       char __user *hlp;
        struct ebt_entry_target *t;
 
-       if ((e->bitmask & EBT_ENTRY_OR_ENTRIES) == 0)
+       if (e->bitmask == 0)
                return 0;
 
-       hlp = ubase - base + (char *)e + e->target_offset;
+       hlp = ubase + (((char *)e + e->target_offset) - base);
        t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
-       
+
        ret = EBT_MATCH_ITERATE(e, ebt_make_matchname, base, ubase);
        if (ret != 0)
                return ret;