From: Greg Kroah-Hartman Date: Wed, 22 Jun 2016 21:19:06 +0000 (-0700) Subject: 4.6-stable patches X-Git-Tag: v3.14.73~13 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4d2f72c5984391a251fe7962fccf6b8cc98d1f29;p=thirdparty%2Fkernel%2Fstable-queue.git 4.6-stable patches 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 --- diff --git a/queue-4.6/netfilter-x_tables-add-and-use-xt_check_entry_offsets.patch b/queue-4.6/netfilter-x_tables-add-and-use-xt_check_entry_offsets.patch new file mode 100644 index 00000000000..03c61d3ed5a --- /dev/null +++ b/queue-4.6/netfilter-x_tables-add-and-use-xt_check_entry_offsets.patch @@ -0,0 +1,157 @@ +From 7d35812c3214afa5b37a675113555259cfd67b98 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Fri, 1 Apr 2016 14:17:23 +0200 +Subject: netfilter: x_tables: add and use xt_check_entry_offsets + +From: Florian Westphal + +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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +@@ -242,6 +242,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 +@@ -541,6 +541,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.6/netfilter-x_tables-add-compat-version-of-xt_check_entry_offsets.patch b/queue-4.6/netfilter-x_tables-add-compat-version-of-xt_check_entry_offsets.patch new file mode 100644 index 00000000000..567c20ddd45 --- /dev/null +++ b/queue-4.6/netfilter-x_tables-add-compat-version-of-xt_check_entry_offsets.patch @@ -0,0 +1,111 @@ +From fc1221b3a163d1386d1052184202d5dc50d302d1 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Fri, 1 Apr 2016 14:17:26 +0200 +Subject: netfilter: x_tables: add compat version of xt_check_entry_offsets + +From: Florian Westphal + +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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +@@ -494,6 +494,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 +@@ -1254,7 +1254,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 +@@ -1513,7 +1513,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 +@@ -1525,7 +1525,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 +@@ -539,6 +539,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 */ + + /** +@@ -549,6 +570,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.6/netfilter-x_tables-assert-minimum-target-size.patch b/queue-4.6/netfilter-x_tables-assert-minimum-target-size.patch new file mode 100644 index 00000000000..5d7bdc017d9 --- /dev/null +++ b/queue-4.6/netfilter-x_tables-assert-minimum-target-size.patch @@ -0,0 +1,31 @@ +From a08e4e190b866579896c09af59b3bdca821da2cd Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Fri, 1 Apr 2016 14:17:25 +0200 +Subject: netfilter: x_tables: assert minimum target size + +From: Florian Westphal + +commit a08e4e190b866579896c09af59b3bdca821da2cd upstream. + +The target size includes the size of the xt_entry_target struct. + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/netfilter/x_tables.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -568,6 +568,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.6/netfilter-x_tables-check-for-bogus-target-offset.patch b/queue-4.6/netfilter-x_tables-check-for-bogus-target-offset.patch new file mode 100644 index 00000000000..b2874f80817 --- /dev/null +++ b/queue-4.6/netfilter-x_tables-check-for-bogus-target-offset.patch @@ -0,0 +1,170 @@ +From ce683e5f9d045e5d67d1312a42b359cb2ab2a13c Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Fri, 1 Apr 2016 14:17:28 +0200 +Subject: netfilter: x_tables: check for bogus target offset + +From: Florian Westphal + +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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +@@ -242,7 +242,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); + +@@ -494,7 +494,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; + +@@ -1254,7 +1255,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; + +@@ -1513,7 +1514,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; + +@@ -1525,7 +1526,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 +@@ -546,14 +546,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; + +@@ -577,12 +580,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 +@@ -591,12 +598,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.6/netfilter-x_tables-check-standard-target-size-too.patch b/queue-4.6/netfilter-x_tables-check-standard-target-size-too.patch new file mode 100644 index 00000000000..9ef9fa52c6a --- /dev/null +++ b/queue-4.6/netfilter-x_tables-check-standard-target-size-too.patch @@ -0,0 +1,66 @@ +From 7ed2abddd20cf8f6bd27f65bd218f26fa5bf7f44 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Fri, 1 Apr 2016 14:17:27 +0200 +Subject: netfilter: x_tables: check standard target size too + +From: Florian Westphal + +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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/netfilter/x_tables.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -540,6 +540,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) +@@ -557,6 +564,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); +@@ -596,6 +607,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.6/netfilter-x_tables-don-t-move-to-non-existent-next-rule.patch b/queue-4.6/netfilter-x_tables-don-t-move-to-non-existent-next-rule.patch new file mode 100644 index 00000000000..3183c1da5e6 --- /dev/null +++ b/queue-4.6/netfilter-x_tables-don-t-move-to-non-existent-next-rule.patch @@ -0,0 +1,106 @@ +From f24e230d257af1ad7476c6e81a8dc3127a74204e Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Fri, 1 Apr 2016 14:17:21 +0200 +Subject: netfilter: x_tables: don't move to non-existent next rule + +From: Florian Westphal + +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 +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + 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.6/netfilter-x_tables-kill-check_entry-helper.patch b/queue-4.6/netfilter-x_tables-kill-check_entry-helper.patch new file mode 100644 index 00000000000..16a3420f1c3 --- /dev/null +++ b/queue-4.6/netfilter-x_tables-kill-check_entry-helper.patch @@ -0,0 +1,155 @@ +From aa412ba225dd3bc36d404c28cdc3d674850d80d0 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Fri, 1 Apr 2016 14:17:24 +0200 +Subject: netfilter: x_tables: kill check_entry helper + +From: Florian Westphal + +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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + 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; + +@@ -1256,8 +1251,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; + +@@ -1516,8 +1510,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; + +@@ -1528,8 +1522,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.6/netfilter-x_tables-validate-all-offsets-and-sizes-in-a-rule.patch b/queue-4.6/netfilter-x_tables-validate-all-offsets-and-sizes-in-a-rule.patch new file mode 100644 index 00000000000..1f6a6ed40d0 --- /dev/null +++ b/queue-4.6/netfilter-x_tables-validate-all-offsets-and-sizes-in-a-rule.patch @@ -0,0 +1,143 @@ +From 13631bfc604161a9d69cd68991dff8603edd66f9 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Fri, 1 Apr 2016 14:17:29 +0200 +Subject: netfilter: x_tables: validate all offsets and sizes in a rule + +From: Florian Westphal + +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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +@@ -416,6 +416,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) + { +@@ -571,7 +612,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 */ +@@ -584,17 +632,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, +@@ -624,7 +694,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.6/netfilter-x_tables-validate-targets-of-jumps.patch b/queue-4.6/netfilter-x_tables-validate-targets-of-jumps.patch new file mode 100644 index 00000000000..d4e3bb836bf --- /dev/null +++ b/queue-4.6/netfilter-x_tables-validate-targets-of-jumps.patch @@ -0,0 +1,133 @@ +From 36472341017529e2b12573093cc0f68719300997 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Fri, 1 Apr 2016 14:17:22 +0200 +Subject: netfilter: x_tables: validate targets of jumps + +From: Florian Westphal + +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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + 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; diff --git a/queue-4.6/series b/queue-4.6/series index a497f00ad2e..337b7842fe2 100644 --- a/queue-4.6/series +++ b/queue-4.6/series @@ -68,3 +68,12 @@ sparc64-fix-return-from-trap-window-fill-crashes.patch gpio-zynq-fix-the-error-path.patch gpio-zynq-initialize-clock-even-without-config_pm.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