Blob Blame History Raw
From 2b32a7d82223d76ace432305b18c5816cadff878 Mon Sep 17 00:00:00 2001
From: Florian Westphal <fw () strlen ! de>
Date: Thu, 10 Mar 2016 00:56:02 -0800
Subject: [PATCH] netfilter: x_tables: deal with bogus nextoffset values

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 xt_entry_foreach() macro stops iterating once e->next_offset
is out of bounds, assuming this is the last entry.

With malformed data thats not necessarily the case so we can
write outside of allocated area later as we might not have walked the
entire blob.

Fix this by simplifying mark_source_chains -- it already has to check
if nextoff is in range to catch invalid jumps, so just do the check
when we move to a next entry as well.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 net/ipv4/netfilter/arp_tables.c | 8 ++++++++
 net/ipv4/netfilter/ip_tables.c  | 8 ++++++++
 net/ipv6/netfilter/ip6_tables.c | 6 ++++++
 3 files changed, 22 insertions(+)

diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 2033f92..a9b6c76 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -376,6 +376,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
 
 				/* Move along one */
 				size = e->next_offset;
+
+				if (pos + size > newinfo->size - sizeof(*e))
+					return 0;
+
 				e = (struct arpt_entry *)
 					(entry0 + pos + size);
 				if (pos + size >= newinfo->size)
@@ -399,6 +403,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
 					if (newpos >= newinfo->size)
 						return 0;
 				}
+
+				if (newpos > newinfo->size - sizeof(*e))
+					return 0;
+
 				e = (struct arpt_entry *)
 					(entry0 + newpos);
 				e->counters.pcnt = pos;
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index 54906e0..7530ecd 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -447,6 +447,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
 
 				/* Move along one */
 				size = e->next_offset;
+
+				if (pos + size > newinfo->size - sizeof(*e))
+					return 0;
+
 				e = (struct ipt_entry *)
 					(entry0 + pos + size);
 				if (pos + size >= newinfo->size)
@@ -470,6 +474,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
 					if (newpos >= newinfo->size)
 						return 0;
 				}
+
+				if (newpos > newinfo->size - sizeof(*e))
+					return 0;
+
 				e = (struct ipt_entry *)
 					(entry0 + newpos);
 				e->counters.pcnt = pos;
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index 63e06c3..894da69 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -474,6 +474,8 @@ mark_source_chains(const struct xt_table_info *newinfo,
 
 				/* Move along one */
 				size = e->next_offset;
+				if (pos + size > newinfo->size - sizeof(*e))
+					return 0;
 				e = (struct ip6t_entry *)
 					(entry0 + pos + size);
 				if (pos + size >= newinfo->size)
@@ -497,6 +499,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
 					if (newpos >= newinfo->size)
 						return 0;
 				}
+
+				if (newpos > newinfo->size - sizeof(*e))
+					return 0;
+
 				e = (struct ip6t_entry *)
 					(entry0 + newpos);
 				e->counters.pcnt = pos;
-- 
2.5.5