--- /dev/null
+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
+@@ -1204,6 +1204,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;
+@@ -1219,8 +1231,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;
+@@ -1291,7 +1302,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;
+@@ -1324,14 +1335,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;
+@@ -1343,8 +1349,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++) {
+@@ -1355,40 +1361,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;
+ }
+ }
+@@ -1398,17 +1403,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[raw_smp_processor_id()];
+ 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;
+ }
+@@ -1418,12 +1423,12 @@ 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;
+ xt_entry_foreach(iter1, entry1, newinfo->size) {
+- ret = check_target(iter1, name);
++ ret = check_target(iter1, compatr->name);
+ if (ret != 0)
+ break;
+ ++i;
+@@ -1468,7 +1473,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);
+@@ -1480,18 +1485,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)
+ {
+@@ -1522,10 +1515,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;
+
--- /dev/null
+From 1086bbe97a074844188c6c988fa0b1a98c3ccbb9 Mon Sep 17 00:00:00 2001
+From: Dave Jones <davej@codemonkey.org.uk>
+Date: Tue, 19 May 2015 20:55:17 -0400
+Subject: netfilter: ensure number of counters is >0 in do_replace()
+
+From: Dave Jones <davej@codemonkey.org.uk>
+
+commit 1086bbe97a074844188c6c988fa0b1a98c3ccbb9 upstream.
+
+After improving setsockopt() coverage in trinity, I started triggering
+vmalloc failures pretty reliably from this code path:
+
+warn_alloc_failed+0xe9/0x140
+__vmalloc_node_range+0x1be/0x270
+vzalloc+0x4b/0x50
+__do_replace+0x52/0x260 [ip_tables]
+do_ipt_set_ctl+0x15d/0x1d0 [ip_tables]
+nf_setsockopt+0x65/0x90
+ip_setsockopt+0x61/0xa0
+raw_setsockopt+0x16/0x60
+sock_common_setsockopt+0x14/0x20
+SyS_setsockopt+0x71/0xd0
+
+It turns out we don't validate that the num_counters field in the
+struct we pass in from userspace is initialized.
+
+The same problem also exists in ebtables, arptables, ipv6, and the
+compat variants.
+
+Signed-off-by: Dave Jones <davej@codemonkey.org.uk>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/bridge/netfilter/ebtables.c | 4 ++++
+ net/ipv4/netfilter/arp_tables.c | 6 ++++++
+ net/ipv4/netfilter/ip_tables.c | 6 ++++++
+ net/ipv6/netfilter/ip6_tables.c | 6 ++++++
+ 4 files changed, 22 insertions(+)
+
+--- a/net/bridge/netfilter/ebtables.c
++++ b/net/bridge/netfilter/ebtables.c
+@@ -1105,6 +1105,8 @@ static int do_replace(struct net *net, c
+ return -ENOMEM;
+ if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
+ return -ENOMEM;
++ if (tmp.num_counters == 0)
++ return -EINVAL;
+
+ tmp.name[sizeof(tmp.name) - 1] = 0;
+
+@@ -2150,6 +2152,8 @@ static int compat_copy_ebt_replace_from_
+ return -ENOMEM;
+ if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
+ return -ENOMEM;
++ if (tmp.num_counters == 0)
++ return -EINVAL;
+
+ memcpy(repl, &tmp, offsetof(struct ebt_replace, hook_entry));
+
+--- a/net/ipv4/netfilter/arp_tables.c
++++ b/net/ipv4/netfilter/arp_tables.c
+@@ -1081,6 +1081,9 @@ static int do_replace(struct net *net, c
+ /* overflow check */
+ if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
+ return -ENOMEM;
++ if (tmp.num_counters == 0)
++ return -EINVAL;
++
+ tmp.name[sizeof(tmp.name)-1] = 0;
+
+ newinfo = xt_alloc_table_info(tmp.size);
+@@ -1495,6 +1498,9 @@ static int compat_do_replace(struct net
+ return -ENOMEM;
+ if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
+ return -ENOMEM;
++ if (tmp.num_counters == 0)
++ return -EINVAL;
++
+ tmp.name[sizeof(tmp.name)-1] = 0;
+
+ newinfo = xt_alloc_table_info(tmp.size);
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -1267,6 +1267,9 @@ do_replace(struct net *net, const void _
+ /* overflow check */
+ if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
+ return -ENOMEM;
++ if (tmp.num_counters == 0)
++ return -EINVAL;
++
+ tmp.name[sizeof(tmp.name)-1] = 0;
+
+ newinfo = xt_alloc_table_info(tmp.size);
+@@ -1802,6 +1805,9 @@ compat_do_replace(struct net *net, void
+ return -ENOMEM;
+ if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
+ return -ENOMEM;
++ if (tmp.num_counters == 0)
++ return -EINVAL;
++
+ tmp.name[sizeof(tmp.name)-1] = 0;
+
+ newinfo = xt_alloc_table_info(tmp.size);
+--- a/net/ipv6/netfilter/ip6_tables.c
++++ b/net/ipv6/netfilter/ip6_tables.c
+@@ -1277,6 +1277,9 @@ do_replace(struct net *net, const void _
+ /* overflow check */
+ if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
+ return -ENOMEM;
++ if (tmp.num_counters == 0)
++ return -EINVAL;
++
+ tmp.name[sizeof(tmp.name)-1] = 0;
+
+ newinfo = xt_alloc_table_info(tmp.size);
+@@ -1811,6 +1814,9 @@ compat_do_replace(struct net *net, void
+ return -ENOMEM;
+ if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
+ return -ENOMEM;
++ if (tmp.num_counters == 0)
++ return -EINVAL;
++
+ tmp.name[sizeof(tmp.name)-1] = 0;
+
+ newinfo = xt_alloc_table_info(tmp.size);
--- /dev/null
+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 | 61 ++++++++++++++++------------------------
+ 1 file changed, 25 insertions(+), 36 deletions(-)
+
+--- a/net/ipv6/netfilter/ip6_tables.c
++++ b/net/ipv6/netfilter/ip6_tables.c
+@@ -1456,7 +1456,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,
+ unsigned int hookmask,
+ int *size)
+@@ -1494,8 +1493,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;
+@@ -1531,8 +1529,8 @@ 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, e->comefrom, &off);
++ ret = compat_find_calc_match(ematch, &e->ipv6, e->comefrom,
++ &off);
+ if (ret != 0)
+ goto release_matches;
+ ++j;
+@@ -1581,7 +1579,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;
+@@ -1655,14 +1653,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;
+@@ -1674,8 +1667,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++) {
+@@ -1686,40 +1679,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;
+ }
+ }
+@@ -1729,17 +1721,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[raw_smp_processor_id()];
+ 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;
+ }
+@@ -1749,12 +1741,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;
+@@ -1799,7 +1791,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);
+@@ -1842,10 +1834,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;
+
--- /dev/null
+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 | 61 ++++++++++++++++-------------------------
+ 1 file changed, 25 insertions(+), 36 deletions(-)
+
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -1444,7 +1444,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,
+ unsigned int hookmask,
+ int *size)
+@@ -1482,8 +1481,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;
+@@ -1519,8 +1517,8 @@ 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, e->comefrom, &off);
++ ret = compat_find_calc_match(ematch, &e->ip, e->comefrom,
++ &off);
+ if (ret != 0)
+ goto release_matches;
+ ++j;
+@@ -1569,7 +1567,7 @@ release_matches:
+
+ static int
+ compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr,
+- unsigned int *size, const char *name,
++ unsigned int *size,
+ struct xt_table_info *newinfo, unsigned char *base)
+ {
+ struct xt_entry_target *t;
+@@ -1645,14 +1643,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;
+@@ -1664,8 +1657,8 @@ translate_compat_table(struct net *net,
+
+ info = *pinfo;
+ entry0 = *pentry0;
+- size = total_size;
+- info->number = number;
++ size = compatr->size;
++ info->number = compatr->num_entries;
+
+ /* Init all hooks to impossible value. */
+ for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+@@ -1676,40 +1669,39 @@ translate_compat_table(struct net *net,
+ duprintf("translate_compat_table: size %u\n", info->size);
+ j = 0;
+ xt_compat_lock(AF_INET);
+- xt_compat_init_offsets(AF_INET, number);
++ xt_compat_init_offsets(AF_INET, compatr->num_entries);
+ /* Walk through entries, checking offsets. */
+- xt_entry_foreach(iter0, entry0, total_size) {
++ xt_entry_foreach(iter0, entry0, compatr->size) {
+ ret = check_compat_entry_size_and_hooks(iter0, info, &size,
+ entry0,
+- entry0 + total_size,
+- hook_entries,
+- underflows,
+- name);
++ entry0 + compatr->size,
++ compatr->hook_entry,
++ compatr->underflow);
+ if (ret != 0)
+ goto out_unlock;
+ ++j;
+ }
+
+ ret = -EINVAL;
+- if (j != number) {
++ if (j != compatr->num_entries) {
+ duprintf("translate_compat_table: %u not %u entries\n",
+- j, number);
++ j, compatr->num_entries);
+ goto out_unlock;
+ }
+
+ /* Check hooks all assigned */
+ for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+ /* Only hooks which are valid */
+- if (!(valid_hooks & (1 << i)))
++ if (!(compatr->valid_hooks & (1 << i)))
+ continue;
+ if (info->hook_entry[i] == 0xFFFFFFFF) {
+ duprintf("Invalid hook entry %u %u\n",
+- i, hook_entries[i]);
++ i, info->hook_entry[i]);
+ goto out_unlock;
+ }
+ if (info->underflow[i] == 0xFFFFFFFF) {
+ duprintf("Invalid underflow %u %u\n",
+- i, underflows[i]);
++ i, info->underflow[i]);
+ goto out_unlock;
+ }
+ }
+@@ -1719,17 +1711,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[raw_smp_processor_id()];
+ pos = entry1;
+- size = total_size;
+- xt_entry_foreach(iter0, entry0, total_size) {
++ size = compatr->size;
++ xt_entry_foreach(iter0, entry0, compatr->size) {
+ ret = compat_copy_entry_from_user(iter0, &pos, &size,
+- name, newinfo, entry1);
++ newinfo, entry1);
+ if (ret != 0)
+ break;
+ }
+@@ -1739,12 +1731,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;
+@@ -1789,7 +1781,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);
+@@ -1832,10 +1824,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;
+
--- /dev/null
+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 | 109 +++++-----------------------
+ net/ipv4/netfilter/ip_tables.c | 151 +++++++---------------------------------
+ net/ipv6/netfilter/ip6_tables.c | 145 +++++---------------------------------
+ net/netfilter/x_tables.c | 8 ++
+ 4 files changed, 80 insertions(+), 333 deletions(-)
+
+--- a/net/ipv4/netfilter/arp_tables.c
++++ b/net/ipv4/netfilter/arp_tables.c
+@@ -1227,19 +1227,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 ||
+@@ -1284,17 +1282,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:
+@@ -1344,7 +1331,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;
+
+@@ -1353,12 +1340,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);
+@@ -1367,9 +1348,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;
+@@ -1382,23 +1361,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)
+@@ -1415,51 +1377,25 @@ 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;
++ memcpy(&repl, compatr, sizeof(*compatr));
+
+- i = 0;
+- xt_entry_foreach(iter1, entry1, newinfo->size) {
+- ret = check_target(iter1, compatr->name);
+- if (ret != 0)
+- 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];
+ }
+
+- /* And one copy for every other CPU */
+- for_each_possible_cpu(i)
+- if (newinfo->entries[i] && newinfo->entries[i] != entry1)
+- memcpy(newinfo->entries[i], entry1, newinfo->size);
++ 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;
+@@ -1468,17 +1404,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 ||
+@@ -1544,17 +1542,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:
+@@ -1597,6 +1584,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;
+@@ -1606,41 +1594,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;
+-
+- 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);
+- }
+- return ret;
+-}
+-
+-static int
+ translate_compat_table(struct net *net,
+ struct xt_table_info **pinfo,
+ void **pentry0,
+@@ -1650,7 +1603,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;
+
+@@ -1659,12 +1612,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);
+@@ -1673,9 +1620,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;
+@@ -1688,23 +1633,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)
+@@ -1712,8 +1640,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[raw_smp_processor_id()];
+ pos = entry1;
+@@ -1722,51 +1650,29 @@ 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];
+ }
+
+- /* And one copy for every other CPU */
+- for_each_possible_cpu(i)
+- if (newinfo->entries[i] && newinfo->entries[i] != entry1)
+- memcpy(newinfo->entries[i], entry1, newinfo->size);
++ 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;
+@@ -1775,17 +1681,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 ||
+@@ -1556,17 +1554,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:
+@@ -1615,41 +1602,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;
+-
+- 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);
+- }
+- return ret;
+-}
+-
+ static int
+ translate_compat_table(struct net *net,
+ struct xt_table_info **pinfo,
+@@ -1660,7 +1612,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;
+
+@@ -1669,12 +1621,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);
+@@ -1683,9 +1629,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;
+@@ -1698,23 +1642,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)
+@@ -1722,60 +1649,33 @@ 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[raw_smp_processor_id()];
+ 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];
+ }
+
+- /* And one copy for every other CPU */
+- for_each_possible_cpu(i)
+- if (newinfo->entries[i] && newinfo->entries[i] != entry1)
+- memcpy(newinfo->entries[i], entry1, newinfo->size);
++ 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;
+@@ -1784,17 +1684,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
+@@ -552,6 +552,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));
+@@ -565,6 +566,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;
+@@ -782,6 +786,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));
+@@ -795,6 +800,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;
--- /dev/null
+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
+@@ -425,7 +425,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
+@@ -1300,7 +1300,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)
+@@ -1309,9 +1309,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));
+@@ -1332,7 +1331,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,
+@@ -1411,16 +1409,11 @@ static int translate_compat_table(struct
+ entry1 = newinfo->entries[raw_smp_processor_id()];
+ 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
+@@ -1565,7 +1565,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)
+@@ -1574,10 +1574,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));
+@@ -1586,11 +1585,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;
+@@ -1603,7 +1600,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
+@@ -1719,16 +1715,12 @@ translate_compat_table(struct net *net,
+ entry1 = newinfo->entries[raw_smp_processor_id()];
+ 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
+@@ -1577,7 +1577,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)
+@@ -1585,10 +1585,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));
+@@ -1597,11 +1596,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);
+@@ -1613,7 +1610,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,
+@@ -1728,17 +1724,12 @@ translate_compat_table(struct net *net,
+ }
+ entry1 = newinfo->entries[raw_smp_processor_id()];
+ 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
+@@ -545,8 +545,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;
+@@ -568,7 +568,6 @@ int xt_compat_match_from_user(struct xt_
+
+ *size += off;
+ *dstptr += msize;
+- return 0;
+ }
+ EXPORT_SYMBOL_GPL(xt_compat_match_from_user);
+
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-ensure-number-of-counters-is-0-in-do_replace.patch
+netfilter-x_tables-do-compat-validation-via-translate_table.patch