]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 23 Jun 2016 04:47:15 +0000 (21:47 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 23 Jun 2016 04:47:15 +0000 (21:47 -0700)
added patches:
netfilter-arp_tables-simplify-translate_compat_table-args.patch
netfilter-ip6_tables-simplify-translate_compat_table-args.patch
netfilter-ip_tables-simplify-translate_compat_table-args.patch
netfilter-x_tables-do-compat-validation-via-translate_table.patch
netfilter-x_tables-introduce-and-use-xt_copy_counters_from_user.patch
netfilter-x_tables-xt_compat_match_from_user-doesn-t-need-a-retval.patch

queue-4.4/netfilter-arp_tables-simplify-translate_compat_table-args.patch [new file with mode: 0644]
queue-4.4/netfilter-ip6_tables-simplify-translate_compat_table-args.patch [new file with mode: 0644]
queue-4.4/netfilter-ip_tables-simplify-translate_compat_table-args.patch [new file with mode: 0644]
queue-4.4/netfilter-x_tables-do-compat-validation-via-translate_table.patch [new file with mode: 0644]
queue-4.4/netfilter-x_tables-introduce-and-use-xt_copy_counters_from_user.patch [new file with mode: 0644]
queue-4.4/netfilter-x_tables-xt_compat_match_from_user-doesn-t-need-a-retval.patch [new file with mode: 0644]
queue-4.4/series

diff --git a/queue-4.4/netfilter-arp_tables-simplify-translate_compat_table-args.patch b/queue-4.4/netfilter-arp_tables-simplify-translate_compat_table-args.patch
new file mode 100644 (file)
index 0000000..f4e4c4b
--- /dev/null
@@ -0,0 +1,216 @@
+From 8dddd32756f6fe8e4e82a63361119b7e2384e02f Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Apr 2016 14:17:32 +0200
+Subject: netfilter: arp_tables: simplify translate_compat_table args
+
+From: Florian Westphal <fw@strlen.de>
+
+commit 8dddd32756f6fe8e4e82a63361119b7e2384e02f upstream.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/ipv4/netfilter/arp_tables.c |   82 +++++++++++++++++-----------------------
+ 1 file changed, 36 insertions(+), 46 deletions(-)
+
+--- a/net/ipv4/netfilter/arp_tables.c
++++ b/net/ipv4/netfilter/arp_tables.c
+@@ -1213,6 +1213,18 @@ static int do_add_counters(struct net *n
+ }
+ #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;
+@@ -1228,8 +1240,7 @@ check_compat_entry_size_and_hooks(struct
+                                 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;
+@@ -1300,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;
+@@ -1333,14 +1344,9 @@ compat_copy_entry_from_user(struct compa
+       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;
+@@ -1352,8 +1358,8 @@ static int translate_compat_table(const
+       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++) {
+@@ -1364,40 +1370,39 @@ static int translate_compat_table(const
+       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;
+               }
+       }
+@@ -1407,17 +1412,17 @@ static int translate_compat_table(const
+       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;
+       }
+@@ -1427,7 +1432,7 @@ static int translate_compat_table(const
+               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;
+@@ -1438,7 +1443,7 @@ static int translate_compat_table(const
+                       break;
+               }
+-              ret = check_target(iter1, name);
++              ret = check_target(iter1, compatr->name);
+               if (ret != 0) {
+                       xt_percpu_counter_free(iter1->counters.pcnt);
+                       break;
+@@ -1480,7 +1485,7 @@ static int translate_compat_table(const
+ 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);
+@@ -1492,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)
+ {
+@@ -1536,10 +1529,7 @@ static int compat_do_replace(struct net
+               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;
diff --git a/queue-4.4/netfilter-ip6_tables-simplify-translate_compat_table-args.patch b/queue-4.4/netfilter-ip6_tables-simplify-translate_compat_table-args.patch
new file mode 100644 (file)
index 0000000..6503151
--- /dev/null
@@ -0,0 +1,191 @@
+From 329a0807124f12fe1c8032f95d8a8eb47047fb0e Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Apr 2016 14:17:31 +0200
+Subject: netfilter: ip6_tables: simplify translate_compat_table args
+
+From: Florian Westphal <fw@strlen.de>
+
+commit 329a0807124f12fe1c8032f95d8a8eb47047fb0e upstream.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/ipv6/netfilter/ip6_tables.c |   59 ++++++++++++++++------------------------
+ 1 file changed, 24 insertions(+), 35 deletions(-)
+
+--- a/net/ipv6/netfilter/ip6_tables.c
++++ b/net/ipv6/netfilter/ip6_tables.c
+@@ -1460,7 +1460,6 @@ compat_copy_entry_to_user(struct ip6t_en
+ static int
+ compat_find_calc_match(struct xt_entry_match *m,
+-                     const char *name,
+                      const struct ip6t_ip6 *ipv6,
+                      int *size)
+ {
+@@ -1497,8 +1496,7 @@ check_compat_entry_size_and_hooks(struct
+                                 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;
+@@ -1534,7 +1532,7 @@ check_compat_entry_size_and_hooks(struct
+       entry_offset = (void *)e - (void *)base;
+       j = 0;
+       xt_ematch_foreach(ematch, e) {
+-              ret = compat_find_calc_match(ematch, name, &e->ipv6, &off);
++              ret = compat_find_calc_match(ematch, &e->ipv6, &off);
+               if (ret != 0)
+                       goto release_matches;
+               ++j;
+@@ -1583,7 +1581,7 @@ release_matches:
+ static int
+ compat_copy_entry_from_user(struct compat_ip6t_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;
+@@ -1663,14 +1661,9 @@ static int compat_check_entry(struct ip6
+ 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_ip6t_replace *compatr)
+ {
+       unsigned int i, j;
+       struct xt_table_info *newinfo, *info;
+@@ -1682,8 +1675,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++) {
+@@ -1694,40 +1687,39 @@ translate_compat_table(struct net *net,
+       duprintf("translate_compat_table: size %u\n", info->size);
+       j = 0;
+       xt_compat_lock(AF_INET6);
+-      xt_compat_init_offsets(AF_INET6, number);
++      xt_compat_init_offsets(AF_INET6, 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;
+               }
+       }
+@@ -1737,17 +1729,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;
+       }
+@@ -1757,12 +1749,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;
+@@ -1802,7 +1794,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);
+@@ -1847,10 +1839,7 @@ compat_do_replace(struct net *net, void
+               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;
diff --git a/queue-4.4/netfilter-ip_tables-simplify-translate_compat_table-args.patch b/queue-4.4/netfilter-ip_tables-simplify-translate_compat_table-args.patch
new file mode 100644 (file)
index 0000000..0b919a2
--- /dev/null
@@ -0,0 +1,191 @@
+From 7d3f843eed29222254c9feab481f55175a1afcc9 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Apr 2016 14:17:30 +0200
+Subject: netfilter: ip_tables: simplify translate_compat_table args
+
+From: Florian Westphal <fw@strlen.de>
+
+commit 7d3f843eed29222254c9feab481f55175a1afcc9 upstream.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/ipv4/netfilter/ip_tables.c |   59 ++++++++++++++++-------------------------
+ 1 file changed, 24 insertions(+), 35 deletions(-)
+
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -1448,7 +1448,6 @@ compat_copy_entry_to_user(struct ipt_ent
+ static int
+ compat_find_calc_match(struct xt_entry_match *m,
+-                     const char *name,
+                      const struct ipt_ip *ip,
+                      int *size)
+ {
+@@ -1485,8 +1484,7 @@ check_compat_entry_size_and_hooks(struct
+                                 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;
+@@ -1522,7 +1520,7 @@ check_compat_entry_size_and_hooks(struct
+       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;
+@@ -1571,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;
+@@ -1654,14 +1652,9 @@ compat_check_entry(struct ipt_entry *e,
+ 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;
+@@ -1673,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++) {
+@@ -1685,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;
+               }
+       }
+@@ -1728,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;
+       }
+@@ -1748,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;
+@@ -1793,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);
+@@ -1838,10 +1830,7 @@ compat_do_replace(struct net *net, void
+               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;
diff --git a/queue-4.4/netfilter-x_tables-do-compat-validation-via-translate_table.patch b/queue-4.4/netfilter-x_tables-do-compat-validation-via-translate_table.patch
new file mode 100644 (file)
index 0000000..2653b2e
--- /dev/null
@@ -0,0 +1,804 @@
+From 09d9686047dbbe1cf4faa558d3ecc4aae2046054 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Apr 2016 14:17:34 +0200
+Subject: netfilter: x_tables: do compat validation via translate_table
+
+From: Florian Westphal <fw@strlen.de>
+
+commit 09d9686047dbbe1cf4faa558d3ecc4aae2046054 upstream.
+
+This looks like refactoring, but its also a bug fix.
+
+Problem is that the compat path (32bit iptables, 64bit kernel) lacks a few
+sanity tests that are done in the normal path.
+
+For example, we do not check for underflows and the base chain policies.
+
+While its possible to also add such checks to the compat path, its more
+copy&pastry, for instance we cannot reuse check_underflow() helper as
+e->target_offset differs in the compat case.
+
+Other problem is that it makes auditing for validation errors harder; two
+places need to be checked and kept in sync.
+
+At a high level 32 bit compat works like this:
+1- initial pass over blob:
+   validate match/entry offsets, bounds checking
+   lookup all matches and targets
+   do bookkeeping wrt. size delta of 32/64bit structures
+   assign match/target.u.kernel pointer (points at kernel
+   implementation, needed to access ->compatsize etc.)
+
+2- allocate memory according to the total bookkeeping size to
+   contain the translated ruleset
+
+3- second pass over original blob:
+   for each entry, copy the 32bit representation to the newly allocated
+   memory.  This also does any special match translations (e.g.
+   adjust 32bit to 64bit longs, etc).
+
+4- check if ruleset is free of loops (chase all jumps)
+
+5-first pass over translated blob:
+   call the checkentry function of all matches and targets.
+
+The alternative implemented by this patch is to drop steps 3&4 from the
+compat process, the translation is changed into an intermediate step
+rather than a full 1:1 translate_table replacement.
+
+In the 2nd pass (step #3), change the 64bit ruleset back to a kernel
+representation, i.e. put() the kernel pointer and restore ->u.user.name .
+
+This gets us a 64bit ruleset that is in the format generated by a 64bit
+iptables userspace -- we can then use translate_table() to get the
+'native' sanity checks.
+
+This has two drawbacks:
+
+1. we re-validate all the match and target entry structure sizes even
+though compat translation is supposed to never generate bogus offsets.
+2. we put and then re-lookup each match and target.
+
+THe upside is that we get all sanity tests and ruleset validations
+provided by the normal path and can remove some duplicated compat code.
+
+iptables-restore time of autogenerated ruleset with 300k chains of form
+-A CHAIN0001 -m limit --limit 1/s -j CHAIN0002
+-A CHAIN0002 -m limit --limit 1/s -j CHAIN0003
+
+shows no noticeable differences in restore times:
+old:   0m30.796s
+new:   0m31.521s
+64bit: 0m25.674s
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/ipv4/netfilter/arp_tables.c |  114 +++++------------------------
+ net/ipv4/netfilter/ip_tables.c  |  155 +++++++---------------------------------
+ net/ipv6/netfilter/ip6_tables.c |  148 +++++---------------------------------
+ net/netfilter/x_tables.c        |    8 ++
+ 4 files changed, 83 insertions(+), 342 deletions(-)
+
+--- a/net/ipv4/netfilter/arp_tables.c
++++ b/net/ipv4/netfilter/arp_tables.c
+@@ -1233,19 +1233,17 @@ static inline void compat_release_entry(
+       module_put(t->u.kernel.target->me);
+ }
+-static inline int
++static int
+ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
+                                 struct xt_table_info *newinfo,
+                                 unsigned int *size,
+                                 const unsigned char *base,
+-                                const unsigned char *limit,
+-                                const unsigned int *hook_entries,
+-                                const unsigned int *underflows)
++                                const unsigned char *limit)
+ {
+       struct xt_entry_target *t;
+       struct xt_target *target;
+       unsigned int entry_offset;
+-      int ret, off, h;
++      int ret, off;
+       duprintf("check_compat_entry_size_and_hooks %p\n", e);
+       if ((unsigned long)e % __alignof__(struct compat_arpt_entry) != 0 ||
+@@ -1290,17 +1288,6 @@ check_compat_entry_size_and_hooks(struct
+       if (ret)
+               goto release_target;
+-      /* Check hooks & underflows */
+-      for (h = 0; h < NF_ARP_NUMHOOKS; h++) {
+-              if ((unsigned char *)e - base == hook_entries[h])
+-                      newinfo->hook_entry[h] = hook_entries[h];
+-              if ((unsigned char *)e - base == underflows[h])
+-                      newinfo->underflow[h] = underflows[h];
+-      }
+-
+-      /* Clear counters and comefrom */
+-      memset(&e->counters, 0, sizeof(e->counters));
+-      e->comefrom = 0;
+       return 0;
+ release_target:
+@@ -1350,7 +1337,7 @@ static int translate_compat_table(struct
+       struct xt_table_info *newinfo, *info;
+       void *pos, *entry0, *entry1;
+       struct compat_arpt_entry *iter0;
+-      struct arpt_entry *iter1;
++      struct arpt_replace repl;
+       unsigned int size;
+       int ret = 0;
+@@ -1359,12 +1346,6 @@ static int translate_compat_table(struct
+       size = compatr->size;
+       info->number = compatr->num_entries;
+-      /* Init all hooks to impossible value. */
+-      for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
+-              info->hook_entry[i] = 0xFFFFFFFF;
+-              info->underflow[i] = 0xFFFFFFFF;
+-      }
+-
+       duprintf("translate_compat_table: size %u\n", info->size);
+       j = 0;
+       xt_compat_lock(NFPROTO_ARP);
+@@ -1373,9 +1354,7 @@ static int translate_compat_table(struct
+       xt_entry_foreach(iter0, entry0, compatr->size) {
+               ret = check_compat_entry_size_and_hooks(iter0, info, &size,
+                                                       entry0,
+-                                                      entry0 + compatr->size,
+-                                                      compatr->hook_entry,
+-                                                      compatr->underflow);
++                                                      entry0 + compatr->size);
+               if (ret != 0)
+                       goto out_unlock;
+               ++j;
+@@ -1388,23 +1367,6 @@ static int translate_compat_table(struct
+               goto out_unlock;
+       }
+-      /* Check hooks all assigned */
+-      for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
+-              /* Only hooks which are valid */
+-              if (!(compatr->valid_hooks & (1 << i)))
+-                      continue;
+-              if (info->hook_entry[i] == 0xFFFFFFFF) {
+-                      duprintf("Invalid hook entry %u %u\n",
+-                               i, info->hook_entry[i]);
+-                      goto out_unlock;
+-              }
+-              if (info->underflow[i] == 0xFFFFFFFF) {
+-                      duprintf("Invalid underflow %u %u\n",
+-                               i, info->underflow[i]);
+-                      goto out_unlock;
+-              }
+-      }
+-
+       ret = -ENOMEM;
+       newinfo = xt_alloc_table_info(size);
+       if (!newinfo)
+@@ -1421,55 +1383,26 @@ static int translate_compat_table(struct
+       xt_entry_foreach(iter0, entry0, compatr->size)
+               compat_copy_entry_from_user(iter0, &pos, &size,
+                                           newinfo, entry1);
++
++      /* all module references in entry0 are now gone */
++
+       xt_compat_flush_offsets(NFPROTO_ARP);
+       xt_compat_unlock(NFPROTO_ARP);
+-      ret = -ELOOP;
+-      if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
+-              goto free_newinfo;
+-
+-      i = 0;
+-      xt_entry_foreach(iter1, entry1, newinfo->size) {
+-              iter1->counters.pcnt = xt_percpu_counter_alloc();
+-              if (IS_ERR_VALUE(iter1->counters.pcnt)) {
+-                      ret = -ENOMEM;
+-                      break;
+-              }
++      memcpy(&repl, compatr, sizeof(*compatr));
+-              ret = check_target(iter1, compatr->name);
+-              if (ret != 0) {
+-                      xt_percpu_counter_free(iter1->counters.pcnt);
+-                      break;
+-              }
+-              ++i;
+-              if (strcmp(arpt_get_target(iter1)->u.user.name,
+-                  XT_ERROR_TARGET) == 0)
+-                      ++newinfo->stacksize;
+-      }
+-      if (ret) {
+-              /*
+-               * The first i matches need cleanup_entry (calls ->destroy)
+-               * because they had called ->check already. The other j-i
+-               * entries need only release.
+-               */
+-              int skip = i;
+-              j -= i;
+-              xt_entry_foreach(iter0, entry0, newinfo->size) {
+-                      if (skip-- > 0)
+-                              continue;
+-                      if (j-- == 0)
+-                              break;
+-                      compat_release_entry(iter0);
+-              }
+-              xt_entry_foreach(iter1, entry1, newinfo->size) {
+-                      if (i-- == 0)
+-                              break;
+-                      cleanup_entry(iter1);
+-              }
+-              xt_free_table_info(newinfo);
+-              return ret;
++      for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
++              repl.hook_entry[i] = newinfo->hook_entry[i];
++              repl.underflow[i] = newinfo->underflow[i];
+       }
++      repl.num_counters = 0;
++      repl.counters = NULL;
++      repl.size = newinfo->size;
++      ret = translate_table(newinfo, entry1, &repl);
++      if (ret)
++              goto free_newinfo;
++
+       *pinfo = newinfo;
+       *pentry0 = entry1;
+       xt_free_table_info(info);
+@@ -1477,17 +1410,16 @@ static int translate_compat_table(struct
+ free_newinfo:
+       xt_free_table_info(newinfo);
+-out:
++      return ret;
++out_unlock:
++      xt_compat_flush_offsets(NFPROTO_ARP);
++      xt_compat_unlock(NFPROTO_ARP);
+       xt_entry_foreach(iter0, entry0, compatr->size) {
+               if (j-- == 0)
+                       break;
+               compat_release_entry(iter0);
+       }
+       return ret;
+-out_unlock:
+-      xt_compat_flush_offsets(NFPROTO_ARP);
+-      xt_compat_unlock(NFPROTO_ARP);
+-      goto out;
+ }
+ static int compat_do_replace(struct net *net, void __user *user,
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -1482,16 +1482,14 @@ check_compat_entry_size_and_hooks(struct
+                                 struct xt_table_info *newinfo,
+                                 unsigned int *size,
+                                 const unsigned char *base,
+-                                const unsigned char *limit,
+-                                const unsigned int *hook_entries,
+-                                const unsigned int *underflows)
++                                const unsigned char *limit)
+ {
+       struct xt_entry_match *ematch;
+       struct xt_entry_target *t;
+       struct xt_target *target;
+       unsigned int entry_offset;
+       unsigned int j;
+-      int ret, off, h;
++      int ret, off;
+       duprintf("check_compat_entry_size_and_hooks %p\n", e);
+       if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 ||
+@@ -1543,17 +1541,6 @@ check_compat_entry_size_and_hooks(struct
+       if (ret)
+               goto out;
+-      /* Check hooks & underflows */
+-      for (h = 0; h < NF_INET_NUMHOOKS; h++) {
+-              if ((unsigned char *)e - base == hook_entries[h])
+-                      newinfo->hook_entry[h] = hook_entries[h];
+-              if ((unsigned char *)e - base == underflows[h])
+-                      newinfo->underflow[h] = underflows[h];
+-      }
+-
+-      /* Clear counters and comefrom */
+-      memset(&e->counters, 0, sizeof(e->counters));
+-      e->comefrom = 0;
+       return 0;
+ out:
+@@ -1596,6 +1583,7 @@ compat_copy_entry_from_user(struct compa
+       xt_compat_target_from_user(t, dstptr, size);
+       de->next_offset = e->next_offset - (origsize - *size);
++
+       for (h = 0; h < NF_INET_NUMHOOKS; h++) {
+               if ((unsigned char *)de - base < newinfo->hook_entry[h])
+                       newinfo->hook_entry[h] -= origsize - *size;
+@@ -1605,48 +1593,6 @@ compat_copy_entry_from_user(struct compa
+ }
+ static int
+-compat_check_entry(struct ipt_entry *e, struct net *net, const char *name)
+-{
+-      struct xt_entry_match *ematch;
+-      struct xt_mtchk_param mtpar;
+-      unsigned int j;
+-      int ret = 0;
+-
+-      e->counters.pcnt = xt_percpu_counter_alloc();
+-      if (IS_ERR_VALUE(e->counters.pcnt))
+-              return -ENOMEM;
+-
+-      j = 0;
+-      mtpar.net       = net;
+-      mtpar.table     = name;
+-      mtpar.entryinfo = &e->ip;
+-      mtpar.hook_mask = e->comefrom;
+-      mtpar.family    = NFPROTO_IPV4;
+-      xt_ematch_foreach(ematch, e) {
+-              ret = check_match(ematch, &mtpar);
+-              if (ret != 0)
+-                      goto cleanup_matches;
+-              ++j;
+-      }
+-
+-      ret = check_target(e, net, name);
+-      if (ret)
+-              goto cleanup_matches;
+-      return 0;
+-
+- cleanup_matches:
+-      xt_ematch_foreach(ematch, e) {
+-              if (j-- == 0)
+-                      break;
+-              cleanup_match(ematch, net);
+-      }
+-
+-      xt_percpu_counter_free(e->counters.pcnt);
+-
+-      return ret;
+-}
+-
+-static int
+ translate_compat_table(struct net *net,
+                      struct xt_table_info **pinfo,
+                      void **pentry0,
+@@ -1656,7 +1602,7 @@ translate_compat_table(struct net *net,
+       struct xt_table_info *newinfo, *info;
+       void *pos, *entry0, *entry1;
+       struct compat_ipt_entry *iter0;
+-      struct ipt_entry *iter1;
++      struct ipt_replace repl;
+       unsigned int size;
+       int ret;
+@@ -1665,12 +1611,6 @@ translate_compat_table(struct net *net,
+       size = compatr->size;
+       info->number = compatr->num_entries;
+-      /* Init all hooks to impossible value. */
+-      for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+-              info->hook_entry[i] = 0xFFFFFFFF;
+-              info->underflow[i] = 0xFFFFFFFF;
+-      }
+-
+       duprintf("translate_compat_table: size %u\n", info->size);
+       j = 0;
+       xt_compat_lock(AF_INET);
+@@ -1679,9 +1619,7 @@ translate_compat_table(struct net *net,
+       xt_entry_foreach(iter0, entry0, compatr->size) {
+               ret = check_compat_entry_size_and_hooks(iter0, info, &size,
+                                                       entry0,
+-                                                      entry0 + compatr->size,
+-                                                      compatr->hook_entry,
+-                                                      compatr->underflow);
++                                                      entry0 + compatr->size);
+               if (ret != 0)
+                       goto out_unlock;
+               ++j;
+@@ -1694,23 +1632,6 @@ translate_compat_table(struct net *net,
+               goto out_unlock;
+       }
+-      /* Check hooks all assigned */
+-      for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+-              /* Only hooks which are valid */
+-              if (!(compatr->valid_hooks & (1 << i)))
+-                      continue;
+-              if (info->hook_entry[i] == 0xFFFFFFFF) {
+-                      duprintf("Invalid hook entry %u %u\n",
+-                               i, info->hook_entry[i]);
+-                      goto out_unlock;
+-              }
+-              if (info->underflow[i] == 0xFFFFFFFF) {
+-                      duprintf("Invalid underflow %u %u\n",
+-                               i, info->underflow[i]);
+-                      goto out_unlock;
+-              }
+-      }
+-
+       ret = -ENOMEM;
+       newinfo = xt_alloc_table_info(size);
+       if (!newinfo)
+@@ -1718,8 +1639,8 @@ translate_compat_table(struct net *net,
+       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];
++              newinfo->hook_entry[i] = compatr->hook_entry[i];
++              newinfo->underflow[i] = compatr->underflow[i];
+       }
+       entry1 = newinfo->entries;
+       pos = entry1;
+@@ -1728,47 +1649,30 @@ translate_compat_table(struct net *net,
+               compat_copy_entry_from_user(iter0, &pos, &size,
+                                           newinfo, entry1);
++      /* all module references in entry0 are now gone.
++       * entry1/newinfo contains a 64bit ruleset that looks exactly as
++       * generated by 64bit userspace.
++       *
++       * Call standard translate_table() to validate all hook_entrys,
++       * underflows, check for loops, etc.
++       */
+       xt_compat_flush_offsets(AF_INET);
+       xt_compat_unlock(AF_INET);
+-      ret = -ELOOP;
+-      if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
+-              goto free_newinfo;
++      memcpy(&repl, compatr, sizeof(*compatr));
+-      i = 0;
+-      xt_entry_foreach(iter1, entry1, newinfo->size) {
+-              ret = compat_check_entry(iter1, net, compatr->name);
+-              if (ret != 0)
+-                      break;
+-              ++i;
+-              if (strcmp(ipt_get_target(iter1)->u.user.name,
+-                  XT_ERROR_TARGET) == 0)
+-                      ++newinfo->stacksize;
+-      }
+-      if (ret) {
+-              /*
+-               * The first i matches need cleanup_entry (calls ->destroy)
+-               * because they had called ->check already. The other j-i
+-               * entries need only release.
+-               */
+-              int skip = i;
+-              j -= i;
+-              xt_entry_foreach(iter0, entry0, newinfo->size) {
+-                      if (skip-- > 0)
+-                              continue;
+-                      if (j-- == 0)
+-                              break;
+-                      compat_release_entry(iter0);
+-              }
+-              xt_entry_foreach(iter1, entry1, newinfo->size) {
+-                      if (i-- == 0)
+-                              break;
+-                      cleanup_entry(iter1, net);
+-              }
+-              xt_free_table_info(newinfo);
+-              return ret;
++      for (i = 0; i < NF_INET_NUMHOOKS; i++) {
++              repl.hook_entry[i] = newinfo->hook_entry[i];
++              repl.underflow[i] = newinfo->underflow[i];
+       }
++      repl.num_counters = 0;
++      repl.counters = NULL;
++      repl.size = newinfo->size;
++      ret = translate_table(net, newinfo, entry1, &repl);
++      if (ret)
++              goto free_newinfo;
++
+       *pinfo = newinfo;
+       *pentry0 = entry1;
+       xt_free_table_info(info);
+@@ -1776,17 +1680,16 @@ translate_compat_table(struct net *net,
+ free_newinfo:
+       xt_free_table_info(newinfo);
+-out:
++      return ret;
++out_unlock:
++      xt_compat_flush_offsets(AF_INET);
++      xt_compat_unlock(AF_INET);
+       xt_entry_foreach(iter0, entry0, compatr->size) {
+               if (j-- == 0)
+                       break;
+               compat_release_entry(iter0);
+       }
+       return ret;
+-out_unlock:
+-      xt_compat_flush_offsets(AF_INET);
+-      xt_compat_unlock(AF_INET);
+-      goto out;
+ }
+ static int
+--- a/net/ipv6/netfilter/ip6_tables.c
++++ b/net/ipv6/netfilter/ip6_tables.c
+@@ -1494,16 +1494,14 @@ check_compat_entry_size_and_hooks(struct
+                                 struct xt_table_info *newinfo,
+                                 unsigned int *size,
+                                 const unsigned char *base,
+-                                const unsigned char *limit,
+-                                const unsigned int *hook_entries,
+-                                const unsigned int *underflows)
++                                const unsigned char *limit)
+ {
+       struct xt_entry_match *ematch;
+       struct xt_entry_target *t;
+       struct xt_target *target;
+       unsigned int entry_offset;
+       unsigned int j;
+-      int ret, off, h;
++      int ret, off;
+       duprintf("check_compat_entry_size_and_hooks %p\n", e);
+       if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 ||
+@@ -1555,17 +1553,6 @@ check_compat_entry_size_and_hooks(struct
+       if (ret)
+               goto out;
+-      /* Check hooks & underflows */
+-      for (h = 0; h < NF_INET_NUMHOOKS; h++) {
+-              if ((unsigned char *)e - base == hook_entries[h])
+-                      newinfo->hook_entry[h] = hook_entries[h];
+-              if ((unsigned char *)e - base == underflows[h])
+-                      newinfo->underflow[h] = underflows[h];
+-      }
+-
+-      /* Clear counters and comefrom */
+-      memset(&e->counters, 0, sizeof(e->counters));
+-      e->comefrom = 0;
+       return 0;
+ out:
+@@ -1614,47 +1601,6 @@ compat_copy_entry_from_user(struct compa
+       }
+ }
+-static int compat_check_entry(struct ip6t_entry *e, struct net *net,
+-                            const char *name)
+-{
+-      unsigned int j;
+-      int ret = 0;
+-      struct xt_mtchk_param mtpar;
+-      struct xt_entry_match *ematch;
+-
+-      e->counters.pcnt = xt_percpu_counter_alloc();
+-      if (IS_ERR_VALUE(e->counters.pcnt))
+-              return -ENOMEM;
+-      j = 0;
+-      mtpar.net       = net;
+-      mtpar.table     = name;
+-      mtpar.entryinfo = &e->ipv6;
+-      mtpar.hook_mask = e->comefrom;
+-      mtpar.family    = NFPROTO_IPV6;
+-      xt_ematch_foreach(ematch, e) {
+-              ret = check_match(ematch, &mtpar);
+-              if (ret != 0)
+-                      goto cleanup_matches;
+-              ++j;
+-      }
+-
+-      ret = check_target(e, net, name);
+-      if (ret)
+-              goto cleanup_matches;
+-      return 0;
+-
+- cleanup_matches:
+-      xt_ematch_foreach(ematch, e) {
+-              if (j-- == 0)
+-                      break;
+-              cleanup_match(ematch, net);
+-      }
+-
+-      xt_percpu_counter_free(e->counters.pcnt);
+-
+-      return ret;
+-}
+-
+ static int
+ translate_compat_table(struct net *net,
+                      struct xt_table_info **pinfo,
+@@ -1665,7 +1611,7 @@ translate_compat_table(struct net *net,
+       struct xt_table_info *newinfo, *info;
+       void *pos, *entry0, *entry1;
+       struct compat_ip6t_entry *iter0;
+-      struct ip6t_entry *iter1;
++      struct ip6t_replace repl;
+       unsigned int size;
+       int ret = 0;
+@@ -1674,12 +1620,6 @@ translate_compat_table(struct net *net,
+       size = compatr->size;
+       info->number = compatr->num_entries;
+-      /* Init all hooks to impossible value. */
+-      for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+-              info->hook_entry[i] = 0xFFFFFFFF;
+-              info->underflow[i] = 0xFFFFFFFF;
+-      }
+-
+       duprintf("translate_compat_table: size %u\n", info->size);
+       j = 0;
+       xt_compat_lock(AF_INET6);
+@@ -1688,9 +1628,7 @@ translate_compat_table(struct net *net,
+       xt_entry_foreach(iter0, entry0, compatr->size) {
+               ret = check_compat_entry_size_and_hooks(iter0, info, &size,
+                                                       entry0,
+-                                                      entry0 + compatr->size,
+-                                                      compatr->hook_entry,
+-                                                      compatr->underflow);
++                                                      entry0 + compatr->size);
+               if (ret != 0)
+                       goto out_unlock;
+               ++j;
+@@ -1703,23 +1641,6 @@ translate_compat_table(struct net *net,
+               goto out_unlock;
+       }
+-      /* Check hooks all assigned */
+-      for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+-              /* Only hooks which are valid */
+-              if (!(compatr->valid_hooks & (1 << i)))
+-                      continue;
+-              if (info->hook_entry[i] == 0xFFFFFFFF) {
+-                      duprintf("Invalid hook entry %u %u\n",
+-                               i, info->hook_entry[i]);
+-                      goto out_unlock;
+-              }
+-              if (info->underflow[i] == 0xFFFFFFFF) {
+-                      duprintf("Invalid underflow %u %u\n",
+-                               i, info->underflow[i]);
+-                      goto out_unlock;
+-              }
+-      }
+-
+       ret = -ENOMEM;
+       newinfo = xt_alloc_table_info(size);
+       if (!newinfo)
+@@ -1727,56 +1648,34 @@ translate_compat_table(struct net *net,
+       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];
++              newinfo->hook_entry[i] = compatr->hook_entry[i];
++              newinfo->underflow[i] = compatr->underflow[i];
+       }
+       entry1 = newinfo->entries;
+       pos = entry1;
++      size = compatr->size;
+       xt_entry_foreach(iter0, entry0, compatr->size)
+               compat_copy_entry_from_user(iter0, &pos, &size,
+                                           newinfo, entry1);
++      /* all module references in entry0 are now gone. */
+       xt_compat_flush_offsets(AF_INET6);
+       xt_compat_unlock(AF_INET6);
+-      ret = -ELOOP;
+-      if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
+-              goto free_newinfo;
++      memcpy(&repl, compatr, sizeof(*compatr));
+-      i = 0;
+-      xt_entry_foreach(iter1, entry1, newinfo->size) {
+-              ret = compat_check_entry(iter1, net, compatr->name);
+-              if (ret != 0)
+-                      break;
+-              ++i;
+-              if (strcmp(ip6t_get_target(iter1)->u.user.name,
+-                  XT_ERROR_TARGET) == 0)
+-                      ++newinfo->stacksize;
+-      }
+-      if (ret) {
+-              /*
+-               * The first i matches need cleanup_entry (calls ->destroy)
+-               * because they had called ->check already. The other j-i
+-               * entries need only release.
+-               */
+-              int skip = i;
+-              j -= i;
+-              xt_entry_foreach(iter0, entry0, newinfo->size) {
+-                      if (skip-- > 0)
+-                              continue;
+-                      if (j-- == 0)
+-                              break;
+-                      compat_release_entry(iter0);
+-              }
+-              xt_entry_foreach(iter1, entry1, newinfo->size) {
+-                      if (i-- == 0)
+-                              break;
+-                      cleanup_entry(iter1, net);
+-              }
+-              xt_free_table_info(newinfo);
+-              return ret;
++      for (i = 0; i < NF_INET_NUMHOOKS; i++) {
++              repl.hook_entry[i] = newinfo->hook_entry[i];
++              repl.underflow[i] = newinfo->underflow[i];
+       }
++      repl.num_counters = 0;
++      repl.counters = NULL;
++      repl.size = newinfo->size;
++      ret = translate_table(net, newinfo, entry1, &repl);
++      if (ret)
++              goto free_newinfo;
++
+       *pinfo = newinfo;
+       *pentry0 = entry1;
+       xt_free_table_info(info);
+@@ -1784,17 +1683,16 @@ translate_compat_table(struct net *net,
+ free_newinfo:
+       xt_free_table_info(newinfo);
+-out:
++      return ret;
++out_unlock:
++      xt_compat_flush_offsets(AF_INET6);
++      xt_compat_unlock(AF_INET6);
+       xt_entry_foreach(iter0, entry0, compatr->size) {
+               if (j-- == 0)
+                       break;
+               compat_release_entry(iter0);
+       }
+       return ret;
+-out_unlock:
+-      xt_compat_flush_offsets(AF_INET6);
+-      xt_compat_unlock(AF_INET6);
+-      goto out;
+ }
+ static int
+--- a/net/netfilter/x_tables.c
++++ b/net/netfilter/x_tables.c
+@@ -532,6 +532,7 @@ void xt_compat_match_from_user(struct xt
+       struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m;
+       int pad, off = xt_compat_match_offset(match);
+       u_int16_t msize = cm->u.user.match_size;
++      char name[sizeof(m->u.user.name)];
+       m = *dstptr;
+       memcpy(m, cm, sizeof(*cm));
+@@ -545,6 +546,9 @@ void xt_compat_match_from_user(struct xt
+       msize += off;
+       m->u.user.match_size = msize;
++      strlcpy(name, match->name, sizeof(name));
++      module_put(match->me);
++      strncpy(m->u.user.name, name, sizeof(m->u.user.name));
+       *size += off;
+       *dstptr += msize;
+@@ -762,6 +766,7 @@ void xt_compat_target_from_user(struct x
+       struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t;
+       int pad, off = xt_compat_target_offset(target);
+       u_int16_t tsize = ct->u.user.target_size;
++      char name[sizeof(t->u.user.name)];
+       t = *dstptr;
+       memcpy(t, ct, sizeof(*ct));
+@@ -775,6 +780,9 @@ void xt_compat_target_from_user(struct x
+       tsize += off;
+       t->u.user.target_size = tsize;
++      strlcpy(name, target->name, sizeof(name));
++      module_put(target->me);
++      strncpy(t->u.user.name, name, sizeof(t->u.user.name));
+       *size += off;
+       *dstptr += tsize;
diff --git a/queue-4.4/netfilter-x_tables-introduce-and-use-xt_copy_counters_from_user.patch b/queue-4.4/netfilter-x_tables-introduce-and-use-xt_copy_counters_from_user.patch
new file mode 100644 (file)
index 0000000..1efe833
--- /dev/null
@@ -0,0 +1,334 @@
+From d7591f0c41ce3e67600a982bab6989ef0f07b3ce Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Apr 2016 15:37:59 +0200
+Subject: netfilter: x_tables: introduce and use xt_copy_counters_from_user
+
+From: Florian Westphal <fw@strlen.de>
+
+commit d7591f0c41ce3e67600a982bab6989ef0f07b3ce upstream.
+
+The three variants use same copy&pasted code, condense this into a
+helper and use that.
+
+Make sure info.name is 0-terminated.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ include/linux/netfilter/x_tables.h |    3 +
+ net/ipv4/netfilter/arp_tables.c    |   48 ++----------------------
+ net/ipv4/netfilter/ip_tables.c     |   48 ++----------------------
+ net/ipv6/netfilter/ip6_tables.c    |   49 ++----------------------
+ net/netfilter/x_tables.c           |   74 +++++++++++++++++++++++++++++++++++++
+ 5 files changed, 92 insertions(+), 130 deletions(-)
+
+--- a/include/linux/netfilter/x_tables.h
++++ b/include/linux/netfilter/x_tables.h
+@@ -248,6 +248,9 @@ int xt_check_match(struct xt_mtchk_param
+ int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto,
+                   bool inv_proto);
++void *xt_copy_counters_from_user(const void __user *user, unsigned int len,
++                               struct xt_counters_info *info, bool compat);
++
+ struct xt_table *xt_register_table(struct net *net,
+                                  const struct xt_table *table,
+                                  struct xt_table_info *bootstrap,
+--- a/net/ipv4/netfilter/arp_tables.c
++++ b/net/ipv4/netfilter/arp_tables.c
+@@ -1130,55 +1130,17 @@ static int do_add_counters(struct net *n
+       unsigned int i;
+       struct xt_counters_info tmp;
+       struct xt_counters *paddc;
+-      unsigned int num_counters;
+-      const char *name;
+-      int size;
+-      void *ptmp;
+       struct xt_table *t;
+       const struct xt_table_info *private;
+       int ret = 0;
+       struct arpt_entry *iter;
+       unsigned int addend;
+-#ifdef CONFIG_COMPAT
+-      struct compat_xt_counters_info compat_tmp;
+-      if (compat) {
+-              ptmp = &compat_tmp;
+-              size = sizeof(struct compat_xt_counters_info);
+-      } else
+-#endif
+-      {
+-              ptmp = &tmp;
+-              size = sizeof(struct xt_counters_info);
+-      }
+-
+-      if (copy_from_user(ptmp, user, size) != 0)
+-              return -EFAULT;
+-
+-#ifdef CONFIG_COMPAT
+-      if (compat) {
+-              num_counters = compat_tmp.num_counters;
+-              name = compat_tmp.name;
+-      } else
+-#endif
+-      {
+-              num_counters = tmp.num_counters;
+-              name = tmp.name;
+-      }
+-
+-      if (len != size + num_counters * sizeof(struct xt_counters))
+-              return -EINVAL;
+-
+-      paddc = vmalloc(len - size);
+-      if (!paddc)
+-              return -ENOMEM;
+-
+-      if (copy_from_user(paddc, user + size, len - size) != 0) {
+-              ret = -EFAULT;
+-              goto free;
+-      }
++      paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
++      if (IS_ERR(paddc))
++              return PTR_ERR(paddc);
+-      t = xt_find_table_lock(net, NFPROTO_ARP, name);
++      t = xt_find_table_lock(net, NFPROTO_ARP, tmp.name);
+       if (IS_ERR_OR_NULL(t)) {
+               ret = t ? PTR_ERR(t) : -ENOENT;
+               goto free;
+@@ -1186,7 +1148,7 @@ static int do_add_counters(struct net *n
+       local_bh_disable();
+       private = t->private;
+-      if (private->number != num_counters) {
++      if (private->number != tmp.num_counters) {
+               ret = -EINVAL;
+               goto unlock_up_free;
+       }
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -1313,55 +1313,17 @@ do_add_counters(struct net *net, const v
+       unsigned int i;
+       struct xt_counters_info tmp;
+       struct xt_counters *paddc;
+-      unsigned int num_counters;
+-      const char *name;
+-      int size;
+-      void *ptmp;
+       struct xt_table *t;
+       const struct xt_table_info *private;
+       int ret = 0;
+       struct ipt_entry *iter;
+       unsigned int addend;
+-#ifdef CONFIG_COMPAT
+-      struct compat_xt_counters_info compat_tmp;
+-      if (compat) {
+-              ptmp = &compat_tmp;
+-              size = sizeof(struct compat_xt_counters_info);
+-      } else
+-#endif
+-      {
+-              ptmp = &tmp;
+-              size = sizeof(struct xt_counters_info);
+-      }
+-
+-      if (copy_from_user(ptmp, user, size) != 0)
+-              return -EFAULT;
+-
+-#ifdef CONFIG_COMPAT
+-      if (compat) {
+-              num_counters = compat_tmp.num_counters;
+-              name = compat_tmp.name;
+-      } else
+-#endif
+-      {
+-              num_counters = tmp.num_counters;
+-              name = tmp.name;
+-      }
+-
+-      if (len != size + num_counters * sizeof(struct xt_counters))
+-              return -EINVAL;
+-
+-      paddc = vmalloc(len - size);
+-      if (!paddc)
+-              return -ENOMEM;
+-
+-      if (copy_from_user(paddc, user + size, len - size) != 0) {
+-              ret = -EFAULT;
+-              goto free;
+-      }
++      paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
++      if (IS_ERR(paddc))
++              return PTR_ERR(paddc);
+-      t = xt_find_table_lock(net, AF_INET, name);
++      t = xt_find_table_lock(net, AF_INET, tmp.name);
+       if (IS_ERR_OR_NULL(t)) {
+               ret = t ? PTR_ERR(t) : -ENOENT;
+               goto free;
+@@ -1369,7 +1331,7 @@ do_add_counters(struct net *net, const v
+       local_bh_disable();
+       private = t->private;
+-      if (private->number != num_counters) {
++      if (private->number != tmp.num_counters) {
+               ret = -EINVAL;
+               goto unlock_up_free;
+       }
+--- a/net/ipv6/netfilter/ip6_tables.c
++++ b/net/ipv6/netfilter/ip6_tables.c
+@@ -1325,55 +1325,16 @@ do_add_counters(struct net *net, const v
+       unsigned int i;
+       struct xt_counters_info tmp;
+       struct xt_counters *paddc;
+-      unsigned int num_counters;
+-      char *name;
+-      int size;
+-      void *ptmp;
+       struct xt_table *t;
+       const struct xt_table_info *private;
+       int ret = 0;
+       struct ip6t_entry *iter;
+       unsigned int addend;
+-#ifdef CONFIG_COMPAT
+-      struct compat_xt_counters_info compat_tmp;
+-      if (compat) {
+-              ptmp = &compat_tmp;
+-              size = sizeof(struct compat_xt_counters_info);
+-      } else
+-#endif
+-      {
+-              ptmp = &tmp;
+-              size = sizeof(struct xt_counters_info);
+-      }
+-
+-      if (copy_from_user(ptmp, user, size) != 0)
+-              return -EFAULT;
+-
+-#ifdef CONFIG_COMPAT
+-      if (compat) {
+-              num_counters = compat_tmp.num_counters;
+-              name = compat_tmp.name;
+-      } else
+-#endif
+-      {
+-              num_counters = tmp.num_counters;
+-              name = tmp.name;
+-      }
+-
+-      if (len != size + num_counters * sizeof(struct xt_counters))
+-              return -EINVAL;
+-
+-      paddc = vmalloc(len - size);
+-      if (!paddc)
+-              return -ENOMEM;
+-
+-      if (copy_from_user(paddc, user + size, len - size) != 0) {
+-              ret = -EFAULT;
+-              goto free;
+-      }
+-
+-      t = xt_find_table_lock(net, AF_INET6, name);
++      paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
++      if (IS_ERR(paddc))
++              return PTR_ERR(paddc);
++      t = xt_find_table_lock(net, AF_INET6, tmp.name);
+       if (IS_ERR_OR_NULL(t)) {
+               ret = t ? PTR_ERR(t) : -ENOENT;
+               goto free;
+@@ -1381,7 +1342,7 @@ do_add_counters(struct net *net, const v
+       local_bh_disable();
+       private = t->private;
+-      if (private->number != num_counters) {
++      if (private->number != tmp.num_counters) {
+               ret = -EINVAL;
+               goto unlock_up_free;
+       }
+--- a/net/netfilter/x_tables.c
++++ b/net/netfilter/x_tables.c
+@@ -751,6 +751,80 @@ int xt_check_target(struct xt_tgchk_para
+ }
+ EXPORT_SYMBOL_GPL(xt_check_target);
++/**
++ * xt_copy_counters_from_user - copy counters and metadata from userspace
++ *
++ * @user: src pointer to userspace memory
++ * @len: alleged size of userspace memory
++ * @info: where to store the xt_counters_info metadata
++ * @compat: true if we setsockopt call is done by 32bit task on 64bit kernel
++ *
++ * Copies counter meta data from @user and stores it in @info.
++ *
++ * vmallocs memory to hold the counters, then copies the counter data
++ * from @user to the new memory and returns a pointer to it.
++ *
++ * If @compat is true, @info gets converted automatically to the 64bit
++ * representation.
++ *
++ * The metadata associated with the counters is stored in @info.
++ *
++ * Return: returns pointer that caller has to test via IS_ERR().
++ * If IS_ERR is false, caller has to vfree the pointer.
++ */
++void *xt_copy_counters_from_user(const void __user *user, unsigned int len,
++                               struct xt_counters_info *info, bool compat)
++{
++      void *mem;
++      u64 size;
++
++#ifdef CONFIG_COMPAT
++      if (compat) {
++              /* structures only differ in size due to alignment */
++              struct compat_xt_counters_info compat_tmp;
++
++              if (len <= sizeof(compat_tmp))
++                      return ERR_PTR(-EINVAL);
++
++              len -= sizeof(compat_tmp);
++              if (copy_from_user(&compat_tmp, user, sizeof(compat_tmp)) != 0)
++                      return ERR_PTR(-EFAULT);
++
++              strlcpy(info->name, compat_tmp.name, sizeof(info->name));
++              info->num_counters = compat_tmp.num_counters;
++              user += sizeof(compat_tmp);
++      } else
++#endif
++      {
++              if (len <= sizeof(*info))
++                      return ERR_PTR(-EINVAL);
++
++              len -= sizeof(*info);
++              if (copy_from_user(info, user, sizeof(*info)) != 0)
++                      return ERR_PTR(-EFAULT);
++
++              info->name[sizeof(info->name) - 1] = '\0';
++              user += sizeof(*info);
++      }
++
++      size = sizeof(struct xt_counters);
++      size *= info->num_counters;
++
++      if (size != (u64)len)
++              return ERR_PTR(-EINVAL);
++
++      mem = vmalloc(len);
++      if (!mem)
++              return ERR_PTR(-ENOMEM);
++
++      if (copy_from_user(mem, user, len) == 0)
++              return mem;
++
++      vfree(mem);
++      return ERR_PTR(-EFAULT);
++}
++EXPORT_SYMBOL_GPL(xt_copy_counters_from_user);
++
+ #ifdef CONFIG_COMPAT
+ int xt_compat_target_offset(const struct xt_target *target)
+ {
diff --git a/queue-4.4/netfilter-x_tables-xt_compat_match_from_user-doesn-t-need-a-retval.patch b/queue-4.4/netfilter-x_tables-xt_compat_match_from_user-doesn-t-need-a-retval.patch
new file mode 100644 (file)
index 0000000..0fa55a4
--- /dev/null
@@ -0,0 +1,240 @@
+From 0188346f21e6546498c2a0f84888797ad4063fc5 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Apr 2016 14:17:33 +0200
+Subject: netfilter: x_tables: xt_compat_match_from_user doesn't need a retval
+
+From: Florian Westphal <fw@strlen.de>
+
+commit 0188346f21e6546498c2a0f84888797ad4063fc5 upstream.
+
+Always returned 0.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ include/linux/netfilter/x_tables.h |    2 +-
+ net/ipv4/netfilter/arp_tables.c    |   17 +++++------------
+ net/ipv4/netfilter/ip_tables.c     |   26 +++++++++-----------------
+ net/ipv6/netfilter/ip6_tables.c    |   27 +++++++++------------------
+ net/netfilter/x_tables.c           |    5 ++---
+ 5 files changed, 26 insertions(+), 51 deletions(-)
+
+--- a/include/linux/netfilter/x_tables.h
++++ b/include/linux/netfilter/x_tables.h
+@@ -482,7 +482,7 @@ void xt_compat_init_offsets(u_int8_t af,
+ int xt_compat_calc_jump(u_int8_t af, unsigned int offset);
+ int xt_compat_match_offset(const struct xt_match *match);
+-int xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
++void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
+                             unsigned int *size);
+ int xt_compat_match_to_user(const struct xt_entry_match *m,
+                           void __user **dstptr, unsigned int *size);
+--- a/net/ipv4/netfilter/arp_tables.c
++++ b/net/ipv4/netfilter/arp_tables.c
+@@ -1309,7 +1309,7 @@ out:
+       return ret;
+ }
+-static int
++static void
+ compat_copy_entry_from_user(struct compat_arpt_entry *e, void **dstptr,
+                           unsigned int *size,
+                           struct xt_table_info *newinfo, unsigned char *base)
+@@ -1318,9 +1318,8 @@ compat_copy_entry_from_user(struct compa
+       struct xt_target *target;
+       struct arpt_entry *de;
+       unsigned int origsize;
+-      int ret, h;
++      int h;
+-      ret = 0;
+       origsize = *size;
+       de = (struct arpt_entry *)*dstptr;
+       memcpy(de, e, sizeof(struct arpt_entry));
+@@ -1341,7 +1340,6 @@ compat_copy_entry_from_user(struct compa
+               if ((unsigned char *)de - base < newinfo->underflow[h])
+                       newinfo->underflow[h] -= origsize - *size;
+       }
+-      return ret;
+ }
+ static int translate_compat_table(struct xt_table_info **pinfo,
+@@ -1420,16 +1418,11 @@ static int translate_compat_table(struct
+       entry1 = newinfo->entries;
+       pos = entry1;
+       size = compatr->size;
+-      xt_entry_foreach(iter0, entry0, compatr->size) {
+-              ret = compat_copy_entry_from_user(iter0, &pos, &size,
+-                                                newinfo, entry1);
+-              if (ret != 0)
+-                      break;
+-      }
++      xt_entry_foreach(iter0, entry0, compatr->size)
++              compat_copy_entry_from_user(iter0, &pos, &size,
++                                          newinfo, entry1);
+       xt_compat_flush_offsets(NFPROTO_ARP);
+       xt_compat_unlock(NFPROTO_ARP);
+-      if (ret)
+-              goto free_newinfo;
+       ret = -ELOOP;
+       if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -1567,7 +1567,7 @@ release_matches:
+       return ret;
+ }
+-static int
++static void
+ compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr,
+                           unsigned int *size,
+                           struct xt_table_info *newinfo, unsigned char *base)
+@@ -1576,10 +1576,9 @@ compat_copy_entry_from_user(struct compa
+       struct xt_target *target;
+       struct ipt_entry *de;
+       unsigned int origsize;
+-      int ret, h;
++      int h;
+       struct xt_entry_match *ematch;
+-      ret = 0;
+       origsize = *size;
+       de = (struct ipt_entry *)*dstptr;
+       memcpy(de, e, sizeof(struct ipt_entry));
+@@ -1588,11 +1587,9 @@ compat_copy_entry_from_user(struct compa
+       *dstptr += sizeof(struct ipt_entry);
+       *size += sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry);
+-      xt_ematch_foreach(ematch, e) {
+-              ret = xt_compat_match_from_user(ematch, dstptr, size);
+-              if (ret != 0)
+-                      return ret;
+-      }
++      xt_ematch_foreach(ematch, e)
++              xt_compat_match_from_user(ematch, dstptr, size);
++
+       de->target_offset = e->target_offset - (origsize - *size);
+       t = compat_ipt_get_target(e);
+       target = t->u.kernel.target;
+@@ -1605,7 +1602,6 @@ compat_copy_entry_from_user(struct compa
+               if ((unsigned char *)de - base < newinfo->underflow[h])
+                       newinfo->underflow[h] -= origsize - *size;
+       }
+-      return ret;
+ }
+ static int
+@@ -1728,16 +1724,12 @@ translate_compat_table(struct net *net,
+       entry1 = newinfo->entries;
+       pos = entry1;
+       size = compatr->size;
+-      xt_entry_foreach(iter0, entry0, compatr->size) {
+-              ret = compat_copy_entry_from_user(iter0, &pos, &size,
+-                                                newinfo, entry1);
+-              if (ret != 0)
+-                      break;
+-      }
++      xt_entry_foreach(iter0, entry0, compatr->size)
++              compat_copy_entry_from_user(iter0, &pos, &size,
++                                          newinfo, entry1);
++
+       xt_compat_flush_offsets(AF_INET);
+       xt_compat_unlock(AF_INET);
+-      if (ret)
+-              goto free_newinfo;
+       ret = -ELOOP;
+       if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
+--- a/net/ipv6/netfilter/ip6_tables.c
++++ b/net/ipv6/netfilter/ip6_tables.c
+@@ -1579,7 +1579,7 @@ release_matches:
+       return ret;
+ }
+-static int
++static void
+ compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
+                           unsigned int *size,
+                           struct xt_table_info *newinfo, unsigned char *base)
+@@ -1587,10 +1587,9 @@ compat_copy_entry_from_user(struct compa
+       struct xt_entry_target *t;
+       struct ip6t_entry *de;
+       unsigned int origsize;
+-      int ret, h;
++      int h;
+       struct xt_entry_match *ematch;
+-      ret = 0;
+       origsize = *size;
+       de = (struct ip6t_entry *)*dstptr;
+       memcpy(de, e, sizeof(struct ip6t_entry));
+@@ -1599,11 +1598,9 @@ compat_copy_entry_from_user(struct compa
+       *dstptr += sizeof(struct ip6t_entry);
+       *size += sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
+-      xt_ematch_foreach(ematch, e) {
+-              ret = xt_compat_match_from_user(ematch, dstptr, size);
+-              if (ret != 0)
+-                      return ret;
+-      }
++      xt_ematch_foreach(ematch, e)
++              xt_compat_match_from_user(ematch, dstptr, size);
++
+       de->target_offset = e->target_offset - (origsize - *size);
+       t = compat_ip6t_get_target(e);
+       xt_compat_target_from_user(t, dstptr, size);
+@@ -1615,7 +1612,6 @@ compat_copy_entry_from_user(struct compa
+               if ((unsigned char *)de - base < newinfo->underflow[h])
+                       newinfo->underflow[h] -= origsize - *size;
+       }
+-      return ret;
+ }
+ static int compat_check_entry(struct ip6t_entry *e, struct net *net,
+@@ -1736,17 +1732,12 @@ translate_compat_table(struct net *net,
+       }
+       entry1 = newinfo->entries;
+       pos = entry1;
+-      size = compatr->size;
+-      xt_entry_foreach(iter0, entry0, compatr->size) {
+-              ret = compat_copy_entry_from_user(iter0, &pos, &size,
+-                                                newinfo, entry1);
+-              if (ret != 0)
+-                      break;
+-      }
++      xt_entry_foreach(iter0, entry0, compatr->size)
++              compat_copy_entry_from_user(iter0, &pos, &size,
++                                          newinfo, entry1);
++
+       xt_compat_flush_offsets(AF_INET6);
+       xt_compat_unlock(AF_INET6);
+-      if (ret)
+-              goto free_newinfo;
+       ret = -ELOOP;
+       if (!mark_source_chains(newinfo, compatr->valid_hooks, entry1))
+--- a/net/netfilter/x_tables.c
++++ b/net/netfilter/x_tables.c
+@@ -525,8 +525,8 @@ int xt_compat_match_offset(const struct
+ }
+ EXPORT_SYMBOL_GPL(xt_compat_match_offset);
+-int xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
+-                            unsigned int *size)
++void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
++                             unsigned int *size)
+ {
+       const struct xt_match *match = m->u.kernel.match;
+       struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m;
+@@ -548,7 +548,6 @@ int xt_compat_match_from_user(struct xt_
+       *size += off;
+       *dstptr += msize;
+-      return 0;
+ }
+ EXPORT_SYMBOL_GPL(xt_compat_match_from_user);
index 0a9a8a8bf02b8d41eb25df11d02455d3ebb3e374..3034bacd10dc727ced73a1ef295deb2c4c213921 100644 (file)
@@ -73,3 +73,9 @@ netfilter-x_tables-check-standard-target-size-too.patch
 netfilter-x_tables-check-for-bogus-target-offset.patch
 netfilter-x_tables-validate-all-offsets-and-sizes-in-a-rule.patch
 netfilter-x_tables-don-t-reject-valid-target-size-on-some-architectures.patch
+netfilter-arp_tables-simplify-translate_compat_table-args.patch
+netfilter-ip_tables-simplify-translate_compat_table-args.patch
+netfilter-ip6_tables-simplify-translate_compat_table-args.patch
+netfilter-x_tables-xt_compat_match_from_user-doesn-t-need-a-retval.patch
+netfilter-x_tables-do-compat-validation-via-translate_table.patch
+netfilter-x_tables-introduce-and-use-xt_copy_counters_from_user.patch