]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 22 Jun 2016 21:18:50 +0000 (14:18 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 22 Jun 2016 21:18:50 +0000 (14:18 -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-move-to-non-existent-next-rule.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-targets-of-jumps.patch

queue-4.4/netfilter-x_tables-add-and-use-xt_check_entry_offsets.patch [new file with mode: 0644]
queue-4.4/netfilter-x_tables-add-compat-version-of-xt_check_entry_offsets.patch [new file with mode: 0644]
queue-4.4/netfilter-x_tables-assert-minimum-target-size.patch [new file with mode: 0644]
queue-4.4/netfilter-x_tables-check-for-bogus-target-offset.patch [new file with mode: 0644]
queue-4.4/netfilter-x_tables-check-standard-target-size-too.patch [new file with mode: 0644]
queue-4.4/netfilter-x_tables-don-t-move-to-non-existent-next-rule.patch [new file with mode: 0644]
queue-4.4/netfilter-x_tables-kill-check_entry-helper.patch [new file with mode: 0644]
queue-4.4/netfilter-x_tables-validate-all-offsets-and-sizes-in-a-rule.patch [new file with mode: 0644]
queue-4.4/netfilter-x_tables-validate-targets-of-jumps.patch [new file with mode: 0644]
queue-4.4/series

diff --git a/queue-4.4/netfilter-x_tables-add-and-use-xt_check_entry_offsets.patch b/queue-4.4/netfilter-x_tables-add-and-use-xt_check_entry_offsets.patch
new file mode 100644 (file)
index 0000000..7623fcc
--- /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
+@@ -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)
+ {
diff --git a/queue-4.4/netfilter-x_tables-add-compat-version-of-xt_check_entry_offsets.patch b/queue-4.4/netfilter-x_tables-add-compat-version-of-xt_check_entry_offsets.patch
new file mode 100644 (file)
index 0000000..483c7c0
--- /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
+@@ -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
diff --git a/queue-4.4/netfilter-x_tables-assert-minimum-target-size.patch b/queue-4.4/netfilter-x_tables-assert-minimum-target-size.patch
new file mode 100644 (file)
index 0000000..ce12400
--- /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
+@@ -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;
diff --git a/queue-4.4/netfilter-x_tables-check-for-bogus-target-offset.patch b/queue-4.4/netfilter-x_tables-check-for-bogus-target-offset.patch
new file mode 100644 (file)
index 0000000..c0cda4b
--- /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);
+@@ -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;
diff --git a/queue-4.4/netfilter-x_tables-check-standard-target-size-too.patch b/queue-4.4/netfilter-x_tables-check-standard-target-size-too.patch
new file mode 100644 (file)
index 0000000..cbdef5b
--- /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
+@@ -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);
diff --git a/queue-4.4/netfilter-x_tables-don-t-move-to-non-existent-next-rule.patch b/queue-4.4/netfilter-x_tables-don-t-move-to-non-existent-next-rule.patch
new file mode 100644 (file)
index 0000000..3183c1d
--- /dev/null
@@ -0,0 +1,106 @@
+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);
diff --git a/queue-4.4/netfilter-x_tables-kill-check_entry-helper.patch b/queue-4.4/netfilter-x_tables-kill-check_entry-helper.patch
new file mode 100644 (file)
index 0000000..6e3f44e
--- /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
+@@ -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;
diff --git a/queue-4.4/netfilter-x_tables-validate-all-offsets-and-sizes-in-a-rule.patch b/queue-4.4/netfilter-x_tables-validate-all-offsets-and-sizes-in-a-rule.patch
new file mode 100644 (file)
index 0000000..e5a7507
--- /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
+@@ -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);
diff --git a/queue-4.4/netfilter-x_tables-validate-targets-of-jumps.patch b/queue-4.4/netfilter-x_tables-validate-targets-of-jumps.patch
new file mode 100644 (file)
index 0000000..d4e3bb8
--- /dev/null
@@ -0,0 +1,133 @@
+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;
index 16d158f598277cdb341ca064760037cf1730d444..67087abbdfcc4735d5a9f72b033204c07470fb12 100644 (file)
@@ -63,3 +63,12 @@ netfilter-x_tables-make-sure-e-next_offset-covers-remaining-blob-size.patch
 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