[linux] 02/02: netfilter: x_tables: Add fixes for CVE-2016-4997, CVE-2016-4998

debian-kernel at lists.debian.org debian-kernel at lists.debian.org
Fri Jun 24 21:51:48 UTC 2016


This is an automated email from the git hooks/post-receive script.

benh pushed a commit to branch jessie-security
in repository linux.

commit 6f66940c04f53d47781eddf3dca02569a79d1189
Author: Ben Hutchings <ben at decadent.org.uk>
Date:   Fri Jun 24 23:29:28 2016 +0200

    netfilter: x_tables: Add fixes for CVE-2016-4997, CVE-2016-4998
    
    ...and probably other issues never assigned an ID.
---
 debian/changelog                                   |  19 +-
 ...rp_tables-simplify-translate_compat_table.patch | 208 ++++++
 ...p6_tables-simplify-translate_compat_table.patch | 185 +++++
 ...p_tables-simplify-translate_compat_table-.patch | 184 +++++
 ..._tables-add-and-use-xt_check_entry_offset.patch | 151 ++++
 ..._tables-add-compat-version-of-xt_check_en.patch | 105 +++
 ...ilter-x_tables-assert-minimum-target-size.patch |  25 +
 ...er-x_tables-check-for-bogus-target-offset.patch | 164 +++++
 ...r-x_tables-check-standard-target-size-too.patch |  60 ++
 ..._tables-do-compat-validation-via-translat.patch | 781 +++++++++++++++++++++
 ..._tables-don-t-move-to-non-existent-next-r.patch | 100 +++
 ..._tables-don-t-reject-valid-target-size-on.patch |  54 ++
 ..._tables-introduce-and-use-xt_copy_counter.patch | 331 +++++++++
 ...etfilter-x_tables-kill-check_entry-helper.patch | 149 ++++
 ..._tables-validate-all-offsets-and-sizes-in.patch | 137 ++++
 ...filter-x_tables-validate-targets-of-jumps.patch | 127 ++++
 ..._tables-xt_compat_match_from_user-doesn-t.patch | 234 ++++++
 debian/patches/series                              |  16 +
 18 files changed, 3029 insertions(+), 1 deletion(-)

diff --git a/debian/changelog b/debian/changelog
index f4b7b18..29a06dd 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -5,10 +5,27 @@ linux (3.16.7-ckt25-2+deb8u1) UNRELEASED; urgency=medium
   * [s390*] mm: four page table levels vs. fork (CVE-2016-2143)
   * [amd64] iopl: Properly context-switch IOPL on Xen PV (CVE-2016-3157)
   * [amd64] entry/compat: Add missing CLAC to entry_INT80_32
-  * netfilter: x_tables: Fix parsing of IPT_SO_SET_REPLACE blobs (CVE-2016-3134)
+  * netfilter: x_tables: Fix parsing of IPT_SO_SET_REPLACE blobs
+    (CVE-2016-3134, CVE-2016-4997, CVE-2016-4998)
     - validate e->target_offset early
     - make sure e->next_offset covers remaining blob size
     - fix unconditional helper
+    - don't move to non-existent next rule
+    - validate targets of jumps
+    - add and use xt_check_entry_offsets
+    - kill check_entry helper
+    - assert minimum target size
+    - add compat version of xt_check_entry_offsets
+    - check standard target size too
+    - check for bogus target offset
+    - validate all offsets and sizes in a rule
+    - don't reject valid target size on some
+    - arp_tables: simplify translate_compat_table args
+    - ip_tables: simplify translate_compat_table args
+    - ip6_tables: simplify translate_compat_table args
+    - xt_compat_match_from_user doesn't need a retval
+    - do compat validation via translate_table
+    - introduce and use xt_copy_counters_from_user
   * ipv4: Don't do expensive useless work during inetdev destroy.
     (CVE-2016-3156)
   * [x86] standardize mmap_rnd() usage
