netfilter: ebtables: split copy_everything_to_user into two functions
[platform/kernel/linux-arm64.git] / net / bridge / netfilter / ebtables.c
index 0b7f262..46030dc 100644 (file)
 #define BUGPRINT(format, args...) printk("kernel msg: ebtables bug: please "\
                                         "report to author: "format, ## args)
 /* #define BUGPRINT(format, args...) */
-#define MEMPRINT(format, args...) printk("kernel msg: ebtables "\
-                                        ": out of memory: "format, ## args)
-/* #define MEMPRINT(format, args...) */
-
-
 
 /*
  * Each cpu has its own set of counters, so there is no need for write_lock in
@@ -82,7 +77,8 @@ static inline int ebt_do_match (struct ebt_entry_match *m,
        return m->u.match->match(skb, par) ? EBT_MATCH : EBT_NOMATCH;
 }
 
-static inline int ebt_dev_check(char *entry, const struct net_device *device)
+static inline int
+ebt_dev_check(const char *entry, const struct net_device *device)
 {
        int i = 0;
        const char *devname;
@@ -100,8 +96,9 @@ static inline int ebt_dev_check(char *entry, const struct net_device *device)
 
 #define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
 /* process standard matches */
-static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
-   const struct net_device *in, const struct net_device *out)
+static inline int
+ebt_basic_match(const struct ebt_entry *e, const struct ethhdr *h,
+                const struct net_device *in, const struct net_device *out)
 {
        int verdict, i;
 
@@ -156,12 +153,12 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb,
        int i, nentries;
        struct ebt_entry *point;
        struct ebt_counter *counter_base, *cb_base;
-       struct ebt_entry_target *t;
+       const struct ebt_entry_target *t;
        int verdict, sp = 0;
        struct ebt_chainstack *cs;
        struct ebt_entries *chaininfo;
-       char *base;
-       struct ebt_table_info *private;
+       const char *base;
+       const struct ebt_table_info *private;
        bool hotdrop = false;
        struct xt_match_param mtpar;
        struct xt_target_param tgpar;
@@ -395,7 +392,7 @@ ebt_check_watcher(struct ebt_entry_watcher *w, struct xt_tgchk_param *par,
        return 0;
 }
 
-static int ebt_verify_pointers(struct ebt_replace *repl,
+static int ebt_verify_pointers(const struct ebt_replace *repl,
                               struct ebt_table_info *newinfo)
 {
        unsigned int limit = repl->entries_size;
@@ -442,6 +439,8 @@ static int ebt_verify_pointers(struct ebt_replace *repl,
                                break;
                        if (left < e->next_offset)
                                break;
+                       if (e->next_offset < sizeof(struct ebt_entry))
+                               return -EINVAL;
                        offset += e->next_offset;
                }
        }
@@ -466,8 +465,8 @@ static int ebt_verify_pointers(struct ebt_replace *repl,
  * to parse the userspace data
  */
 static inline int
-ebt_check_entry_size_and_hooks(struct ebt_entry *e,
-   struct ebt_table_info *newinfo,
+ebt_check_entry_size_and_hooks(const struct ebt_entry *e,
+   const struct ebt_table_info *newinfo,
    unsigned int *n, unsigned int *cnt,
    unsigned int *totalcnt, unsigned int *udc_cnt)
 {
@@ -561,13 +560,14 @@ ebt_get_udc_positions(struct ebt_entry *e, struct ebt_table_info *newinfo,
 }
 
 static inline int
-ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
+ebt_cleanup_match(struct ebt_entry_match *m, struct net *net, unsigned int *i)
 {
        struct xt_mtdtor_param par;
 
        if (i && (*i)-- == 0)
                return 1;
 
+       par.net       = net;
        par.match     = m->u.match;
        par.matchinfo = m->data;
        par.family    = NFPROTO_BRIDGE;
@@ -578,13 +578,14 @@ ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
 }
 
 static inline int
-ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
+ebt_cleanup_watcher(struct ebt_entry_watcher *w, struct net *net, unsigned int *i)
 {
        struct xt_tgdtor_param par;
 
        if (i && (*i)-- == 0)
                return 1;
 
+       par.net      = net;
        par.target   = w->u.watcher;
        par.targinfo = w->data;
        par.family   = NFPROTO_BRIDGE;
@@ -595,7 +596,7 @@ ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
 }
 
 static inline int
-ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
+ebt_cleanup_entry(struct ebt_entry *e, struct net *net, unsigned int *cnt)
 {
        struct xt_tgdtor_param par;
        struct ebt_entry_target *t;
@@ -605,10 +606,11 @@ ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
        /* we're done */
        if (cnt && (*cnt)-- == 0)
                return 1;
-       EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
-       EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
+       EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, net, NULL);
+       EBT_MATCH_ITERATE(e, ebt_cleanup_match, net, NULL);
        t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
 
+       par.net      = net;
        par.target   = t->u.target;
        par.targinfo = t->data;
        par.family   = NFPROTO_BRIDGE;
@@ -619,7 +621,8 @@ 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,
+ebt_check_entry(struct ebt_entry *e, struct net *net,
+   const struct ebt_table_info *newinfo,
    const char *name, unsigned int *cnt,
    struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
 {
@@ -671,6 +674,7 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
        }
        i = 0;
 
+       mtpar.net       = tgpar.net       = net;
        mtpar.table     = tgpar.table     = name;
        mtpar.entryinfo = tgpar.entryinfo = e;
        mtpar.hook_mask = tgpar.hook_mask = hookmask;
@@ -726,9 +730,9 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
        (*cnt)++;
        return 0;
 cleanup_watchers:
-       EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
+       EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, net, &j);
 cleanup_matches:
-       EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
+       EBT_MATCH_ITERATE(e, ebt_cleanup_match, net, &i);
        return ret;
 }
 
@@ -737,12 +741,12 @@ cleanup_matches:
  * the hook mask for udc tells us from which base chains the udc can be
  * accessed. This mask is a parameter to the check() functions of the extensions
  */
-static int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
+static int check_chainloops(const struct ebt_entries *chain, struct ebt_cl_stack *cl_s,
    unsigned int udc_cnt, unsigned int hooknr, char *base)
 {
        int i, chain_nr = -1, pos = 0, nentries = chain->nentries, verdict;
-       struct ebt_entry *e = (struct ebt_entry *)chain->data;
-       struct ebt_entry_target *t;
+       const struct ebt_entry *e = (struct ebt_entry *)chain->data;
+       const struct ebt_entry_target *t;
 
        while (pos < nentries || chain_nr != -1) {
                /* end of udc, go back one 'recursion' step */
@@ -808,7 +812,8 @@ letscontinue:
 }
 
 /* do the parsing of the table/chains/entries/matches/watchers/targets, heh */
-static int translate_table(char *name, struct ebt_table_info *newinfo)
+static int translate_table(struct net *net, const char *name,
+                          struct ebt_table_info *newinfo)
 {
        unsigned int i, j, k, udc_cnt;
        int ret;
@@ -917,17 +922,17 @@ static int translate_table(char *name, struct ebt_table_info *newinfo)
        /* 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, name, &i, cl_s, udc_cnt);
+          ebt_check_entry, net, newinfo, name, &i, cl_s, udc_cnt);
        if (ret != 0) {
                EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
-                  ebt_cleanup_entry, &i);
+                                 ebt_cleanup_entry, net, &i);
        }
        vfree(cl_s);
        return ret;
 }
 
 /* called under write_lock */
-static void get_counters(struct ebt_counter *oldcounters,
+static void get_counters(const struct ebt_counter *oldcounters,
    struct ebt_counter *counters, unsigned int nentries)
 {
        int i, cpu;
@@ -949,90 +954,45 @@ static void get_counters(struct ebt_counter *oldcounters,
        }
 }
 
-/* replace the table */
-static int do_replace(struct net *net, void __user *user, unsigned int len)
+static int do_replace_finish(struct net *net, struct ebt_replace *repl,
+                             struct ebt_table_info *newinfo)
 {
-       int ret, i, countersize;
-       struct ebt_table_info *newinfo;
-       struct ebt_replace tmp;
-       struct ebt_table *t;
+       int ret, i;
        struct ebt_counter *counterstmp = NULL;
        /* used to be able to unlock earlier */
        struct ebt_table_info *table;
-
-       if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
-               return -EFAULT;
-
-       if (len != sizeof(tmp) + tmp.entries_size) {
-               BUGPRINT("Wrong len argument\n");
-               return -EINVAL;
-       }
-
-       if (tmp.entries_size == 0) {
-               BUGPRINT("Entries_size never zero\n");
-               return -EINVAL;
-       }
-       /* overflow check */
-       if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) / NR_CPUS -
-                       SMP_CACHE_BYTES) / sizeof(struct ebt_counter))
-               return -ENOMEM;
-       if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
-               return -ENOMEM;
-
-       countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids;
-       newinfo = vmalloc(sizeof(*newinfo) + countersize);
-       if (!newinfo)
-               return -ENOMEM;
-
-       if (countersize)
-               memset(newinfo->counters, 0, countersize);
-
-       newinfo->entries = vmalloc(tmp.entries_size);
-       if (!newinfo->entries) {
-               ret = -ENOMEM;
-               goto free_newinfo;
-       }
-       if (copy_from_user(
-          newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
-               BUGPRINT("Couldn't copy entries from userspace\n");
-               ret = -EFAULT;
-               goto free_entries;
-       }
+       struct ebt_table *t;
 
        /* the user wants counters back
           the check on the size is done later, when we have the lock */
-       if (tmp.num_counters) {
-               counterstmp = vmalloc(tmp.num_counters * sizeof(*counterstmp));
-               if (!counterstmp) {
-                       ret = -ENOMEM;
-                       goto free_entries;
-               }
+       if (repl->num_counters) {
+               unsigned long size = repl->num_counters * sizeof(*counterstmp);
+               counterstmp = vmalloc(size);
+               if (!counterstmp)
+                       return -ENOMEM;
        }
-       else
-               counterstmp = NULL;
 
-       /* this can get initialized by translate_table() */
        newinfo->chainstack = NULL;
-       ret = ebt_verify_pointers(&tmp, newinfo);
+       ret = ebt_verify_pointers(repl, newinfo);
        if (ret != 0)
                goto free_counterstmp;
 
-       ret = translate_table(tmp.name, newinfo);
+       ret = translate_table(net, repl->name, newinfo);
 
        if (ret != 0)
                goto free_counterstmp;
 
-       t = find_table_lock(net, tmp.name, &ret, &ebt_mutex);
+       t = find_table_lock(net, repl->name, &ret, &ebt_mutex);
        if (!t) {
                ret = -ENOENT;
                goto free_iterate;
        }
 
        /* the table doesn't like it */
-       if (t->check && (ret = t->check(newinfo, tmp.valid_hooks)))
+       if (t->check && (ret = t->check(newinfo, repl->valid_hooks)))
                goto free_unlock;
 
-       if (tmp.num_counters && tmp.num_counters != t->private->nentries) {
+       if (repl->num_counters && repl->num_counters != t->private->nentries) {
                BUGPRINT("Wrong nr. of counters requested\n");
                ret = -EINVAL;
                goto free_unlock;
@@ -1048,7 +1008,7 @@ static int do_replace(struct net *net, void __user *user, unsigned int len)
                module_put(t->me);
        /* we need an atomic snapshot of the counters */
        write_lock_bh(&t->lock);
-       if (tmp.num_counters)
+       if (repl->num_counters)
                get_counters(t->private->counters, counterstmp,
                   t->private->nentries);
 
@@ -1059,10 +1019,9 @@ static int do_replace(struct net *net, void __user *user, unsigned int len)
           allocation. Only reason why this is done is because this way the lock
           is held only once, while this doesn't bring the kernel into a
           dangerous state. */
-       if (tmp.num_counters &&
-          copy_to_user(tmp.counters, counterstmp,
-          tmp.num_counters * sizeof(struct ebt_counter))) {
-               BUGPRINT("Couldn't copy counters to userspace\n");
+       if (repl->num_counters &&
+          copy_to_user(repl->counters, counterstmp,
+          repl->num_counters * sizeof(struct ebt_counter))) {
                ret = -EFAULT;
        }
        else
@@ -1070,7 +1029,7 @@ static int do_replace(struct net *net, void __user *user, unsigned int len)
 
        /* decrease module count and free resources */
        EBT_ENTRY_ITERATE(table->entries, table->entries_size,
-          ebt_cleanup_entry, NULL);
+                         ebt_cleanup_entry, net, NULL);
 
        vfree(table->entries);
        if (table->chainstack) {
@@ -1087,7 +1046,7 @@ free_unlock:
        mutex_unlock(&ebt_mutex);
 free_iterate:
        EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
-          ebt_cleanup_entry, NULL);
+                         ebt_cleanup_entry, net, NULL);
 free_counterstmp:
        vfree(counterstmp);
        /* can be initialized in translate_table() */
@@ -1096,6 +1055,59 @@ free_counterstmp:
                        vfree(newinfo->chainstack[i]);
                vfree(newinfo->chainstack);
        }
+       return ret;
+}
+
+/* replace the table */
+static int do_replace(struct net *net, const void __user *user,
+                     unsigned int len)
+{
+       int ret, countersize;
+       struct ebt_table_info *newinfo;
+       struct ebt_replace tmp;
+
+       if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
+               return -EFAULT;
+
+       if (len != sizeof(tmp) + tmp.entries_size) {
+               BUGPRINT("Wrong len argument\n");
+               return -EINVAL;
+       }
+
+       if (tmp.entries_size == 0) {
+               BUGPRINT("Entries_size never zero\n");
+               return -EINVAL;
+       }
+       /* overflow check */
+       if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) /
+                       NR_CPUS - SMP_CACHE_BYTES) / sizeof(struct ebt_counter))
+               return -ENOMEM;
+       if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
+               return -ENOMEM;
+
+       countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids;
+       newinfo = vmalloc(sizeof(*newinfo) + countersize);
+       if (!newinfo)
+               return -ENOMEM;
+
+       if (countersize)
+               memset(newinfo->counters, 0, countersize);
+
+       newinfo->entries = vmalloc(tmp.entries_size);
+       if (!newinfo->entries) {
+               ret = -ENOMEM;
+               goto free_newinfo;
+       }
+       if (copy_from_user(
+          newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
+               BUGPRINT("Couldn't copy entries from userspace\n");
+               ret = -EFAULT;
+               goto free_entries;
+       }
+
+       ret = do_replace_finish(net, &tmp, newinfo);
+       if (ret == 0)
+               return ret;
 free_entries:
        vfree(newinfo->entries);
 free_newinfo:
@@ -1154,7 +1166,7 @@ ebt_register_table(struct net *net, const struct ebt_table *input_table)
                        newinfo->hook_entry[i] = p +
                                ((char *)repl->hook_entry[i] - repl->entries);
        }
-       ret = translate_table(repl->name, newinfo);
+       ret = translate_table(net, repl->name, newinfo);
        if (ret != 0) {
                BUGPRINT("Translate_table failed\n");
                goto free_chainstack;
@@ -1204,7 +1216,7 @@ out:
        return ERR_PTR(ret);
 }
 
-void ebt_unregister_table(struct ebt_table *table)
+void ebt_unregister_table(struct net *net, struct ebt_table *table)
 {
        int i;
 
@@ -1216,7 +1228,7 @@ void ebt_unregister_table(struct ebt_table *table)
        list_del(&table->list);
        mutex_unlock(&ebt_mutex);
        EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size,
-                         ebt_cleanup_entry, NULL);
+                         ebt_cleanup_entry, net, NULL);
        if (table->private->nentries)
                module_put(table->me);
        vfree(table->private->entries);
@@ -1230,7 +1242,8 @@ void ebt_unregister_table(struct ebt_table *table)
 }
 
 /* userspace just supplied us with counters */
-static int update_counters(struct net *net, void __user *user, unsigned int len)
+static int update_counters(struct net *net, const void __user *user,
+                          unsigned int len)
 {
        int i, ret;
        struct ebt_counter *tmp;
@@ -1245,10 +1258,8 @@ static int update_counters(struct net *net, void __user *user, unsigned int len)
        if (hlp.num_counters == 0)
                return -EINVAL;
 
-       if (!(tmp = vmalloc(hlp.num_counters * sizeof(*tmp)))) {
-               MEMPRINT("Update_counters && nomemory\n");
+       if (!(tmp = vmalloc(hlp.num_counters * sizeof(*tmp))))
                return -ENOMEM;
-       }
 
        t = find_table_lock(net, hlp.name, &ret, &ebt_mutex);
        if (!t)
@@ -1285,8 +1296,8 @@ free_tmp:
        return ret;
 }
 
-static inline int ebt_make_matchname(struct ebt_entry_match *m,
-   char *base, char __user *ubase)
+static inline int ebt_make_matchname(const struct ebt_entry_match *m,
+    const char *base, char __user *ubase)
 {
        char __user *hlp = ubase + ((char *)m - base);
        if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
@@ -1294,8 +1305,8 @@ static inline int ebt_make_matchname(struct ebt_entry_match *m,
        return 0;
 }
 
-static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
-   char *base, char __user *ubase)
+static inline int ebt_make_watchername(const struct ebt_entry_watcher *w,
+    const char *base, char __user *ubase)
 {
        char __user *hlp = ubase + ((char *)w - base);
        if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
@@ -1303,11 +1314,12 @@ static inline int ebt_make_watchername(struct ebt_entry_watcher *w,
        return 0;
 }
 
-static inline int ebt_make_names(struct ebt_entry *e, char *base, char __user *ubase)
+static inline int
+ebt_make_names(struct ebt_entry *e, const char *base, char __user *ubase)
 {
        int ret;
        char __user *hlp;
-       struct ebt_entry_target *t;
+       const struct ebt_entry_target *t;
 
        if (e->bitmask == 0)
                return 0;
@@ -1326,13 +1338,46 @@ static inline int ebt_make_names(struct ebt_entry *e, char *base, char __user *u
        return 0;
 }
 
+static int copy_counters_to_user(struct ebt_table *t,
+                                 const struct ebt_counter *oldcounters,
+                                 void __user *user, unsigned int num_counters,
+                                 unsigned int nentries)
+{
+       struct ebt_counter *counterstmp;
+       int ret = 0;
+
+       /* userspace might not need the counters */
+       if (num_counters == 0)
+               return 0;
+
+       if (num_counters != nentries) {
+               BUGPRINT("Num_counters wrong\n");
+               return -EINVAL;
+       }
+
+       counterstmp = vmalloc(nentries * sizeof(*counterstmp));
+       if (!counterstmp)
+               return -ENOMEM;
+
+       write_lock_bh(&t->lock);
+       get_counters(oldcounters, counterstmp, nentries);
+       write_unlock_bh(&t->lock);
+
+       if (copy_to_user(user, counterstmp,
+          nentries * sizeof(struct ebt_counter)))
+               ret = -EFAULT;
+       vfree(counterstmp);
+       return ret;
+}
+
 /* called with ebt_mutex locked */
 static int copy_everything_to_user(struct ebt_table *t, void __user *user,
-   int *len, int cmd)
+    const int *len, int cmd)
 {
        struct ebt_replace tmp;
-       struct ebt_counter *counterstmp, *oldcounters;
+       const struct ebt_counter *oldcounters;
        unsigned int entries_size, nentries;
+       int ret;
        char *entries;
 
        if (cmd == EBT_SO_GET_ENTRIES) {
@@ -1368,29 +1413,10 @@ static int copy_everything_to_user(struct ebt_table *t, void __user *user,
                return -EINVAL;
        }
 
-       /* userspace might not need the counters */
-       if (tmp.num_counters) {
-               if (tmp.num_counters != nentries) {
-                       BUGPRINT("Num_counters wrong\n");
-                       return -EINVAL;
-               }
-               counterstmp = vmalloc(nentries * sizeof(*counterstmp));
-               if (!counterstmp) {
-                       MEMPRINT("Couldn't copy counters, out of memory\n");
-                       return -ENOMEM;
-               }
-               write_lock_bh(&t->lock);
-               get_counters(oldcounters, counterstmp, nentries);
-               write_unlock_bh(&t->lock);
-
-               if (copy_to_user(tmp.counters, counterstmp,
-                  nentries * sizeof(struct ebt_counter))) {
-                       BUGPRINT("Couldn't copy counters to userspace\n");
-                       vfree(counterstmp);
-                       return -EFAULT;
-               }
-               vfree(counterstmp);
-       }
+       ret = copy_counters_to_user(t, oldcounters, tmp.counters,
+                                       tmp.num_counters, nentries);
+       if (ret)
+               return ret;
 
        if (copy_to_user(tmp.entries, entries, entries_size)) {
                BUGPRINT("Couldn't copy entries to userspace\n");