| From bdf533de6968e9686df777dc178486f600c6e617 Mon Sep 17 00:00:00 2001 |
| From: Florian Westphal <fw@strlen.de> |
| Date: Tue, 22 Mar 2016 18:02:49 +0100 |
| Subject: netfilter: x_tables: validate e->target_offset early |
| |
| From: Florian Westphal <fw@strlen.de> |
| |
| commit bdf533de6968e9686df777dc178486f600c6e617 upstream. |
| |
| We should check that e->target_offset is sane before |
| mark_source_chains gets called since it will fetch the target entry |
| for loop detection. |
| |
| Signed-off-by: Florian Westphal <fw@strlen.de> |
| Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| net/ipv4/netfilter/arp_tables.c | 17 ++++++++--------- |
| net/ipv4/netfilter/ip_tables.c | 17 ++++++++--------- |
| net/ipv6/netfilter/ip6_tables.c | 17 ++++++++--------- |
| 3 files changed, 24 insertions(+), 27 deletions(-) |
| |
| --- a/net/ipv4/netfilter/arp_tables.c |
| +++ b/net/ipv4/netfilter/arp_tables.c |
| @@ -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; |
| |