diff --git a/debian/patches/bugfix/all/netfilter-arp_tables-simplify-translate_compat_table.patch b/debian/patches/bugfix/all/netfilter-arp_tables-simplify-translate_compat_table.patch
new file mode 100644
index 0000000..bbd7bdc
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-arp_tables-simplify-translate_compat_table.patch
@@ -0,0 +1,208 @@
+From: Florian Westphal <fw at strlen.de>
+Date: Fri, 1 Apr 2016 14:17:32 +0200
+Subject: netfilter: arp_tables: simplify translate_compat_table args
+Origin: https://git.kernel.org/linus/8dddd32756f6fe8e4e82a63361119b7e2384e02f
+
+Signed-off-by: Florian Westphal <fw at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+[bwh: Backported to 3.6: adjust context]
+---
+ 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;
+ 
diff --git a/debian/patches/bugfix/all/netfilter-ip6_tables-simplify-translate_compat_table.patch b/debian/patches/bugfix/all/netfilter-ip6_tables-simplify-translate_compat_table.patch
new file mode 100644
index 0000000..a565ef4
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-ip6_tables-simplify-translate_compat_table.patch
@@ -0,0 +1,185 @@
+From: Florian Westphal <fw at strlen.de>
+Date: Fri, 1 Apr 2016 14:17:31 +0200
+Subject: netfilter: ip6_tables: simplify translate_compat_table args
+Origin: https://git.kernel.org/linus/329a0807124f12fe1c8032f95d8a8eb47047fb0e
+
+Signed-off-by: Florian Westphal <fw at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+[bwh: Backported to 3.16: adjust context]
+---
+--- 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;
+ 
diff --git a/debian/patches/bugfix/all/netfilter-ip_tables-simplify-translate_compat_table-.patch b/debian/patches/bugfix/all/netfilter-ip_tables-simplify-translate_compat_table-.patch
new file mode 100644
index 0000000..7518397
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-ip_tables-simplify-translate_compat_table-.patch
@@ -0,0 +1,184 @@
+From: Florian Westphal <fw at strlen.de>
+Date: Fri, 1 Apr 2016 14:17:30 +0200
+Subject: netfilter: ip_tables: simplify translate_compat_table args
+Origin: https://git.kernel.org/linus/7d3f843eed29222254c9feab481f55175a1afcc9
+
+Signed-off-by: Florian Westphal <fw at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+[bwh: Backported to 3.16: adjust context]
+---
+--- 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,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, e->comefrom, &off);
++		ret = compat_find_calc_match(ematch, &e->ip, e->comefrom, &off);
+ 		if (ret != 0)
+ 			goto release_matches;
+ 		++j;
+@@ -1569,7 +1566,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 +1642,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 +1656,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 +1668,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 +1710,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 +1730,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 +1780,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 +1823,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/debian/patches/bugfix/all/netfilter-x_tables-add-and-use-xt_check_entry_offset.patch b/debian/patches/bugfix/all/netfilter-x_tables-add-and-use-xt_check_entry_offset.patch
new file mode 100644
index 0000000..7ee23c0
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-x_tables-add-and-use-xt_check_entry_offset.patch
@@ -0,0 +1,151 @@
+From: Florian Westphal <fw at strlen.de>
+Date: Fri, 1 Apr 2016 14:17:23 +0200
+Subject: netfilter: x_tables: add and use xt_check_entry_offsets
+Origin: https://git.kernel.org/linus/7d35812c3214afa5b37a675113555259cfd67b98
+
+Currently arp/ip and ip6tables each implement a short helper to check that
+the target offset is large enough to hold one xt_entry_target struct and
+that t->u.target_size fits within the current rule.
+
+Unfortunately these checks are not sufficient.
+
+To avoid adding new tests to all of ip/ip6/arptables move the current
+checks into a helper, then extend this helper in followup patches.
+
+Signed-off-by: Florian Westphal <fw at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+ include/linux/netfilter/x_tables.h |  4 ++++
+ net/ipv4/netfilter/arp_tables.c    | 11 +----------
+ net/ipv4/netfilter/ip_tables.c     | 12 +-----------
+ net/ipv6/netfilter/ip6_tables.c    | 12 +-----------
+ net/netfilter/x_tables.c           | 34 ++++++++++++++++++++++++++++++++++
+ 5 files changed, 41 insertions(+), 32 deletions(-)
+
+--- a/include/linux/netfilter/x_tables.h
++++ b/include/linux/netfilter/x_tables.h
+@@ -239,6 +239,10 @@ void xt_unregister_match(struct xt_match
+ int xt_register_matches(struct xt_match *match, unsigned int n);
+ void xt_unregister_matches(struct xt_match *match, unsigned int n);
+ 
++int xt_check_entry_offsets(const void *base,
++			   unsigned int target_offset,
++			   unsigned int next_offset);
++
+ int xt_check_match(struct xt_mtchk_param *, unsigned int size, u_int8_t proto,
+ 		   bool inv_proto);
+ int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto,
+--- a/net/ipv4/netfilter/arp_tables.c
++++ b/net/ipv4/netfilter/arp_tables.c
+@@ -492,19 +492,10 @@ static int mark_source_chains(const stru
+ 
+ static inline int check_entry(const struct arpt_entry *e)
+ {
+-	const struct xt_entry_target *t;
+-
+ 	if (!arp_checkentry(&e->arp))
+ 		return -EINVAL;
+ 
+-	if (e->target_offset + sizeof(struct xt_entry_target) > e->next_offset)
+-		return -EINVAL;
+-
+-	t = arpt_get_target_c(e);
+-	if (e->target_offset + t->u.target_size > e->next_offset)
+-		return -EINVAL;
+-
+-	return 0;
++	return xt_check_entry_offsets(e, e->target_offset, e->next_offset);
+ }
+ 
+ static inline int check_target(struct arpt_entry *e, const char *name)
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -586,20 +586,10 @@ static void cleanup_match(struct xt_entr
+ static int
+ check_entry(const struct ipt_entry *e)
+ {
+-	const struct xt_entry_target *t;
+-
+ 	if (!ip_checkentry(&e->ip))
+ 		return -EINVAL;
+ 
+-	if (e->target_offset + sizeof(struct xt_entry_target) >
+-	    e->next_offset)
+-		return -EINVAL;
+-
+-	t = ipt_get_target_c(e);
+-	if (e->target_offset + t->u.target_size > e->next_offset)
+-		return -EINVAL;
+-
+-	return 0;
++	return xt_check_entry_offsets(e, e->target_offset, e->next_offset);
+ }
+ 
+ static int
+--- a/net/ipv6/netfilter/ip6_tables.c
++++ b/net/ipv6/netfilter/ip6_tables.c
+@@ -596,20 +596,10 @@ static void cleanup_match(struct xt_entr
+ static int
+ check_entry(const struct ip6t_entry *e)
+ {
+-	const struct xt_entry_target *t;
+-
+ 	if (!ip6_checkentry(&e->ipv6))
+ 		return -EINVAL;
+ 
+-	if (e->target_offset + sizeof(struct xt_entry_target) >
+-	    e->next_offset)
+-		return -EINVAL;
+-
+-	t = ip6t_get_target_c(e);
+-	if (e->target_offset + t->u.target_size > e->next_offset)
+-		return -EINVAL;
+-
+-	return 0;
++	return xt_check_entry_offsets(e, e->target_offset, e->next_offset);
+ }
+ 
+ static int check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
+--- a/net/netfilter/x_tables.c
++++ b/net/netfilter/x_tables.c
+@@ -560,6 +560,40 @@ int xt_compat_match_to_user(const struct
+ EXPORT_SYMBOL_GPL(xt_compat_match_to_user);
+ #endif /* CONFIG_COMPAT */
+ 
++/**
++ * xt_check_entry_offsets - validate arp/ip/ip6t_entry
++ *
++ * @base: pointer to arp/ip/ip6t_entry
++ * @target_offset: the arp/ip/ip6_t->target_offset
++ * @next_offset: the arp/ip/ip6_t->next_offset
++ *
++ * validates that target_offset and next_offset are sane.
++ *
++ * The arp/ip/ip6t_entry structure @base must have passed following tests:
++ * - it must point to a valid memory location
++ * - base to base + next_offset must be accessible, i.e. not exceed allocated
++ *   length.
++ *
++ * Return: 0 on success, negative errno on failure.
++ */
++int xt_check_entry_offsets(const void *base,
++			   unsigned int target_offset,
++			   unsigned int next_offset)
++{
++	const struct xt_entry_target *t;
++	const char *e = base;
++
++	if (target_offset + sizeof(*t) > next_offset)
++		return -EINVAL;
++
++	t = (void *)(e + target_offset);
++	if (target_offset + t->u.target_size > next_offset)
++		return -EINVAL;
++
++	return 0;
++}
++EXPORT_SYMBOL(xt_check_entry_offsets);
++
+ int xt_check_target(struct xt_tgchk_param *par,
+ 		    unsigned int size, u_int8_t proto, bool inv_proto)
+ {
diff --git a/debian/patches/bugfix/all/netfilter-x_tables-add-compat-version-of-xt_check_en.patch b/debian/patches/bugfix/all/netfilter-x_tables-add-compat-version-of-xt_check_en.patch
new file mode 100644
index 0000000..d6eac7b
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-x_tables-add-compat-version-of-xt_check_en.patch
@@ -0,0 +1,105 @@
+From: Florian Westphal <fw at strlen.de>
+Date: Fri, 1 Apr 2016 14:17:26 +0200
+Subject: netfilter: x_tables: add compat version of xt_check_entry_offsets
+Origin: https://git.kernel.org/linus/fc1221b3a163d1386d1052184202d5dc50d302d1
+
+32bit rulesets have different layout and alignment requirements, so once
+more integrity checks get added to xt_check_entry_offsets it will reject
+well-formed 32bit rulesets.
+
+Signed-off-by: Florian Westphal <fw at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+ include/linux/netfilter/x_tables.h |  3 +++
+ net/ipv4/netfilter/arp_tables.c    |  3 ++-
+ net/ipv4/netfilter/ip_tables.c     |  3 ++-
+ net/ipv6/netfilter/ip6_tables.c    |  3 ++-
+ net/netfilter/x_tables.c           | 22 ++++++++++++++++++++++
+ 5 files changed, 31 insertions(+), 3 deletions(-)
+
+--- a/include/linux/netfilter/x_tables.h
++++ b/include/linux/netfilter/x_tables.h
+@@ -435,6 +435,9 @@ void xt_compat_target_from_user(struct x
+ 				unsigned int *size);
+ int xt_compat_target_to_user(const struct xt_entry_target *t,
+ 			     void __user **dstptr, unsigned int *size);
++int xt_compat_check_entry_offsets(const void *base,
++				  unsigned int target_offset,
++				  unsigned int next_offset);
+ 
+ #endif /* CONFIG_COMPAT */
+ #endif /* _X_TABLES_H */
+--- a/net/ipv4/netfilter/arp_tables.c
++++ b/net/ipv4/netfilter/arp_tables.c
+@@ -1244,7 +1244,8 @@ check_compat_entry_size_and_hooks(struct
+ 	if (!arp_checkentry(&e->arp))
+ 		return -EINVAL;
+ 
+-	ret = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
++	ret = xt_compat_check_entry_offsets(e, e->target_offset,
++					    e->next_offset);
+ 	if (ret)
+ 		return ret;
+ 
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -1509,7 +1509,8 @@ check_compat_entry_size_and_hooks(struct
+ 	if (!ip_checkentry(&e->ip))
+ 		return -EINVAL;
+ 
+-	ret = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
++	ret = xt_compat_check_entry_offsets(e,
++					    e->target_offset, e->next_offset);
+ 	if (ret)
+ 		return ret;
+ 
+--- a/net/ipv6/netfilter/ip6_tables.c
++++ b/net/ipv6/netfilter/ip6_tables.c
+@@ -1521,7 +1521,8 @@ check_compat_entry_size_and_hooks(struct
+ 	if (!ip6_checkentry(&e->ipv6))
+ 		return -EINVAL;
+ 
+-	ret = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
++	ret = xt_compat_check_entry_offsets(e,
++					    e->target_offset, e->next_offset);
+ 	if (ret)
+ 		return ret;
+ 
+--- a/net/netfilter/x_tables.c
++++ b/net/netfilter/x_tables.c
+@@ -558,6 +558,27 @@ int xt_compat_match_to_user(const struct
+ 	return 0;
+ }
+ EXPORT_SYMBOL_GPL(xt_compat_match_to_user);
++
++int xt_compat_check_entry_offsets(const void *base,
++				  unsigned int target_offset,
++				  unsigned int next_offset)
++{
++	const struct compat_xt_entry_target *t;
++	const char *e = base;
++
++	if (target_offset + sizeof(*t) > next_offset)
++		return -EINVAL;
++
++	t = (void *)(e + target_offset);
++	if (t->u.target_size < sizeof(*t))
++		return -EINVAL;
++
++	if (target_offset + t->u.target_size > next_offset)
++		return -EINVAL;
++
++	return 0;
++}
++EXPORT_SYMBOL(xt_compat_check_entry_offsets);
+ #endif /* CONFIG_COMPAT */
+ 
+ /**
+@@ -568,6 +589,7 @@ EXPORT_SYMBOL_GPL(xt_compat_match_to_use
+  * @next_offset: the arp/ip/ip6_t->next_offset
+  *
+  * validates that target_offset and next_offset are sane.
++ * Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
+  *
+  * The arp/ip/ip6t_entry structure @base must have passed following tests:
+  * - it must point to a valid memory location
diff --git a/debian/patches/bugfix/all/netfilter-x_tables-assert-minimum-target-size.patch b/debian/patches/bugfix/all/netfilter-x_tables-assert-minimum-target-size.patch
new file mode 100644
index 0000000..8f32eb2
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-x_tables-assert-minimum-target-size.patch
@@ -0,0 +1,25 @@
+From: Florian Westphal <fw at strlen.de>
+Date: Fri, 1 Apr 2016 14:17:25 +0200
+Subject: netfilter: x_tables: assert minimum target size
+Origin: https://git.kernel.org/linus/a08e4e190b866579896c09af59b3bdca821da2cd
+
+The target size includes the size of the xt_entry_target struct.
+
+Signed-off-by: Florian Westphal <fw at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+ net/netfilter/x_tables.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/net/netfilter/x_tables.c
++++ b/net/netfilter/x_tables.c
+@@ -587,6 +587,9 @@ int xt_check_entry_offsets(const void *b
+ 		return -EINVAL;
+ 
+ 	t = (void *)(e + target_offset);
++	if (t->u.target_size < sizeof(*t))
++		return -EINVAL;
++
+ 	if (target_offset + t->u.target_size > next_offset)
+ 		return -EINVAL;
+ 
diff --git a/debian/patches/bugfix/all/netfilter-x_tables-check-for-bogus-target-offset.patch b/debian/patches/bugfix/all/netfilter-x_tables-check-for-bogus-target-offset.patch
new file mode 100644
index 0000000..494910d
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-x_tables-check-for-bogus-target-offset.patch
@@ -0,0 +1,164 @@
+From: Florian Westphal <fw at strlen.de>
+Date: Fri, 1 Apr 2016 14:17:28 +0200
+Subject: netfilter: x_tables: check for bogus target offset
+Origin: https://git.kernel.org/linus/ce683e5f9d045e5d67d1312a42b359cb2ab2a13c
+
+We're currently asserting that targetoff + targetsize <= nextoff.
+
+Extend it to also check that targetoff is >= sizeof(xt_entry).
+Since this is generic code, add an argument pointing to the start of the
+match/target, we can then derive the base structure size from the delta.
+
+We also need the e->elems pointer in a followup change to validate matches.
+
+Signed-off-by: Florian Westphal <fw at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+ include/linux/netfilter/x_tables.h |  4 ++--
+ net/ipv4/netfilter/arp_tables.c    |  5 +++--
+ net/ipv4/netfilter/ip_tables.c     |  5 +++--
+ net/ipv6/netfilter/ip6_tables.c    |  5 +++--
+ net/netfilter/x_tables.c           | 17 +++++++++++++++--
+ 5 files changed, 26 insertions(+), 10 deletions(-)
+
+--- a/include/linux/netfilter/x_tables.h
++++ b/include/linux/netfilter/x_tables.h
+@@ -239,7 +239,7 @@ void xt_unregister_match(struct xt_match
+ int xt_register_matches(struct xt_match *match, unsigned int n);
+ void xt_unregister_matches(struct xt_match *match, unsigned int n);
+ 
+-int xt_check_entry_offsets(const void *base,
++int xt_check_entry_offsets(const void *base, const char *elems,
+ 			   unsigned int target_offset,
+ 			   unsigned int next_offset);
+ 
+@@ -435,7 +435,7 @@ void xt_compat_target_from_user(struct x
+ 				unsigned int *size);
+ int xt_compat_target_to_user(const struct xt_entry_target *t,
+ 			     void __user **dstptr, unsigned int *size);
+-int xt_compat_check_entry_offsets(const void *base,
++int xt_compat_check_entry_offsets(const void *base, const char *elems,
+ 				  unsigned int target_offset,
+ 				  unsigned int next_offset);
+ 
+--- a/net/ipv4/netfilter/arp_tables.c
++++ b/net/ipv4/netfilter/arp_tables.c
+@@ -582,7 +582,8 @@ static inline int check_entry_size_and_h
+ 	if (!arp_checkentry(&e->arp))
+ 		return -EINVAL;
+ 
+-	err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
++	err = xt_check_entry_offsets(e, e->elems, e->target_offset,
++				     e->next_offset);
+ 	if (err)
+ 		return err;
+ 
+@@ -1244,7 +1245,7 @@ check_compat_entry_size_and_hooks(struct
+ 	if (!arp_checkentry(&e->arp))
+ 		return -EINVAL;
+ 
+-	ret = xt_compat_check_entry_offsets(e, e->target_offset,
++	ret = xt_compat_check_entry_offsets(e, e->elems, e->target_offset,
+ 					    e->next_offset);
+ 	if (ret)
+ 		return ret;
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -742,7 +742,8 @@ check_entry_size_and_hooks(struct ipt_en
+ 	if (!ip_checkentry(&e->ip))
+ 		return -EINVAL;
+ 
+-	err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
++	err = xt_check_entry_offsets(e, e->elems, e->target_offset,
++				     e->next_offset);
+ 	if (err)
+ 		return err;
+ 
+@@ -1509,7 +1510,7 @@ check_compat_entry_size_and_hooks(struct
+ 	if (!ip_checkentry(&e->ip))
+ 		return -EINVAL;
+ 
+-	ret = xt_compat_check_entry_offsets(e,
++	ret = xt_compat_check_entry_offsets(e, e->elems,
+ 					    e->target_offset, e->next_offset);
+ 	if (ret)
+ 		return ret;
+--- a/net/ipv6/netfilter/ip6_tables.c
++++ b/net/ipv6/netfilter/ip6_tables.c
+@@ -753,7 +753,8 @@ check_entry_size_and_hooks(struct ip6t_e
+ 	if (!ip6_checkentry(&e->ipv6))
+ 		return -EINVAL;
+ 
+-	err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
++	err = xt_check_entry_offsets(e, e->elems, e->target_offset,
++				     e->next_offset);
+ 	if (err)
+ 		return err;
+ 
+@@ -1521,7 +1522,7 @@ check_compat_entry_size_and_hooks(struct
+ 	if (!ip6_checkentry(&e->ipv6))
+ 		return -EINVAL;
+ 
+-	ret = xt_compat_check_entry_offsets(e,
++	ret = xt_compat_check_entry_offsets(e, e->elems,
+ 					    e->target_offset, e->next_offset);
+ 	if (ret)
+ 		return ret;
+--- a/net/netfilter/x_tables.c
++++ b/net/netfilter/x_tables.c
+@@ -565,14 +565,17 @@ struct compat_xt_standard_target {
+ 	compat_uint_t verdict;
+ };
+ 
+-/* see xt_check_entry_offsets */
+-int xt_compat_check_entry_offsets(const void *base,
++int xt_compat_check_entry_offsets(const void *base, const char *elems,
+ 				  unsigned int target_offset,
+ 				  unsigned int next_offset)
+ {
++	long size_of_base_struct = elems - (const char *)base;
+ 	const struct compat_xt_entry_target *t;
+ 	const char *e = base;
+ 
++	if (target_offset < size_of_base_struct)
++		return -EINVAL;
++
+ 	if (target_offset + sizeof(*t) > next_offset)
+ 		return -EINVAL;
+ 
+@@ -596,12 +599,16 @@ EXPORT_SYMBOL(xt_compat_check_entry_offs
+  * xt_check_entry_offsets - validate arp/ip/ip6t_entry
+  *
+  * @base: pointer to arp/ip/ip6t_entry
++ * @elems: pointer to first xt_entry_match, i.e. ip(6)t_entry->elems
+  * @target_offset: the arp/ip/ip6_t->target_offset
+  * @next_offset: the arp/ip/ip6_t->next_offset
+  *
+  * validates that target_offset and next_offset are sane.
+  * Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
+  *
++ * This function does not validate the targets or matches themselves, it
++ * only tests that all the offsets and sizes are correct.
++ *
+  * The arp/ip/ip6t_entry structure @base must have passed following tests:
+  * - it must point to a valid memory location
+  * - base to base + next_offset must be accessible, i.e. not exceed allocated
+@@ -610,12 +617,18 @@ EXPORT_SYMBOL(xt_compat_check_entry_offs
+  * Return: 0 on success, negative errno on failure.
+  */
+ int xt_check_entry_offsets(const void *base,
++			   const char *elems,
+ 			   unsigned int target_offset,
+ 			   unsigned int next_offset)
+ {
++	long size_of_base_struct = elems - (const char *)base;
+ 	const struct xt_entry_target *t;
+ 	const char *e = base;
+ 
++	/* target start is within the ip/ip6/arpt_entry struct */
++	if (target_offset < size_of_base_struct)
++		return -EINVAL;
++
+ 	if (target_offset + sizeof(*t) > next_offset)
+ 		return -EINVAL;
+ 
diff --git a/debian/patches/bugfix/all/netfilter-x_tables-check-standard-target-size-too.patch b/debian/patches/bugfix/all/netfilter-x_tables-check-standard-target-size-too.patch
new file mode 100644
index 0000000..5e73ba5
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-x_tables-check-standard-target-size-too.patch
@@ -0,0 +1,60 @@
+From: Florian Westphal <fw at strlen.de>
+Date: Fri, 1 Apr 2016 14:17:27 +0200
+Subject: netfilter: x_tables: check standard target size too
+Origin: https://git.kernel.org/linus/7ed2abddd20cf8f6bd27f65bd218f26fa5bf7f44
+
+We have targets and standard targets -- the latter carries a verdict.
+
+The ip/ip6tables validation functions will access t->verdict for the
+standard targets to fetch the jump offset or verdict for chainloop
+detection, but this happens before the targets get checked/validated.
+
+Thus we also need to check for verdict presence here, else t->verdict
+can point right after a blob.
+
+Spotted with UBSAN while testing malformed blobs.
+
+Signed-off-by: Florian Westphal <fw at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+ net/netfilter/x_tables.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/net/netfilter/x_tables.c
++++ b/net/netfilter/x_tables.c
+@@ -559,6 +559,13 @@ int xt_compat_match_to_user(const struct
+ }
+ EXPORT_SYMBOL_GPL(xt_compat_match_to_user);
+ 
++/* non-compat version may have padding after verdict */
++struct compat_xt_standard_target {
++	struct compat_xt_entry_target t;
++	compat_uint_t verdict;
++};
++
++/* see xt_check_entry_offsets */
+ int xt_compat_check_entry_offsets(const void *base,
+ 				  unsigned int target_offset,
+ 				  unsigned int next_offset)
+@@ -576,6 +583,10 @@ int xt_compat_check_entry_offsets(const
+ 	if (target_offset + t->u.target_size > next_offset)
+ 		return -EINVAL;
+ 
++	if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
++	    target_offset + sizeof(struct compat_xt_standard_target) != next_offset)
++		return -EINVAL;
++
+ 	return 0;
+ }
+ EXPORT_SYMBOL(xt_compat_check_entry_offsets);
+@@ -615,6 +626,10 @@ int xt_check_entry_offsets(const void *b
+ 	if (target_offset + t->u.target_size > next_offset)
+ 		return -EINVAL;
+ 
++	if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
++	    target_offset + sizeof(struct xt_standard_target) != next_offset)
++		return -EINVAL;
++
+ 	return 0;
+ }
+ EXPORT_SYMBOL(xt_check_entry_offsets);
diff --git a/debian/patches/bugfix/all/netfilter-x_tables-do-compat-validation-via-translat.patch b/debian/patches/bugfix/all/netfilter-x_tables-do-compat-validation-via-translat.patch
new file mode 100644
index 0000000..24e1b68
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-x_tables-do-compat-validation-via-translat.patch
@@ -0,0 +1,781 @@
+From: Florian Westphal <fw at strlen.de>
+Date: Fri, 1 Apr 2016 14:17:34 +0200
+Subject: netfilter: x_tables: do compat validation via translate_table
+Origin: https://git.kernel.org/linus/09d9686047dbbe1cf4faa558d3ecc4aae2046054
+
+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 at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+[bwh: Backported to 3.16: deleted code is a little different]
+---
+--- a/net/ipv4/netfilter/arp_tables.c
++++ b/net/ipv4/netfilter/arp_tables.c
+@@ -1224,19 +1224,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 ||
+@@ -1281,17 +1279,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:
+@@ -1341,7 +1328,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;
+ 
+@@ -1350,12 +1337,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);
+@@ -1364,9 +1345,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;
+@@ -1379,23 +1358,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)
+@@ -1412,51 +1374,24 @@ 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) {
+-		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;
++	memcpy(&repl, compatr, sizeof(*compatr));
++	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;
+@@ -1465,17 +1400,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
+@@ -1479,16 +1479,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 ||
+@@ -1540,17 +1538,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:
+@@ -1593,6 +1580,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;
+@@ -1602,41 +1590,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,
+@@ -1646,7 +1599,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;
+ 
+@@ -1655,12 +1608,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);
+@@ -1669,9 +1616,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;
+@@ -1684,23 +1629,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)
+@@ -1708,8 +1636,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;
+@@ -1718,51 +1646,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;
+@@ -1771,17 +1677,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
+@@ -1491,16 +1491,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 ||
+@@ -1553,17 +1551,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:
+@@ -1612,41 +1599,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,
+@@ -1657,7 +1609,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;
+ 
+@@ -1666,12 +1618,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);
+@@ -1680,9 +1626,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;
+@@ -1695,23 +1639,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)
+@@ -1719,60 +1646,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;
+@@ -1781,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_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;
diff --git a/debian/patches/bugfix/all/netfilter-x_tables-don-t-move-to-non-existent-next-r.patch b/debian/patches/bugfix/all/netfilter-x_tables-don-t-move-to-non-existent-next-r.patch
new file mode 100644
index 0000000..ed9dc85
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-x_tables-don-t-move-to-non-existent-next-r.patch
@@ -0,0 +1,100 @@
+From: Florian Westphal <fw at strlen.de>
+Date: Fri, 1 Apr 2016 14:17:21 +0200
+Subject: netfilter: x_tables: don't move to non-existent next rule
+Origin: https://git.kernel.org/linus/f24e230d257af1ad7476c6e81a8dc3127a74204e
+
+Ben Hawkes says:
+
+ In the mark_source_chains function (net/ipv4/netfilter/ip_tables.c) it
+ is possible for a user-supplied ipt_entry structure to have a large
+ next_offset field. This field is not bounds checked prior to writing a
+ counter value at the supplied offset.
+
+Base chains enforce absolute verdict.
+
+User defined chains are supposed to end with an unconditional return,
+xtables userspace adds them automatically.
+
+But if such return is missing we will move to non-existent next rule.
+
+Reported-by: Ben Hawkes <hawkes at google.com>
+Signed-off-by: Florian Westphal <fw at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+ net/ipv4/netfilter/arp_tables.c | 8 +++++---
+ net/ipv4/netfilter/ip_tables.c  | 4 ++++
+ net/ipv6/netfilter/ip6_tables.c | 4 ++++
+ 3 files changed, 13 insertions(+), 3 deletions(-)
+
+--- a/net/ipv4/netfilter/arp_tables.c
++++ b/net/ipv4/netfilter/arp_tables.c
+@@ -435,6 +435,8 @@ static int mark_source_chains(const stru
+ 				size = e->next_offset;
+ 				e = (struct arpt_entry *)
+ 					(entry0 + pos + size);
++				if (pos + size >= newinfo->size)
++					return 0;
+ 				e->counters.pcnt = pos;
+ 				pos += size;
+ 			} else {
+@@ -457,6 +459,8 @@ static int mark_source_chains(const stru
+ 				} else {
+ 					/* ... this is a fallthru */
+ 					newpos = pos + e->next_offset;
++					if (newpos >= newinfo->size)
++						return 0;
+ 				}
+ 				e = (struct arpt_entry *)
+ 					(entry0 + newpos);
+@@ -680,10 +684,8 @@ static int translate_table(struct xt_tab
+ 		}
+ 	}
+ 
+-	if (!mark_source_chains(newinfo, repl->valid_hooks, entry0)) {
+-		duprintf("Looping hook\n");
++	if (!mark_source_chains(newinfo, repl->valid_hooks, entry0))
+ 		return -ELOOP;
+-	}
+ 
+ 	/* Finally, each sanity check must pass */
+ 	i = 0;
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -516,6 +516,8 @@ mark_source_chains(const struct xt_table
+ 				size = e->next_offset;
+ 				e = (struct ipt_entry *)
+ 					(entry0 + pos + size);
++				if (pos + size >= newinfo->size)
++					return 0;
+ 				e->counters.pcnt = pos;
+ 				pos += size;
+ 			} else {
+@@ -537,6 +539,8 @@ mark_source_chains(const struct xt_table
+ 				} else {
+ 					/* ... this is a fallthru */
+ 					newpos = pos + e->next_offset;
++					if (newpos >= newinfo->size)
++						return 0;
+ 				}
+ 				e = (struct ipt_entry *)
+ 					(entry0 + newpos);
+--- a/net/ipv6/netfilter/ip6_tables.c
++++ b/net/ipv6/netfilter/ip6_tables.c
+@@ -526,6 +526,8 @@ mark_source_chains(const struct xt_table
+ 				size = e->next_offset;
+ 				e = (struct ip6t_entry *)
+ 					(entry0 + pos + size);
++				if (pos + size >= newinfo->size)
++					return 0;
+ 				e->counters.pcnt = pos;
+ 				pos += size;
+ 			} else {
+@@ -547,6 +549,8 @@ mark_source_chains(const struct xt_table
+ 				} else {
+ 					/* ... this is a fallthru */
+ 					newpos = pos + e->next_offset;
++					if (newpos >= newinfo->size)
++						return 0;
+ 				}
+ 				e = (struct ip6t_entry *)
+ 					(entry0 + newpos);
diff --git a/debian/patches/bugfix/all/netfilter-x_tables-don-t-reject-valid-target-size-on.patch b/debian/patches/bugfix/all/netfilter-x_tables-don-t-reject-valid-target-size-on.patch
new file mode 100644
index 0000000..1d2a14e
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-x_tables-don-t-reject-valid-target-size-on.patch
@@ -0,0 +1,54 @@
+From: Florian Westphal <fw at strlen.de>
+Date: Wed, 1 Jun 2016 02:04:44 +0200
+Subject: netfilter: x_tables: don't reject valid target size on some
+ architectures
+Origin: https://git.kernel.org/linus/7b7eba0f3515fca3296b8881d583f7c1042f5226
+
+Quoting John Stultz:
+  In updating a 32bit arm device from 4.6 to Linus' current HEAD, I
+  noticed I was having some trouble with networking, and realized that
+  /proc/net/ip_tables_names was suddenly empty.
+  Digging through the registration process, it seems we're catching on the:
+
+   if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
+       target_offset + sizeof(struct xt_standard_target) != next_offset)
+         return -EINVAL;
+
+  Where next_offset seems to be 4 bytes larger then the
+  offset + standard_target struct size.
+
+next_offset needs to be aligned via XT_ALIGN (so we can access all members
+of ip(6)t_entry struct).
+
+This problem didn't show up on i686 as it only needs 4-byte alignment for
+u64, but iptables userspace on other 32bit arches does insert extra padding.
+
+Reported-by: John Stultz <john.stultz at linaro.org>
+Tested-by: John Stultz <john.stultz at linaro.org>
+Fixes: 7ed2abddd20cf ("netfilter: x_tables: check standard target size too")
+Signed-off-by: Florian Westphal <fw at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+ net/netfilter/x_tables.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/net/netfilter/x_tables.c
++++ b/net/netfilter/x_tables.c
+@@ -628,7 +628,7 @@ int xt_compat_check_entry_offsets(const
+ 		return -EINVAL;
+ 
+ 	if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
+-	    target_offset + sizeof(struct compat_xt_standard_target) != next_offset)
++	    COMPAT_XT_ALIGN(target_offset + sizeof(struct compat_xt_standard_target)) != next_offset)
+ 		return -EINVAL;
+ 
+ 	/* compat_xt_entry match has less strict aligment requirements,
+@@ -710,7 +710,7 @@ int xt_check_entry_offsets(const void *b
+ 		return -EINVAL;
+ 
+ 	if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
+-	    target_offset + sizeof(struct xt_standard_target) != next_offset)
++	    XT_ALIGN(target_offset + sizeof(struct xt_standard_target)) != next_offset)
+ 		return -EINVAL;
+ 
+ 	return xt_check_entry_match(elems, base + target_offset,
diff --git a/debian/patches/bugfix/all/netfilter-x_tables-introduce-and-use-xt_copy_counter.patch b/debian/patches/bugfix/all/netfilter-x_tables-introduce-and-use-xt_copy_counter.patch
new file mode 100644
index 0000000..982785b
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-x_tables-introduce-and-use-xt_copy_counter.patch
@@ -0,0 +1,331 @@
+From: Florian Westphal <fw at strlen.de>
+Date: Fri, 1 Apr 2016 15:37:59 +0200
+Subject: netfilter: x_tables: introduce and use xt_copy_counters_from_user
+Origin: https://git.kernel.org/linus/d7591f0c41ce3e67600a982bab6989ef0f07b3ce
+
+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 at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.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
+@@ -1121,56 +1121,18 @@ static int do_add_counters(struct net *n
+ 	unsigned int i, curcpu;
+ 	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;
+ 	void *loc_cpu_entry;
+ 	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;
+@@ -1178,7 +1140,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
+@@ -1308,56 +1308,18 @@ do_add_counters(struct net *net, const v
+ 	unsigned int i, curcpu;
+ 	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;
+ 	void *loc_cpu_entry;
+ 	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;
+@@ -1365,7 +1327,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
+@@ -1318,56 +1318,17 @@ do_add_counters(struct net *net, const v
+ 	unsigned int i, curcpu;
+ 	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;
+ 	const void *loc_cpu_entry;
+ 	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;
+@@ -1376,7 +1337,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
+@@ -771,6 +771,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/debian/patches/bugfix/all/netfilter-x_tables-kill-check_entry-helper.patch b/debian/patches/bugfix/all/netfilter-x_tables-kill-check_entry-helper.patch
new file mode 100644
index 0000000..4dfba53
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-x_tables-kill-check_entry-helper.patch
@@ -0,0 +1,149 @@
+From: Florian Westphal <fw at strlen.de>
+Date: Fri, 1 Apr 2016 14:17:24 +0200
+Subject: netfilter: x_tables: kill check_entry helper
+Origin: https://git.kernel.org/linus/aa412ba225dd3bc36d404c28cdc3d674850d80d0
+
+Once we add more sanity testing to xt_check_entry_offsets it
+becomes relvant if we're expecting a 32bit 'config_compat' blob
+or a normal one.
+
+Since we already have a lot of similar-named functions (check_entry,
+compat_check_entry, find_and_check_entry, etc.) and the current
+incarnation is short just fold its contents into the callers.
+
+Signed-off-by: Florian Westphal <fw at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+ net/ipv4/netfilter/arp_tables.c | 19 ++++++++-----------
+ net/ipv4/netfilter/ip_tables.c  | 20 ++++++++------------
+ net/ipv6/netfilter/ip6_tables.c | 20 ++++++++------------
+ 3 files changed, 24 insertions(+), 35 deletions(-)
+
+--- a/net/ipv4/netfilter/arp_tables.c
++++ b/net/ipv4/netfilter/arp_tables.c
+@@ -490,14 +490,6 @@ static int mark_source_chains(const stru
+ 	return 1;
+ }
+ 
+-static inline int check_entry(const struct arpt_entry *e)
+-{
+-	if (!arp_checkentry(&e->arp))
+-		return -EINVAL;
+-
+-	return xt_check_entry_offsets(e, e->target_offset, e->next_offset);
+-}
+-
+ static inline int check_target(struct arpt_entry *e, const char *name)
+ {
+ 	struct xt_entry_target *t = arpt_get_target(e);
+@@ -587,7 +579,10 @@ static inline int check_entry_size_and_h
+ 		return -EINVAL;
+ 	}
+ 
+-	err = check_entry(e);
++	if (!arp_checkentry(&e->arp))
++		return -EINVAL;
++
++	err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
+ 	if (err)
+ 		return err;
+ 
+@@ -1246,8 +1241,10 @@ check_compat_entry_size_and_hooks(struct
+ 		return -EINVAL;
+ 	}
+ 
+-	/* For purposes of check_entry casting the compat entry is fine */
+-	ret = check_entry((struct arpt_entry *)e);
++	if (!arp_checkentry(&e->arp))
++		return -EINVAL;
++
++	ret = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
+ 	if (ret)
+ 		return ret;
+ 
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -584,15 +584,6 @@ static void cleanup_match(struct xt_entr
+ }
+ 
+ static int
+-check_entry(const struct ipt_entry *e)
+-{
+-	if (!ip_checkentry(&e->ip))
+-		return -EINVAL;
+-
+-	return xt_check_entry_offsets(e, e->target_offset, e->next_offset);
+-}
+-
+-static int
+ check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
+ {
+ 	const struct ipt_ip *ip = par->entryinfo;
+@@ -748,7 +739,10 @@ check_entry_size_and_hooks(struct ipt_en
+ 		return -EINVAL;
+ 	}
+ 
+-	err = check_entry(e);
++	if (!ip_checkentry(&e->ip))
++		return -EINVAL;
++
++	err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
+ 	if (err)
+ 		return err;
+ 
+@@ -1512,8 +1506,10 @@ check_compat_entry_size_and_hooks(struct
+ 		return -EINVAL;
+ 	}
+ 
+-	/* For purposes of check_entry casting the compat entry is fine */
+-	ret = check_entry((struct ipt_entry *)e);
++	if (!ip_checkentry(&e->ip))
++		return -EINVAL;
++
++	ret = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
+ 	if (ret)
+ 		return ret;
+ 
+--- a/net/ipv6/netfilter/ip6_tables.c
++++ b/net/ipv6/netfilter/ip6_tables.c
+@@ -593,15 +593,6 @@ static void cleanup_match(struct xt_entr
+ 	module_put(par.match->me);
+ }
+ 
+-static int
+-check_entry(const struct ip6t_entry *e)
+-{
+-	if (!ip6_checkentry(&e->ipv6))
+-		return -EINVAL;
+-
+-	return xt_check_entry_offsets(e, e->target_offset, e->next_offset);
+-}
+-
+ static int check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
+ {
+ 	const struct ip6t_ip6 *ipv6 = par->entryinfo;
+@@ -759,7 +750,10 @@ check_entry_size_and_hooks(struct ip6t_e
+ 		return -EINVAL;
+ 	}
+ 
+-	err = check_entry(e);
++	if (!ip6_checkentry(&e->ipv6))
++		return -EINVAL;
++
++	err = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
+ 	if (err)
+ 		return err;
+ 
+@@ -1524,8 +1518,10 @@ check_compat_entry_size_and_hooks(struct
+ 		return -EINVAL;
+ 	}
+ 
+-	/* For purposes of check_entry casting the compat entry is fine */
+-	ret = check_entry((struct ip6t_entry *)e);
++	if (!ip6_checkentry(&e->ipv6))
++		return -EINVAL;
++
++	ret = xt_check_entry_offsets(e, e->target_offset, e->next_offset);
+ 	if (ret)
+ 		return ret;
+ 
diff --git a/debian/patches/bugfix/all/netfilter-x_tables-validate-all-offsets-and-sizes-in.patch b/debian/patches/bugfix/all/netfilter-x_tables-validate-all-offsets-and-sizes-in.patch
new file mode 100644
index 0000000..fff80bd
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-x_tables-validate-all-offsets-and-sizes-in.patch
@@ -0,0 +1,137 @@
+From: Florian Westphal <fw at strlen.de>
+Date: Fri, 1 Apr 2016 14:17:29 +0200
+Subject: netfilter: x_tables: validate all offsets and sizes in a rule
+Origin: https://git.kernel.org/linus/13631bfc604161a9d69cd68991dff8603edd66f9
+
+Validate that all matches (if any) add up to the beginning of
+the target and that each match covers at least the base structure size.
+
+The compat path should be able to safely re-use the function
+as the structures only differ in alignment; added a
+BUILD_BUG_ON just in case we have an arch that adds padding as well.
+
+Signed-off-by: Florian Westphal <fw at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+ net/netfilter/x_tables.c | 81 +++++++++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 76 insertions(+), 5 deletions(-)
+
+--- a/net/netfilter/x_tables.c
++++ b/net/netfilter/x_tables.c
+@@ -435,6 +435,47 @@ int xt_check_match(struct xt_mtchk_param
+ }
+ EXPORT_SYMBOL_GPL(xt_check_match);
+ 
++/** xt_check_entry_match - check that matches end before start of target
++ *
++ * @match: beginning of xt_entry_match
++ * @target: beginning of this rules target (alleged end of matches)
++ * @alignment: alignment requirement of match structures
++ *
++ * Validates that all matches add up to the beginning of the target,
++ * and that each match covers at least the base structure size.
++ *
++ * Return: 0 on success, negative errno on failure.
++ */
++static int xt_check_entry_match(const char *match, const char *target,
++				const size_t alignment)
++{
++	const struct xt_entry_match *pos;
++	int length = target - match;
++
++	if (length == 0) /* no matches */
++		return 0;
++
++	pos = (struct xt_entry_match *)match;
++	do {
++		if ((unsigned long)pos % alignment)
++			return -EINVAL;
++
++		if (length < (int)sizeof(struct xt_entry_match))
++			return -EINVAL;
++
++		if (pos->u.match_size < sizeof(struct xt_entry_match))
++			return -EINVAL;
++
++		if (pos->u.match_size > length)
++			return -EINVAL;
++
++		length -= pos->u.match_size;
++		pos = ((void *)((char *)(pos) + (pos)->u.match_size));
++	} while (length > 0);
++
++	return 0;
++}
++
+ #ifdef CONFIG_COMPAT
+ int xt_compat_add_offset(u_int8_t af, unsigned int offset, int delta)
+ {
+@@ -590,7 +631,14 @@ int xt_compat_check_entry_offsets(const
+ 	    target_offset + sizeof(struct compat_xt_standard_target) != next_offset)
+ 		return -EINVAL;
+ 
+-	return 0;
++	/* compat_xt_entry match has less strict aligment requirements,
++	 * otherwise they are identical.  In case of padding differences
++	 * we need to add compat version of xt_check_entry_match.
++	 */
++	BUILD_BUG_ON(sizeof(struct compat_xt_entry_match) != sizeof(struct xt_entry_match));
++
++	return xt_check_entry_match(elems, base + target_offset,
++				    __alignof__(struct compat_xt_entry_match));
+ }
+ EXPORT_SYMBOL(xt_compat_check_entry_offsets);
+ #endif /* CONFIG_COMPAT */
+@@ -603,17 +651,39 @@ EXPORT_SYMBOL(xt_compat_check_entry_offs
+  * @target_offset: the arp/ip/ip6_t->target_offset
+  * @next_offset: the arp/ip/ip6_t->next_offset
+  *
+- * validates that target_offset and next_offset are sane.
+- * Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
++ * validates that target_offset and next_offset are sane and that all
++ * match sizes (if any) align with the target offset.
+  *
+  * This function does not validate the targets or matches themselves, it
+- * only tests that all the offsets and sizes are correct.
++ * only tests that all the offsets and sizes are correct, that all
++ * match structures are aligned, and that the last structure ends where
++ * the target structure begins.
++ *
++ * Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
+  *
+  * The arp/ip/ip6t_entry structure @base must have passed following tests:
+  * - it must point to a valid memory location
+  * - base to base + next_offset must be accessible, i.e. not exceed allocated
+  *   length.
+  *
++ * A well-formed entry looks like this:
++ *
++ * ip(6)t_entry   match [mtdata]  match [mtdata] target [tgdata] ip(6)t_entry
++ * e->elems[]-----'                              |               |
++ *                matchsize                      |               |
++ *                                matchsize      |               |
++ *                                               |               |
++ * target_offset---------------------------------'               |
++ * next_offset---------------------------------------------------'
++ *
++ * elems[]: flexible array member at end of ip(6)/arpt_entry struct.
++ *          This is where matches (if any) and the target reside.
++ * target_offset: beginning of target.
++ * next_offset: start of the next rule; also: size of this rule.
++ * Since targets have a minimum size, target_offset + minlen <= next_offset.
++ *
++ * Every match stores its size, sum of sizes must not exceed target_offset.
++ *
+  * Return: 0 on success, negative errno on failure.
+  */
+ int xt_check_entry_offsets(const void *base,
+@@ -643,7 +713,8 @@ int xt_check_entry_offsets(const void *b
+ 	    target_offset + sizeof(struct xt_standard_target) != next_offset)
+ 		return -EINVAL;
+ 
+-	return 0;
++	return xt_check_entry_match(elems, base + target_offset,
++				    __alignof__(struct xt_entry_match));
+ }
+ EXPORT_SYMBOL(xt_check_entry_offsets);
+ 
diff --git a/debian/patches/bugfix/all/netfilter-x_tables-validate-targets-of-jumps.patch b/debian/patches/bugfix/all/netfilter-x_tables-validate-targets-of-jumps.patch
new file mode 100644
index 0000000..3258c13
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-x_tables-validate-targets-of-jumps.patch
@@ -0,0 +1,127 @@
+From: Florian Westphal <fw at strlen.de>
+Date: Fri, 1 Apr 2016 14:17:22 +0200
+Subject: netfilter: x_tables: validate targets of jumps
+Origin: https://git.kernel.org/linus/36472341017529e2b12573093cc0f68719300997
+
+When we see a jump also check that the offset gets us to beginning of
+a rule (an ipt_entry).
+
+The extra overhead is negible, even with absurd cases.
+
+300k custom rules, 300k jumps to 'next' user chain:
+[ plus one jump from INPUT to first userchain ]:
+
+Before:
+real    0m24.874s
+user    0m7.532s
+sys     0m16.076s
+
+After:
+real    0m27.464s
+user    0m7.436s
+sys     0m18.840s
+
+Signed-off-by: Florian Westphal <fw at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
+---
+ net/ipv4/netfilter/arp_tables.c | 16 ++++++++++++++++
+ net/ipv4/netfilter/ip_tables.c  | 16 ++++++++++++++++
+ net/ipv6/netfilter/ip6_tables.c | 16 ++++++++++++++++
+ 3 files changed, 48 insertions(+)
+
+--- a/net/ipv4/netfilter/arp_tables.c
++++ b/net/ipv4/netfilter/arp_tables.c
+@@ -363,6 +363,18 @@ static inline bool unconditional(const s
+ 	       memcmp(&e->arp, &uncond, sizeof(uncond)) == 0;
+ }
+ 
++static bool find_jump_target(const struct xt_table_info *t,
++			     const struct arpt_entry *target)
++{
++	struct arpt_entry *iter;
++
++	xt_entry_foreach(iter, t->entries, t->size) {
++		 if (iter == target)
++			return true;
++	}
++	return false;
++}
++
+ /* Figures out from what hook each rule can be called: returns 0 if
+  * there are loops.  Puts hook bitmask in comefrom.
+  */
+@@ -456,6 +468,10 @@ static int mark_source_chains(const stru
+ 					/* This a jump; chase it. */
+ 					duprintf("Jump rule %u -> %u\n",
+ 						 pos, newpos);
++					e = (struct arpt_entry *)
++						(entry0 + newpos);
++					if (!find_jump_target(newinfo, e))
++						return 0;
+ 				} else {
+ 					/* ... this is a fallthru */
+ 					newpos = pos + e->next_offset;
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -439,6 +439,18 @@ ipt_do_table(struct sk_buff *skb,
+ #endif
+ }
+ 
++static bool find_jump_target(const struct xt_table_info *t,
++			     const struct ipt_entry *target)
++{
++	struct ipt_entry *iter;
++
++	xt_entry_foreach(iter, t->entries, t->size) {
++		 if (iter == target)
++			return true;
++	}
++	return false;
++}
++
+ /* Figures out from what hook each rule can be called: returns 0 if
+    there are loops.  Puts hook bitmask in comefrom. */
+ static int
+@@ -536,6 +548,10 @@ mark_source_chains(const struct xt_table
+ 					/* This a jump; chase it. */
+ 					duprintf("Jump rule %u -> %u\n",
+ 						 pos, newpos);
++					e = (struct ipt_entry *)
++						(entry0 + newpos);
++					if (!find_jump_target(newinfo, e))
++						return 0;
+ 				} else {
+ 					/* ... this is a fallthru */
+ 					newpos = pos + e->next_offset;
+--- a/net/ipv6/netfilter/ip6_tables.c
++++ b/net/ipv6/netfilter/ip6_tables.c
+@@ -449,6 +449,18 @@ ip6t_do_table(struct sk_buff *skb,
+ #endif
+ }
+ 
++static bool find_jump_target(const struct xt_table_info *t,
++			     const struct ip6t_entry *target)
++{
++	struct ip6t_entry *iter;
++
++	xt_entry_foreach(iter, t->entries, t->size) {
++		 if (iter == target)
++			return true;
++	}
++	return false;
++}
++
+ /* Figures out from what hook each rule can be called: returns 0 if
+    there are loops.  Puts hook bitmask in comefrom. */
+ static int
+@@ -546,6 +558,10 @@ mark_source_chains(const struct xt_table
+ 					/* This a jump; chase it. */
+ 					duprintf("Jump rule %u -> %u\n",
+ 						 pos, newpos);
++					e = (struct ip6t_entry *)
++						(entry0 + newpos);
++					if (!find_jump_target(newinfo, e))
++						return 0;
+ 				} else {
+ 					/* ... this is a fallthru */
+ 					newpos = pos + e->next_offset;
diff --git a/debian/patches/bugfix/all/netfilter-x_tables-xt_compat_match_from_user-doesn-t.patch b/debian/patches/bugfix/all/netfilter-x_tables-xt_compat_match_from_user-doesn-t.patch
new file mode 100644
index 0000000..388627d
--- /dev/null
+++ b/debian/patches/bugfix/all/netfilter-x_tables-xt_compat_match_from_user-doesn-t.patch
@@ -0,0 +1,234 @@
+From: Florian Westphal <fw at 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
+Origin: https://git.kernel.org/linus/0188346f21e6546498c2a0f84888797ad4063fc5
+
+Always returned 0.
+
+Signed-off-by: Florian Westphal <fw at strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.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
+@@ -1564,7 +1564,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)
+@@ -1573,10 +1573,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));
+@@ -1585,11 +1584,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;
+@@ -1602,7 +1599,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
+@@ -1718,16 +1714,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);
+ 
diff --git a/debian/patches/series b/debian/patches/series
index d64a5fa..38d25bb 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -703,3 +703,19 @@ bugfix/all/net-fix-a-kernel-infoleak-in-x25-module.patch
 bugfix/all/ib-security-restrict-use-of-the-write-interface.patch
 bugfix/all/ppp-take-reference-on-channels-netns.patch
 bugfix/all/keys-potential-uninitialized-variable.patch
+bugfix/all/netfilter-x_tables-don-t-move-to-non-existent-next-r.patch
+bugfix/all/netfilter-x_tables-validate-targets-of-jumps.patch
+bugfix/all/netfilter-x_tables-add-and-use-xt_check_entry_offset.patch
+bugfix/all/netfilter-x_tables-kill-check_entry-helper.patch
+bugfix/all/netfilter-x_tables-assert-minimum-target-size.patch
+bugfix/all/netfilter-x_tables-add-compat-version-of-xt_check_en.patch
+bugfix/all/netfilter-x_tables-check-standard-target-size-too.patch
+bugfix/all/netfilter-x_tables-check-for-bogus-target-offset.patch
+bugfix/all/netfilter-x_tables-validate-all-offsets-and-sizes-in.patch
+bugfix/all/netfilter-x_tables-don-t-reject-valid-target-size-on.patch
+bugfix/all/netfilter-arp_tables-simplify-translate_compat_table.patch
+bugfix/all/netfilter-ip_tables-simplify-translate_compat_table-.patch
+bugfix/all/netfilter-ip6_tables-simplify-translate_compat_table.patch
+bugfix/all/netfilter-x_tables-xt_compat_match_from_user-doesn-t.patch
+bugfix/all/netfilter-x_tables-do-compat-validation-via-translat.patch
+bugfix/all/netfilter-x_tables-introduce-and-use-xt_copy_counter.patch

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/kernel/linux.git



More information about the Kernel-svn-changes mailing list