d960d84
Subject:    [PATCH nf] netfilter: x_tables: deal with bogus nextoffset values
d960d84
From:       Florian Westphal <fw () strlen ! de>
d960d84
Date:       2016-03-10 0:56:02
d960d84
d960d84
Ben Hawkes says:
d960d84
d960d84
 In the mark_source_chains function (net/ipv4/netfilter/ip_tables.c) it
d960d84
 is possible for a user-supplied ipt_entry structure to have a large
d960d84
 next_offset field. This field is not bounds checked prior to writing a
d960d84
 counter value at the supplied offset.
d960d84
d960d84
Problem is that xt_entry_foreach() macro stops iterating once e->next_offset
d960d84
is out of bounds, assuming this is the last entry.
d960d84
d960d84
With malformed data thats not necessarily the case so we can
d960d84
write outside of allocated area later as we might not have walked the
d960d84
entire blob.
d960d84
d960d84
Fix this by simplifying mark_source_chains -- it already has to check
d960d84
if nextoff is in range to catch invalid jumps, so just do the check
d960d84
when we move to a next entry as well.
d960d84
d960d84
Signed-off-by: Florian Westphal <fw@strlen.de>
d960d84
---
d960d84
 net/ipv4/netfilter/arp_tables.c | 16 ++++++++--------
d960d84
 net/ipv4/netfilter/ip_tables.c  | 15 ++++++++-------
d960d84
 net/ipv6/netfilter/ip6_tables.c | 13 ++++++-------
d960d84
 3 files changed, 22 insertions(+), 22 deletions(-)
d960d84
d960d84
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
d960d84
index b488cac..5a0b591 100644
d960d84
--- a/net/ipv4/netfilter/arp_tables.c
d960d84
+++ b/net/ipv4/netfilter/arp_tables.c
d960d84
@@ -437,6 +437,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
d960d84
 
d960d84
 				/* Move along one */
d960d84
 				size = e->next_offset;
d960d84
+
d960d84
+				if (pos + size > newinfo->size - sizeof(*e))
d960d84
+					return 0;
d960d84
+
d960d84
 				e = (struct arpt_entry *)
d960d84
 					(entry0 + pos + size);
d960d84
 				e->counters.pcnt = pos;
d960d84
@@ -447,14 +451,6 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
d960d84
 				if (strcmp(t->target.u.user.name,
d960d84
 					   XT_STANDARD_TARGET) == 0 &&
d960d84
 				    newpos >= 0) {
d960d84
-					if (newpos > newinfo->size -
d960d84
-						sizeof(struct arpt_entry)) {
d960d84
-						duprintf("mark_source_chains: "
d960d84
-							"bad verdict (%i)\n",
d960d84
-								newpos);
d960d84
-						return 0;
d960d84
-					}
d960d84
-
d960d84
 					/* This a jump; chase it. */
d960d84
 					duprintf("Jump rule %u -> %u\n",
d960d84
 						 pos, newpos);
d960d84
@@ -462,6 +458,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
d960d84
 					/* ... this is a fallthru */
d960d84
 					newpos = pos + e->next_offset;
d960d84
 				}
d960d84
+
d960d84
+				if (newpos > newinfo->size - sizeof(*e))
d960d84
+					return 0;
d960d84
+
d960d84
 				e = (struct arpt_entry *)
d960d84
 					(entry0 + newpos);
d960d84
 				e->counters.pcnt = pos;
d960d84
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
d960d84
index b99affa..ceb995f 100644
d960d84
--- a/net/ipv4/netfilter/ip_tables.c
d960d84
+++ b/net/ipv4/netfilter/ip_tables.c
d960d84
@@ -519,6 +519,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
d960d84
 
d960d84
 				/* Move along one */
d960d84
 				size = e->next_offset;
d960d84
+
d960d84
+				if (pos + size > newinfo->size - sizeof(*e))
d960d84
+					return 0;
d960d84
+
d960d84
 				e = (struct ipt_entry *)
d960d84
 					(entry0 + pos + size);
d960d84
 				e->counters.pcnt = pos;
d960d84
@@ -529,13 +533,6 @@ mark_source_chains(const struct xt_table_info *newinfo,
d960d84
 				if (strcmp(t->target.u.user.name,
d960d84
 					   XT_STANDARD_TARGET) == 0 &&
d960d84
 				    newpos >= 0) {
d960d84
-					if (newpos > newinfo->size -
d960d84
-						sizeof(struct ipt_entry)) {
d960d84
-						duprintf("mark_source_chains: "
d960d84
-							"bad verdict (%i)\n",
d960d84
-								newpos);
d960d84
-						return 0;
d960d84
-					}
d960d84
 					/* This a jump; chase it. */
d960d84
 					duprintf("Jump rule %u -> %u\n",
d960d84
 						 pos, newpos);
d960d84
@@ -543,6 +540,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
d960d84
 					/* ... this is a fallthru */
d960d84
 					newpos = pos + e->next_offset;
d960d84
 				}
d960d84
+
d960d84
+				if (newpos > newinfo->size - sizeof(*e))
d960d84
+					return 0;
d960d84
+
d960d84
 				e = (struct ipt_entry *)
d960d84
 					(entry0 + newpos);
d960d84
 				e->counters.pcnt = pos;
d960d84
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
d960d84
index 99425cf..d88a794 100644
d960d84
--- a/net/ipv6/netfilter/ip6_tables.c
d960d84
+++ b/net/ipv6/netfilter/ip6_tables.c
d960d84
@@ -531,6 +531,8 @@ mark_source_chains(const struct xt_table_info *newinfo,
d960d84
 
d960d84
 				/* Move along one */
d960d84
 				size = e->next_offset;
d960d84
+				if (pos + size > newinfo->size - sizeof(*e))
d960d84
+					return 0;
d960d84
 				e = (struct ip6t_entry *)
d960d84
 					(entry0 + pos + size);
d960d84
 				e->counters.pcnt = pos;
d960d84
@@ -541,13 +543,6 @@ mark_source_chains(const struct xt_table_info *newinfo,
d960d84
 				if (strcmp(t->target.u.user.name,
d960d84
 					   XT_STANDARD_TARGET) == 0 &&
d960d84
 				    newpos >= 0) {
d960d84
-					if (newpos > newinfo->size -
d960d84
-						sizeof(struct ip6t_entry)) {
d960d84
-						duprintf("mark_source_chains: "
d960d84
-							"bad verdict (%i)\n",
d960d84
-								newpos);
d960d84
-						return 0;
d960d84
-					}
d960d84
 					/* This a jump; chase it. */
d960d84
 					duprintf("Jump rule %u -> %u\n",
d960d84
 						 pos, newpos);
d960d84
@@ -555,6 +550,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
d960d84
 					/* ... this is a fallthru */
d960d84
 					newpos = pos + e->next_offset;
d960d84
 				}
d960d84
+
d960d84
+				if (newpos > newinfo->size - sizeof(*e))
d960d84
+					return 0;
d960d84
+
d960d84
 				e = (struct ip6t_entry *)
d960d84
 					(entry0 + newpos);
d960d84
 				e->counters.pcnt = pos;
d960d84
-- 
d960d84
2.4.10