diff options
author | Kirill Korotaev <dev@openvz.org> | 2006-02-04 02:16:56 -0800 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-02-04 23:51:25 -0800 |
commit | ee4bb818ae35f68d1f848eae0a7b150a38eb4168 (patch) | |
tree | 85a6ba60fc5782d77779f466f1ad5f2ec4330914 | |
parent | df4e9574a36748c3a4d9b03ffca6b42321a797a9 (diff) |
[NETFILTER]: Fix possible overflow in netfilters do_replace()
netfilter's do_replace() can overflow on addition within SMP_ALIGN()
and/or on multiplication by NR_CPUS, resulting in a buffer overflow on
the copy_from_user(). In practice, the overflow on addition is
triggerable on all systems, whereas the multiplication one might require
much physical memory to be present due to the check above. Either is
sufficient to overwrite arbitrary amounts of kernel memory.
I really hate adding the same check to all 4 versions of do_replace(),
but the code is duplicate...
Found by Solar Designer during security audit of OpenVZ.org
Signed-Off-By: Kirill Korotaev <dev@openvz.org>
Signed-Off-By: Solar Designer <solar@openwall.com>
Signed-off-by: Patrck McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/bridge/netfilter/ebtables.c | 7 | ||||
-rw-r--r-- | net/ipv4/netfilter/arp_tables.c | 7 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_tables.c | 7 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6_tables.c | 7 |
4 files changed, 28 insertions, 0 deletions
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 00729b3604f8..cbd4020cc84d 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -934,6 +934,13 @@ static int do_replace(void __user *user, unsigned int len) BUGPRINT("Entries_size never zero\n"); return -EINVAL; } + /* overflow check */ + if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) / NR_CPUS - + SMP_CACHE_BYTES) / sizeof(struct ebt_counter)) + return -ENOMEM; + if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter)) + return -ENOMEM; + countersize = COUNTER_OFFSET(tmp.nentries) * (highest_possible_processor_id()+1); newinfo = (struct ebt_table_info *) diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index afe3d8f8177d..dd1048be8a01 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -807,6 +807,13 @@ static int do_replace(void __user *user, unsigned int len) if (len != sizeof(tmp) + tmp.size) return -ENOPROTOOPT; + /* overflow check */ + if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS - + SMP_CACHE_BYTES) + return -ENOMEM; + if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) + return -ENOMEM; + newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) return -ENOMEM; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 2371b2062c2d..16f47c675fef 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -921,6 +921,13 @@ do_replace(void __user *user, unsigned int len) if (len != sizeof(tmp) + tmp.size) return -ENOPROTOOPT; + /* overflow check */ + if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS - + SMP_CACHE_BYTES) + return -ENOMEM; + if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) + return -ENOMEM; + newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) return -ENOMEM; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 847068fd3367..74ff56c322f4 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -978,6 +978,13 @@ do_replace(void __user *user, unsigned int len) if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) return -EFAULT; + /* overflow check */ + if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS - + SMP_CACHE_BYTES) + return -ENOMEM; + if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) + return -ENOMEM; + newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) return -ENOMEM; |