]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.14-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 22 Jun 2016 22:25:43 +0000 (15:25 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 22 Jun 2016 22:25:43 +0000 (15:25 -0700)
added patches:
netfilter-x_tables-add-and-use-xt_check_entry_offsets.patch
netfilter-x_tables-add-compat-version-of-xt_check_entry_offsets.patch
netfilter-x_tables-assert-minimum-target-size.patch
netfilter-x_tables-check-for-bogus-target-offset.patch
netfilter-x_tables-check-standard-target-size-too.patch
netfilter-x_tables-don-t-reject-valid-target-size-on-some-architectures.patch
netfilter-x_tables-kill-check_entry-helper.patch
netfilter-x_tables-validate-all-offsets-and-sizes-in-a-rule.patch
netfilter-x_tables-validate-e-target_offset-early.patch

13 files changed:
queue-3.14/netfilter-x_tables-add-and-use-xt_check_entry_offsets.patch [new file with mode: 0644]
queue-3.14/netfilter-x_tables-add-compat-version-of-xt_check_entry_offsets.patch [new file with mode: 0644]
queue-3.14/netfilter-x_tables-assert-minimum-target-size.patch [new file with mode: 0644]
queue-3.14/netfilter-x_tables-check-for-bogus-target-offset.patch [new file with mode: 0644]
queue-3.14/netfilter-x_tables-check-standard-target-size-too.patch [new file with mode: 0644]
queue-3.14/netfilter-x_tables-don-t-move-to-non-existent-next-rule.patch
queue-3.14/netfilter-x_tables-don-t-reject-valid-target-size-on-some-architectures.patch [new file with mode: 0644]
queue-3.14/netfilter-x_tables-fix-unconditional-helper.patch
queue-3.14/netfilter-x_tables-kill-check_entry-helper.patch [new file with mode: 0644]
queue-3.14/netfilter-x_tables-make-sure-e-next_offset-covers-remaining-blob-size.patch
queue-3.14/netfilter-x_tables-validate-all-offsets-and-sizes-in-a-rule.patch [new file with mode: 0644]
queue-3.14/netfilter-x_tables-validate-e-target_offset-early.patch [new file with mode: 0644]
queue-3.14/series

diff --git a/queue-3.14/netfilter-x_tables-add-and-use-xt_check_entry_offsets.patch b/queue-3.14/netfilter-x_tables-add-and-use-xt_check_entry_offsets.patch
new file mode 100644 (file)
index 0000000..dbf0470
--- /dev/null
@@ -0,0 +1,157 @@
+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
+@@ -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/queue-3.14/netfilter-x_tables-add-compat-version-of-xt_check_entry_offsets.patch b/queue-3.14/netfilter-x_tables-add-compat-version-of-xt_check_entry_offsets.patch
new file mode 100644 (file)
index 0000000..5ba69c1
--- /dev/null
@@ -0,0 +1,111 @@
+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
+@@ -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/queue-3.14/netfilter-x_tables-assert-minimum-target-size.patch b/queue-3.14/netfilter-x_tables-assert-minimum-target-size.patch
new file mode 100644 (file)
index 0000000..88d99c9
--- /dev/null
@@ -0,0 +1,31 @@
+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
+@@ -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/queue-3.14/netfilter-x_tables-check-for-bogus-target-offset.patch b/queue-3.14/netfilter-x_tables-check-for-bogus-target-offset.patch
new file mode 100644 (file)
index 0000000..c1b0d99
--- /dev/null
@@ -0,0 +1,170 @@
+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);
+@@ -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/queue-3.14/netfilter-x_tables-check-standard-target-size-too.patch b/queue-3.14/netfilter-x_tables-check-standard-target-size-too.patch
new file mode 100644 (file)
index 0000000..7116a47
--- /dev/null
@@ -0,0 +1,66 @@
+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
+@@ -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);
index d19e00f2e44f3e5412c7c48aa3a4c16cbc23ed56..1e11625c96d082b9542c87ef17cfb53807fc4b8e 100644 (file)
@@ -52,7 +52,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
                                }
                                e = (struct arpt_entry *)
                                        (entry0 + newpos);
