ec226c2
From 2b32a7d82223d76ace432305b18c5816cadff878 Mon Sep 17 00:00:00 2001
ec226c2
From: Florian Westphal <fw () strlen ! de>
ec226c2
Date: Thu, 10 Mar 2016 00:56:02 -0800
ec226c2
Subject: [PATCH] netfilter: x_tables: deal with bogus nextoffset values
ec226c2
ec226c2
Ben Hawkes says:
ec226c2
ec226c2
 In the mark_source_chains function (net/ipv4/netfilter/ip_tables.c) it
ec226c2
 is possible for a user-supplied ipt_entry structure to have a large
ec226c2
 next_offset field. This field is not bounds checked prior to writing a
ec226c2
 counter value at the supplied offset.
ec226c2
ec226c2
Problem is that xt_entry_foreach() macro stops iterating once e->next_offset
ec226c2
is out of bounds, assuming this is the last entry.
ec226c2
ec226c2
With malformed data thats not necessarily the case so we can
ec226c2
write outside of allocated area later as we might not have walked the
ec226c2
entire blob.
ec226c2
ec226c2
Fix this by simplifying mark_source_chains -- it already has to check
ec226c2
if nextoff is in range to catch invalid jumps, so just do the check
ec226c2
when we move to a next entry as well.
ec226c2
ec226c2
Signed-off-by: Florian Westphal <fw@strlen.de>
ec226c2
---
ec226c2
 net/ipv4/netfilter/arp_tables.c | 8 ++++++++
ec226c2
 net/ipv4/netfilter/ip_tables.c  | 8 ++++++++
ec226c2
 net/ipv6/netfilter/ip6_tables.c | 6 ++++++
ec226c2
 3 files changed, 22 insertions(+)
ec226c2
ec226c2
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
ec226c2
index 2033f92..a9b6c76 100644
ec226c2
--- a/net/ipv4/netfilter/arp_tables.c
ec226c2
+++ b/net/ipv4/netfilter/arp_tables.c
ec226c2
@@ -376,6 +376,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
ec226c2
 
ec226c2
 				/* Move along one */
ec226c2
 				size = e->next_offset;
ec226c2
+
ec226c2
+				if (pos + size > newinfo->size - sizeof(*e))
ec226c2
+					return 0;
ec226c2
+
ec226c2
 				e = (struct arpt_entry *)
ec226c2
 					(entry0 + pos + size);
ec226c2
 				if (pos + size >= newinfo->size)
ec226c2
@@ -399,6 +403,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
ec226c2
 					if (newpos >= newinfo->size)
ec226c2
 						return 0;
ec226c2
 				}
ec226c2
+
ec226c2
+				if (newpos > newinfo->size - sizeof(*e))
ec226c2
+					return 0;
ec226c2
+
ec226c2
 				e = (struct arpt_entry *)
ec226c2
 					(entry0 + newpos);
ec226c2
 				e->counters.pcnt = pos;
ec226c2
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
ec226c2
index 54906e0..7530ecd 100644
ec226c2
--- a/net/ipv4/netfilter/ip_tables.c
ec226c2
+++ b/net/ipv4/netfilter/ip_tables.c
ec226c2
@@ -447,6 +447,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
ec226c2
 
ec226c2
 				/* Move along one */
ec226c2
 				size = e->next_offset;
ec226c2
+
ec226c2
+				if (pos + size > newinfo->size - sizeof(*e))
ec226c2
+					return 0;
ec226c2
+
ec226c2
 				e = (struct ipt_entry *)
ec226c2
 					(entry0 + pos + size);
ec226c2
 				if (pos + size >= newinfo->size)
ec226c2
@@ -470,6 +474,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
ec226c2
 					if (newpos >= newinfo->size)
ec226c2
 						return 0;
ec226c2
 				}
ec226c2
+
ec226c2
+				if (newpos > newinfo->size - sizeof(*e))
ec226c2
+					return 0;
ec226c2
+
ec226c2
 				e = (struct ipt_entry *)
ec226c2
 					(entry0 + newpos);
ec226c2
 				e->counters.pcnt = pos;
ec226c2
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
ec226c2
index 63e06c3..894da69 100644
ec226c2
--- a/net/ipv6/netfilter/ip6_tables.c
ec226c2
+++ b/net/ipv6/netfilter/ip6_tables.c
ec226c2
@@ -474,6 +474,8 @@ mark_source_chains(const struct xt_table_info *newinfo,
ec226c2
 
ec226c2
 				/* Move along one */
ec226c2
 				size = e->next_offset;
ec226c2
+				if (pos + size > newinfo->size - sizeof(*e))
ec226c2
+					return 0;
ec226c2
 				e = (struct ip6t_entry *)
ec226c2
 					(entry0 + pos + size);
ec226c2
 				if (pos + size >= newinfo->size)
ec226c2
@@ -497,6 +499,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
ec226c2
 					if (newpos >= newinfo->size)
ec226c2
 						return 0;
ec226c2
 				}
ec226c2
+
ec226c2
+				if (newpos > newinfo->size - sizeof(*e))
ec226c2
+					return 0;
ec226c2
+
ec226c2
 				e = (struct ip6t_entry *)
ec226c2
 					(entry0 + newpos);
ec226c2
 				e->counters.pcnt = pos;
ec226c2
-- 
ec226c2
2.5.5
ec226c2