From: Greg Kroah-Hartman Date: Wed, 22 Jun 2016 05:08:14 +0000 (-0700) Subject: 4.4-stable patches X-Git-Tag: v3.14.73~21 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c0675f1dfb95575118cd96978ce380f549eefd72;p=thirdparty%2Fkernel%2Fstable-queue.git 4.4-stable patches added patches: netfilter-x_tables-fix-unconditional-helper.patch netfilter-x_tables-make-sure-e-next_offset-covers-remaining-blob-size.patch netfilter-x_tables-validate-e-target_offset-early.patch --- diff --git a/queue-4.4/netfilter-x_tables-fix-unconditional-helper.patch b/queue-4.4/netfilter-x_tables-fix-unconditional-helper.patch new file mode 100644 index 00000000000..358583a007f --- /dev/null +++ b/queue-4.4/netfilter-x_tables-fix-unconditional-helper.patch @@ -0,0 +1,231 @@ +From 54d83fc74aa9ec72794373cb47432c5f7fb1a309 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Tue, 22 Mar 2016 18:02:52 +0100 +Subject: netfilter: x_tables: fix unconditional helper + +From: Florian Westphal + +commit 54d83fc74aa9ec72794373cb47432c5f7fb1a309 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. + +Problem is that mark_source_chains should not have been called -- +the rule doesn't have a next entry, so its supposed to return +an absolute verdict of either ACCEPT or DROP. + +However, the function conditional() doesn't work as the name implies. +It only checks that the rule is using wildcard address matching. + +However, an unconditional rule must also not be using any matches +(no -m args). + +The underflow validator only checked the addresses, therefore +passing the 'unconditional absolute verdict' test, while +mark_source_chains also tested for presence of matches, and thus +proceeeded to the next (not-existent) rule. + +Unify this so that all the callers have same idea of 'unconditional 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 | 18 +++++++++--------- + net/ipv4/netfilter/ip_tables.c | 23 +++++++++++------------ + net/ipv6/netfilter/ip6_tables.c | 23 +++++++++++------------ + 3 files changed, 31 insertions(+), 33 deletions(-) + +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -359,11 +359,12 @@ unsigned int arpt_do_table(struct sk_buf + } + + /* All zeroes == unconditional rule. */ +-static inline bool unconditional(const struct arpt_arp *arp) ++static inline bool unconditional(const struct arpt_entry *e) + { + static const struct arpt_arp uncond; + +- return memcmp(arp, &uncond, sizeof(uncond)) == 0; ++ return e->target_offset == sizeof(struct arpt_entry) && ++ memcmp(&e->arp, &uncond, sizeof(uncond)) == 0; + } + + /* Figures out from what hook each rule can be called: returns 0 if +@@ -402,11 +403,10 @@ static int mark_source_chains(const stru + |= ((1 << hook) | (1 << NF_ARP_NUMHOOKS)); + + /* Unconditional return/END. */ +- if ((e->target_offset == sizeof(struct arpt_entry) && ++ if ((unconditional(e) && + (strcmp(t->target.u.user.name, + XT_STANDARD_TARGET) == 0) && +- t->verdict < 0 && unconditional(&e->arp)) || +- visited) { ++ t->verdict < 0) || visited) { + unsigned int oldpos, size; + + if ((strcmp(t->target.u.user.name, +@@ -551,7 +551,7 @@ static bool check_underflow(const struct + const struct xt_entry_target *t; + unsigned int verdict; + +- if (!unconditional(&e->arp)) ++ if (!unconditional(e)) + return false; + t = arpt_get_target_c(e); + if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0) +@@ -598,9 +598,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)) { +- pr_err("Underflows must be unconditional and " +- "use the STANDARD target with " +- "ACCEPT/DROP\n"); ++ pr_debug("Underflows must be unconditional and " ++ "use the STANDARD target with " ++ "ACCEPT/DROP\n"); + return -EINVAL; + } + newinfo->underflow[h] = underflows[h]; +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -168,11 +168,12 @@ get_entry(const void *base, unsigned int + + /* All zeroes == unconditional rule. */ + /* Mildly perf critical (only if packet tracing is on) */ +-static inline bool unconditional(const struct ipt_ip *ip) ++static inline bool unconditional(const struct ipt_entry *e) + { + static const struct ipt_ip uncond; + +- return memcmp(ip, &uncond, sizeof(uncond)) == 0; ++ return e->target_offset == sizeof(struct ipt_entry) && ++ memcmp(&e->ip, &uncond, sizeof(uncond)) == 0; + #undef FWINV + } + +@@ -229,11 +230,10 @@ get_chainname_rulenum(const struct ipt_e + } else if (s == e) { + (*rulenum)++; + +- if (s->target_offset == sizeof(struct ipt_entry) && ++ if (unconditional(s) && + strcmp(t->target.u.kernel.target->name, + XT_STANDARD_TARGET) == 0 && +- t->verdict < 0 && +- unconditional(&s->ip)) { ++ t->verdict < 0) { + /* Tail of chains: STANDARD target (return/policy) */ + *comment = *chainname == hookname + ? comments[NF_IP_TRACE_COMMENT_POLICY] +@@ -476,11 +476,10 @@ mark_source_chains(const struct xt_table + e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS)); + + /* Unconditional return/END. */ +- if ((e->target_offset == sizeof(struct ipt_entry) && ++ if ((unconditional(e) && + (strcmp(t->target.u.user.name, + XT_STANDARD_TARGET) == 0) && +- t->verdict < 0 && unconditional(&e->ip)) || +- visited) { ++ t->verdict < 0) || visited) { + unsigned int oldpos, size; + + if ((strcmp(t->target.u.user.name, +@@ -715,7 +714,7 @@ static bool check_underflow(const struct + const struct xt_entry_target *t; + unsigned int verdict; + +- if (!unconditional(&e->ip)) ++ if (!unconditional(e)) + return false; + t = ipt_get_target_c(e); + if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0) +@@ -763,9 +762,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)) { +- pr_err("Underflows must be unconditional and " +- "use the STANDARD target with " +- "ACCEPT/DROP\n"); ++ pr_debug("Underflows must be unconditional and " ++ "use the STANDARD target with " ++ "ACCEPT/DROP\n"); + return -EINVAL; + } + newinfo->underflow[h] = underflows[h]; +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -198,11 +198,12 @@ get_entry(const void *base, unsigned int + + /* All zeroes == unconditional rule. */ + /* Mildly perf critical (only if packet tracing is on) */ +-static inline bool unconditional(const struct ip6t_ip6 *ipv6) ++static inline bool unconditional(const struct ip6t_entry *e) + { + static const struct ip6t_ip6 uncond; + +- return memcmp(ipv6, &uncond, sizeof(uncond)) == 0; ++ return e->target_offset == sizeof(struct ip6t_entry) && ++ memcmp(&e->ipv6, &uncond, sizeof(uncond)) == 0; + } + + static inline const struct xt_entry_target * +@@ -258,11 +259,10 @@ get_chainname_rulenum(const struct ip6t_ + } else if (s == e) { + (*rulenum)++; + +- if (s->target_offset == sizeof(struct ip6t_entry) && ++ if (unconditional(s) && + strcmp(t->target.u.kernel.target->name, + XT_STANDARD_TARGET) == 0 && +- t->verdict < 0 && +- unconditional(&s->ipv6)) { ++ t->verdict < 0) { + /* Tail of chains: STANDARD target (return/policy) */ + *comment = *chainname == hookname + ? comments[NF_IP6_TRACE_COMMENT_POLICY] +@@ -488,11 +488,10 @@ mark_source_chains(const struct xt_table + e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS)); + + /* Unconditional return/END. */ +- if ((e->target_offset == sizeof(struct ip6t_entry) && ++ if ((unconditional(e) && + (strcmp(t->target.u.user.name, + XT_STANDARD_TARGET) == 0) && +- t->verdict < 0 && +- unconditional(&e->ipv6)) || visited) { ++ t->verdict < 0) || visited) { + unsigned int oldpos, size; + + if ((strcmp(t->target.u.user.name, +@@ -727,7 +726,7 @@ static bool check_underflow(const struct + const struct xt_entry_target *t; + unsigned int verdict; + +- if (!unconditional(&e->ipv6)) ++ if (!unconditional(e)) + return false; + t = ip6t_get_target_c(e); + if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0) +@@ -775,9 +774,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)) { +- pr_err("Underflows must be unconditional and " +- "use the STANDARD target with " +- "ACCEPT/DROP\n"); ++ pr_debug("Underflows must be unconditional and " ++ "use the STANDARD target with " ++ "ACCEPT/DROP\n"); + return -EINVAL; + } + newinfo->underflow[h] = underflows[h]; diff --git a/queue-4.4/netfilter-x_tables-make-sure-e-next_offset-covers-remaining-blob-size.patch b/queue-4.4/netfilter-x_tables-make-sure-e-next_offset-covers-remaining-blob-size.patch new file mode 100644 index 00000000000..de5dac13baf --- /dev/null +++ b/queue-4.4/netfilter-x_tables-make-sure-e-next_offset-covers-remaining-blob-size.patch @@ -0,0 +1,87 @@ +From 6e94e0cfb0887e4013b3b930fa6ab1fe6bb6ba91 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Tue, 22 Mar 2016 18:02:50 +0100 +Subject: netfilter: x_tables: make sure e->next_offset covers remaining blob size + +From: Florian Westphal + +commit 6e94e0cfb0887e4013b3b930fa6ab1fe6bb6ba91 upstream. + +Otherwise this function may read data beyond the ruleset blob. + +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + net/ipv4/netfilter/arp_tables.c | 6 ++++-- + net/ipv4/netfilter/ip_tables.c | 6 ++++-- + net/ipv6/netfilter/ip6_tables.c | 6 ++++-- + 3 files changed, 12 insertions(+), 6 deletions(-) + +--- a/net/ipv4/netfilter/arp_tables.c ++++ b/net/ipv4/netfilter/arp_tables.c +@@ -573,7 +573,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) { ++ (unsigned char *)e + sizeof(struct arpt_entry) >= limit || ++ (unsigned char *)e + e->next_offset > limit) { + duprintf("Bad offset %p\n", e); + return -EINVAL; + } +@@ -1232,7 +1233,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 || +- (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit) { ++ (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit || ++ (unsigned char *)e + e->next_offset > limit) { + duprintf("Bad offset %p, limit = %p\n", e, limit); + return -EINVAL; + } +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -738,7 +738,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) { ++ (unsigned char *)e + sizeof(struct ipt_entry) >= limit || ++ (unsigned char *)e + e->next_offset > limit) { + duprintf("Bad offset %p\n", e); + return -EINVAL; + } +@@ -1492,7 +1493,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 || +- (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) { ++ (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit || ++ (unsigned char *)e + e->next_offset > limit) { + duprintf("Bad offset %p, limit = %p\n", e, limit); + return -EINVAL; + } +--- a/net/ipv6/netfilter/ip6_tables.c ++++ b/net/ipv6/netfilter/ip6_tables.c +@@ -750,7 +750,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) { ++ (unsigned char *)e + sizeof(struct ip6t_entry) >= limit || ++ (unsigned char *)e + e->next_offset > limit) { + duprintf("Bad offset %p\n", e); + return -EINVAL; + } +@@ -1504,7 +1505,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 || +- (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) { ++ (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit || ++ (unsigned char *)e + e->next_offset > limit) { + duprintf("Bad offset %p, limit = %p\n", e, limit); + return -EINVAL; + } diff --git a/queue-4.4/netfilter-x_tables-validate-e-target_offset-early.patch b/queue-4.4/netfilter-x_tables-validate-e-target_offset-early.patch new file mode 100644 index 00000000000..2dc2c94ef0b --- /dev/null +++ b/queue-4.4/netfilter-x_tables-validate-e-target_offset-early.patch @@ -0,0 +1,197 @@ +From bdf533de6968e9686df777dc178486f600c6e617 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Tue, 22 Mar 2016 18:02:49 +0100 +Subject: netfilter: x_tables: validate e->target_offset early + +From: Florian Westphal + +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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + 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 +@@ -474,14 +474,12 @@ next: + 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; +@@ -522,10 +520,6 @@ find_check_entry(struct arpt_entry *e, c + struct xt_target *target; + int ret; + +- ret = check_entry(e, name); +- if (ret) +- return ret; +- + e->counters.pcnt = xt_percpu_counter_alloc(); + if (IS_ERR_VALUE(e->counters.pcnt)) + return -ENOMEM; +@@ -576,6 +570,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) { +@@ -590,6 +585,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))) +@@ -1246,7 +1245,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 +@@ -569,14 +569,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) +@@ -666,10 +664,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; +- + e->counters.pcnt = xt_percpu_counter_alloc(); + if (IS_ERR_VALUE(e->counters.pcnt)) + return -ENOMEM; +@@ -741,6 +735,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) { +@@ -755,6 +750,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))) +@@ -1506,7 +1505,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 +@@ -581,14 +581,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) +@@ -679,10 +677,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; +- + e->counters.pcnt = xt_percpu_counter_alloc(); + if (IS_ERR_VALUE(e->counters.pcnt)) + return -ENOMEM; +@@ -753,6 +747,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) { +@@ -767,6 +762,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))) +@@ -1518,7 +1517,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; + diff --git a/queue-4.4/series b/queue-4.4/series index 40b3fee1354..75147439c38 100644 --- a/queue-4.4/series +++ b/queue-4.4/series @@ -58,3 +58,6 @@ sparc64-take-ctx_alloc_lock-properly-in-hugetlb_setup.patch sparc-harden-signal-return-frame-checks.patch sparc64-fix-return-from-trap-window-fill-crashes.patch mips-fix-64k-page-support-for-32-bit-kernels.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