-@@ -681,10 +685,8 @@ static int translate_table(struct xt_tab
+@@ -680,10 +684,8 @@ static int translate_table(struct xt_tab
                }
        }
  
diff --git a/queue-3.14/netfilter-x_tables-don-t-reject-valid-target-size-on-some-architectures.patch b/queue-3.14/netfilter-x_tables-don-t-reject-valid-target-size-on-some-architectures.patch
new file mode 100644 (file)
index 0000000..39fd676
--- /dev/null
@@ -0,0 +1,59 @@
+From 7b7eba0f3515fca3296b8881d583f7c1042f5226 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Wed, 1 Jun 2016 02:04:44 +0200
+Subject: netfilter: x_tables: don't reject valid target size on some architectures
+
+From: Florian Westphal <fw@strlen.de>
+
+commit 7b7eba0f3515fca3296b8881d583f7c1042f5226 upstream.
+
+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@linaro.org>
+Tested-by: John Stultz <john.stultz@linaro.org>
+Fixes: 7ed2abddd20cf ("netfilter: x_tables: check standard target size too")
+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 |    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,
index dd100a59d7b8773a59a9f8e7713192431e4e119a..48658de35eb65adb20034da318407d79195bf778 100644 (file)
@@ -73,7 +73,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
                                unsigned int oldpos, size;
  
                                if ((strcmp(t->target.u.user.name,
-@@ -547,7 +547,7 @@ static bool check_underflow(const struct
+@@ -541,7 +541,7 @@ static bool check_underflow(const struct
        const struct xt_entry_target *t;
        unsigned int verdict;
  
@@ -82,7 +82,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
                return false;
        t = arpt_get_target_c(e);
        if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
-@@ -589,9 +589,9 @@ static inline int check_entry_size_and_h
+@@ -588,9 +588,9 @@ static inline int check_entry_size_and_h
                        newinfo->hook_entry[h] = hook_entries[h];
                if ((unsigned char *)e - base == underflows[h]) {
                        if (!check_underflow(e)) {
@@ -140,7 +140,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
                                unsigned int oldpos, size;
  
                                if ((strcmp(t->target.u.user.name,
-@@ -709,7 +708,7 @@ static bool check_underflow(const struct
+@@ -703,7 +702,7 @@ static bool check_underflow(const struct
        const struct xt_entry_target *t;
        unsigned int verdict;
  
@@ -149,7 +149,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
                return false;
        t = ipt_get_target_c(e);
        if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
-@@ -752,9 +751,9 @@ check_entry_size_and_hooks(struct ipt_en
+@@ -751,9 +750,9 @@ check_entry_size_and_hooks(struct ipt_en
                        newinfo->hook_entry[h] = hook_entries[h];
                if ((unsigned char *)e - base == underflows[h]) {
                        if (!check_underflow(e)) {
@@ -207,7 +207,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
                                unsigned int oldpos, size;
  
                                if ((strcmp(t->target.u.user.name,
-@@ -720,7 +719,7 @@ static bool check_underflow(const struct
+@@ -714,7 +713,7 @@ static bool check_underflow(const struct
        const struct xt_entry_target *t;
        unsigned int verdict;
  
@@ -216,7 +216,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
                return false;
        t = ip6t_get_target_c(e);
        if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
-@@ -763,9 +762,9 @@ check_entry_size_and_hooks(struct ip6t_e
+@@ -762,9 +761,9 @@ check_entry_size_and_hooks(struct ip6t_e
                        newinfo->hook_entry[h] = hook_entries[h];
                if ((unsigned char *)e - base == underflows[h]) {
                        if (!check_underflow(e)) {
diff --git a/queue-3.14/netfilter-x_tables-kill-check_entry-helper.patch b/queue-3.14/netfilter-x_tables-kill-check_entry-helper.patch
new file mode 100644 (file)
index 0000000..5fbf52e
--- /dev/null
@@ -0,0 +1,155 @@
+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
+@@ -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;
index 1a8614ce0dcb4417cdc0a2f176c33d9a8443a781..482fc170286e26ecd6e6a8b833572dd7ac1fcf2d 100644 (file)
@@ -21,8 +21,8 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 
 --- a/net/ipv4/netfilter/arp_tables.c
 +++ b/net/ipv4/netfilter/arp_tables.c
-@@ -568,7 +568,8 @@ static inline int check_entry_size_and_h
-       unsigned int h;
+@@ -563,7 +563,8 @@ static inline int check_entry_size_and_h
+       int err;
  
        if ((unsigned long)e % __alignof__(struct arpt_entry) != 0 ||
 -          (unsigned char *)e + sizeof(struct arpt_entry) >= limit) {
@@ -31,7 +31,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
                duprintf("Bad offset %p\n", e);
                return -EINVAL;
        }
-@@ -1224,7 +1225,8 @@ check_compat_entry_size_and_hooks(struct
+@@ -1223,7 +1224,8 @@ check_compat_entry_size_and_hooks(struct
  
        duprintf("check_compat_entry_size_and_hooks %p\n", e);
        if ((unsigned long)e % __alignof__(struct compat_arpt_entry) != 0 ||
@@ -43,8 +43,8 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
        }
 --- a/net/ipv4/netfilter/ip_tables.c
 +++ b/net/ipv4/netfilter/ip_tables.c
-@@ -731,7 +731,8 @@ check_entry_size_and_hooks(struct ipt_en
-       unsigned int h;
+@@ -726,7 +726,8 @@ check_entry_size_and_hooks(struct ipt_en
+       int err;
  
        if ((unsigned long)e % __alignof__(struct ipt_entry) != 0 ||
 -          (unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
@@ -53,7 +53,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
                duprintf("Bad offset %p\n", e);
                return -EINVAL;
        }
-@@ -1490,7 +1491,8 @@ check_compat_entry_size_and_hooks(struct
+@@ -1489,7 +1490,8 @@ check_compat_entry_size_and_hooks(struct
  
        duprintf("check_compat_entry_size_and_hooks %p\n", e);
        if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 ||
@@ -65,8 +65,8 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
        }
 --- a/net/ipv6/netfilter/ip6_tables.c
 +++ b/net/ipv6/netfilter/ip6_tables.c
-@@ -742,7 +742,8 @@ check_entry_size_and_hooks(struct ip6t_e
-       unsigned int h;
+@@ -737,7 +737,8 @@ check_entry_size_and_hooks(struct ip6t_e
+       int err;
  
        if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0 ||
 -          (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
@@ -75,7 +75,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
                duprintf("Bad offset %p\n", e);
                return -EINVAL;
        }
-@@ -1502,7 +1503,8 @@ check_compat_entry_size_and_hooks(struct
+@@ -1501,7 +1502,8 @@ check_compat_entry_size_and_hooks(struct
  
        duprintf("check_compat_entry_size_and_hooks %p\n", e);
        if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 ||
diff --git a/queue-3.14/netfilter-x_tables-validate-all-offsets-and-sizes-in-a-rule.patch b/queue-3.14/netfilter-x_tables-validate-all-offsets-and-sizes-in-a-rule.patch
new file mode 100644 (file)
index 0000000..ec86705
--- /dev/null
@@ -0,0 +1,143 @@
+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
+@@ -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/queue-3.14/netfilter-x_tables-validate-e-target_offset-early.patch b/queue-3.14/netfilter-x_tables-validate-e-target_offset-early.patch
new file mode 100644 (file)
index 0000000..680e9ab
--- /dev/null
@@ -0,0 +1,197 @@
+From bdf533de6968e9686df777dc178486f600c6e617 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Tue, 22 Mar 2016 18:02:49 +0100
+Subject: netfilter: x_tables: validate e->target_offset early
+
+From: Florian Westphal <fw@strlen.de>
+
+commit bdf533de6968e9686df777dc178486f600c6e617 upstream.
+
+We should check that e->target_offset is sane before
+mark_source_chains gets called since it will fetch the target entry
+for loop detection.
+
+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 |   17 ++++++++---------
+ net/ipv4/netfilter/ip_tables.c  |   17 ++++++++---------
+ net/ipv6/netfilter/ip6_tables.c |   17 ++++++++---------
+ 3 files changed, 24 insertions(+), 27 deletions(-)
+
+--- a/net/ipv4/netfilter/arp_tables.c
++++ b/net/ipv4/netfilter/arp_tables.c
+@@ -470,14 +470,12 @@ static int mark_source_chains(const stru
+       return 1;
+ }
+-static inline int check_entry(const struct arpt_entry *e, const char *name)
++static inline int check_entry(const struct arpt_entry *e)
+ {
+       const struct xt_entry_target *t;
+-      if (!arp_checkentry(&e->arp)) {
+-              duprintf("arp_tables: arp check failed %p %s.\n", e, name);
++      if (!arp_checkentry(&e->arp))
+               return -EINVAL;
+-      }
+       if (e->target_offset + sizeof(struct xt_entry_target) > e->next_offset)
+               return -EINVAL;
+@@ -518,10 +516,6 @@ find_check_entry(struct arpt_entry *e, c
+       struct xt_target *target;
+       int ret;
+-      ret = check_entry(e, name);
+-      if (ret)
+-              return ret;
+-
+       t = arpt_get_target(e);
+       target = xt_request_find_target(NFPROTO_ARP, t->u.user.name,
+                                       t->u.user.revision);
+@@ -566,6 +560,7 @@ static inline int check_entry_size_and_h
+                                            unsigned int valid_hooks)
+ {
+       unsigned int h;
++      int err;
+       if ((unsigned long)e % __alignof__(struct arpt_entry) != 0 ||
+           (unsigned char *)e + sizeof(struct arpt_entry) >= limit) {
+@@ -580,6 +575,10 @@ static inline int check_entry_size_and_h
+               return -EINVAL;
+       }
++      err = check_entry(e);
++      if (err)
++              return err;
++
+       /* Check hooks & underflows */
+       for (h = 0; h < NF_ARP_NUMHOOKS; h++) {
+               if (!(valid_hooks & (1 << h)))
+@@ -1237,7 +1236,7 @@ check_compat_entry_size_and_hooks(struct
+       }
+       /* For purposes of check_entry casting the compat entry is fine */
+-      ret = check_entry((struct arpt_entry *)e, name);
++      ret = check_entry((struct arpt_entry *)e);
+       if (ret)
+               return ret;
+--- a/net/ipv4/netfilter/ip_tables.c
++++ b/net/ipv4/netfilter/ip_tables.c
+@@ -565,14 +565,12 @@ static void cleanup_match(struct xt_entr
+ }
+ static int
+-check_entry(const struct ipt_entry *e, const char *name)
++check_entry(const struct ipt_entry *e)
+ {
+       const struct xt_entry_target *t;
+-      if (!ip_checkentry(&e->ip)) {
+-              duprintf("ip check failed %p %s.\n", e, name);
++      if (!ip_checkentry(&e->ip))
+               return -EINVAL;
+-      }
+       if (e->target_offset + sizeof(struct xt_entry_target) >
+           e->next_offset)
+@@ -662,10 +660,6 @@ find_check_entry(struct ipt_entry *e, st
+       struct xt_mtchk_param mtpar;
+       struct xt_entry_match *ematch;
+-      ret = check_entry(e, name);
+-      if (ret)
+-              return ret;
+-
+       j = 0;
+       mtpar.net       = net;
+       mtpar.table     = name;
+@@ -729,6 +723,7 @@ check_entry_size_and_hooks(struct ipt_en
+                          unsigned int valid_hooks)
+ {
+       unsigned int h;
++      int err;
+       if ((unsigned long)e % __alignof__(struct ipt_entry) != 0 ||
+           (unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
+@@ -743,6 +738,10 @@ check_entry_size_and_hooks(struct ipt_en
+               return -EINVAL;
+       }
++      err = check_entry(e);
++      if (err)
++              return err;
++
+       /* Check hooks & underflows */
+       for (h = 0; h < NF_INET_NUMHOOKS; h++) {
+               if (!(valid_hooks & (1 << h)))
+@@ -1503,7 +1502,7 @@ check_compat_entry_size_and_hooks(struct
+       }
+       /* For purposes of check_entry casting the compat entry is fine */
+-      ret = check_entry((struct ipt_entry *)e, name);
++      ret = check_entry((struct ipt_entry *)e);
+       if (ret)
+               return ret;
+--- a/net/ipv6/netfilter/ip6_tables.c
++++ b/net/ipv6/netfilter/ip6_tables.c
+@@ -575,14 +575,12 @@ static void cleanup_match(struct xt_entr
+ }
+ static int
+-check_entry(const struct ip6t_entry *e, const char *name)
++check_entry(const struct ip6t_entry *e)
+ {
+       const struct xt_entry_target *t;
+-      if (!ip6_checkentry(&e->ipv6)) {
+-              duprintf("ip_tables: ip check failed %p %s.\n", e, name);
++      if (!ip6_checkentry(&e->ipv6))
+               return -EINVAL;
+-      }
+       if (e->target_offset + sizeof(struct xt_entry_target) >
+           e->next_offset)
+@@ -673,10 +671,6 @@ find_check_entry(struct ip6t_entry *e, s
+       struct xt_mtchk_param mtpar;
+       struct xt_entry_match *ematch;
+-      ret = check_entry(e, name);
+-      if (ret)
+-              return ret;
+-
+       j = 0;
+       mtpar.net       = net;
+       mtpar.table     = name;
+@@ -740,6 +734,7 @@ check_entry_size_and_hooks(struct ip6t_e
+                          unsigned int valid_hooks)
+ {
+       unsigned int h;
++      int err;
+       if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0 ||
+           (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
+@@ -754,6 +749,10 @@ check_entry_size_and_hooks(struct ip6t_e
+               return -EINVAL;
+       }
++      err = check_entry(e);
++      if (err)
++              return err;
++
+       /* Check hooks & underflows */
+       for (h = 0; h < NF_INET_NUMHOOKS; h++) {
+               if (!(valid_hooks & (1 << h)))
+@@ -1515,7 +1514,7 @@ check_compat_entry_size_and_hooks(struct
+       }
+       /* For purposes of check_entry casting the compat entry is fine */
+-      ret = check_entry((struct ip6t_entry *)e, name);
++      ret = check_entry((struct ip6t_entry *)e);
+       if (ret)
+               return ret;
index 39d568b00e6ed9a38eca81b2bf469d42aadc101a..49eb260268805005228a6357febe9410afdbabe6 100644 (file)
@@ -12,9 +12,18 @@ wext-fix-32-bit-iwpriv-compatibility-issue-with-64-bit-kernel.patch
 fix-d_walk-non-delayed-__d_free-race.patch
 mips-fix-64k-page-support-for-32-bit-kernels.patch
 powerpc-pseries-eeh-handle-rtas-delay-requests-in-configure_bridge.patch
+netfilter-x_tables-validate-e-target_offset-early.patch
 netfilter-x_tables-make-sure-e-next_offset-covers-remaining-blob-size.patch
 netfilter-x_tables-fix-unconditional-helper.patch
 xfs-fix-up-backport-error-in-fs-xfs-xfs_inode.c.patch
 pipe-limit-the-per-user-amount-of-pages-allocated-in-pipes.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
+netfilter-x_tables-don-t-reject-valid-target-size-on-some-architectures.patch