--- /dev/null
+From 7d35812c3214afa5b37a675113555259cfd67b98 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Apr 2016 14:17:23 +0200
+Subject: netfilter: x_tables: add and use xt_check_entry_offsets
+
+From: Florian Westphal <fw@strlen.de>
+
+commit 7d35812c3214afa5b37a675113555259cfd67b98 upstream.
+
+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@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ include/linux/netfilter/x_tables.h | 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
+@@ -496,19 +496,10 @@ next:
+
+ 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
+@@ -590,20 +590,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
+@@ -602,20 +602,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
+@@ -540,6 +540,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)
+ {
--- /dev/null
+From fc1221b3a163d1386d1052184202d5dc50d302d1 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Apr 2016 14:17:26 +0200
+Subject: netfilter: x_tables: add compat version of xt_check_entry_offsets
+
+From: Florian Westphal <fw@strlen.de>
+
+commit fc1221b3a163d1386d1052184202d5dc50d302d1 upstream.
+
+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@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ include/linux/netfilter/x_tables.h | 3 +++
+ net/ipv4/netfilter/arp_tables.c | 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
+@@ -492,6 +492,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
+@@ -1253,7 +1253,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
+@@ -1512,7 +1512,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
+@@ -1524,7 +1524,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
+@@ -538,6 +538,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 */
+
+ /**
+@@ -548,6 +569,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
--- /dev/null
+From a08e4e190b866579896c09af59b3bdca821da2cd Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Apr 2016 14:17:25 +0200
+Subject: netfilter: x_tables: assert minimum target size
+
+From: Florian Westphal <fw@strlen.de>
+
+commit a08e4e190b866579896c09af59b3bdca821da2cd upstream.
+
+The target size includes the size of the xt_entry_target struct.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/netfilter/x_tables.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/net/netfilter/x_tables.c
++++ b/net/netfilter/x_tables.c
+@@ -567,6 +567,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;
+
--- /dev/null
+From ce683e5f9d045e5d67d1312a42b359cb2ab2a13c Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Apr 2016 14:17:28 +0200
+Subject: netfilter: x_tables: check for bogus target offset
+
+From: Florian Westphal <fw@strlen.de>
+
+commit ce683e5f9d045e5d67d1312a42b359cb2ab2a13c upstream.
+
+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@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ include/linux/netfilter/x_tables.h | 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);
+
+@@ -492,7 +492,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
+@@ -592,7 +592,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;
+
+@@ -1253,7 +1254,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
+@@ -754,7 +754,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;
+
+@@ -1512,7 +1513,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
+@@ -766,7 +766,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;
+
+@@ -1524,7 +1525,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
+@@ -545,14 +545,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;
+
+@@ -576,12 +579,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
+@@ -590,12 +597,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;
+
--- /dev/null
+From 7ed2abddd20cf8f6bd27f65bd218f26fa5bf7f44 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Apr 2016 14:17:27 +0200
+Subject: netfilter: x_tables: check standard target size too
+
+From: Florian Westphal <fw@strlen.de>
+
+commit 7ed2abddd20cf8f6bd27f65bd218f26fa5bf7f44 upstream.
+
+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@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/netfilter/x_tables.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/net/netfilter/x_tables.c
++++ b/net/netfilter/x_tables.c
+@@ -539,6 +539,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)
+@@ -556,6 +563,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);
+@@ -595,6 +606,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);
--- /dev/null
+From f24e230d257af1ad7476c6e81a8dc3127a74204e Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Apr 2016 14:17:21 +0200
+Subject: netfilter: x_tables: don't move to non-existent next rule
+
+From: Florian Westphal <fw@strlen.de>
+
+commit f24e230d257af1ad7476c6e81a8dc3127a74204e upstream.
+
+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@google.com>
+Signed-off-by: Florian Westphal <fw@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/ipv4/netfilter/arp_tables.c | 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
+@@ -439,6 +439,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 {
+@@ -461,6 +463,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);
+@@ -691,10 +695,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
+@@ -520,6 +520,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 {
+@@ -541,6 +543,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
+@@ -532,6 +532,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 {
+@@ -553,6 +555,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);
--- /dev/null
+From aa412ba225dd3bc36d404c28cdc3d674850d80d0 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Apr 2016 14:17:24 +0200
+Subject: netfilter: x_tables: kill check_entry helper
+
+From: Florian Westphal <fw@strlen.de>
+
+commit aa412ba225dd3bc36d404c28cdc3d674850d80d0 upstream.
+
+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@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/ipv4/netfilter/arp_tables.c | 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
+@@ -494,14 +494,6 @@ next:
+ 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);
+@@ -597,7 +589,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;
+
+@@ -1255,8 +1250,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
+@@ -588,15 +588,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;
+@@ -760,7 +751,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;
+
+@@ -1515,8 +1509,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
+@@ -599,15 +599,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;
+@@ -772,7 +763,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;
+
+@@ -1527,8 +1521,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;
+
--- /dev/null
+From 13631bfc604161a9d69cd68991dff8603edd66f9 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Apr 2016 14:17:29 +0200
+Subject: netfilter: x_tables: validate all offsets and sizes in a rule
+
+From: Florian Westphal <fw@strlen.de>
+
+commit 13631bfc604161a9d69cd68991dff8603edd66f9 upstream.
+
+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@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.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
+@@ -415,6 +415,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)
+ {
+@@ -570,7 +611,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 */
+@@ -583,17 +631,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,
+@@ -623,7 +693,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);
+
--- /dev/null
+From 36472341017529e2b12573093cc0f68719300997 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Fri, 1 Apr 2016 14:17:22 +0200
+Subject: netfilter: x_tables: validate targets of jumps
+
+From: Florian Westphal <fw@strlen.de>
+
+commit 36472341017529e2b12573093cc0f68719300997 upstream.
+
+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@strlen.de>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/ipv4/netfilter/arp_tables.c | 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
+@@ -367,6 +367,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.
+ */
+@@ -460,6 +472,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
+@@ -443,6 +443,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
+@@ -540,6 +552,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
+@@ -455,6 +455,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
+@@ -552,6 +564,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;
netfilter-x_tables-fix-unconditional-helper.patch
crypto-qat-fix-adf_ctl_drv.c-undefined-reference-to-adf_init_pf_wq.patch
drm-core-do-not-preserve-framebuffer-on-rmfb-v4.patch
+netfilter-x_tables-don-t-move-to-non-existent-next-rule.patch
+netfilter-x_tables-validate-targets-of-jumps.patch
+netfilter-x_tables-add-and-use-xt_check_entry_offsets.patch
+netfilter-x_tables-kill-check_entry-helper.patch
+netfilter-x_tables-assert-minimum-target-size.patch
+netfilter-x_tables-add-compat-version-of-xt_check_entry_offsets.patch
+netfilter-x_tables-check-standard-target-size-too.patch
+netfilter-x_tables-check-for-bogus-target-offset.patch
+netfilter-x_tables-validate-all-offsets-and-sizes-in-a-rule.patch