+++ /dev/null
-From 0d7df906a0e78079a02108b06d32c3ef2238ad25 Mon Sep 17 00:00:00 2001
-From: Florian Westphal <fw@strlen.de>
-Date: Tue, 27 Feb 2018 19:42:37 +0100
-Subject: netfilter: x_tables: ensure last rule in base chain matches underflow/policy
-
-From: Florian Westphal <fw@strlen.de>
-
-commit 0d7df906a0e78079a02108b06d32c3ef2238ad25 upstream.
-
-Harmless from kernel point of view, but again iptables assumes that
-this is true when decoding ruleset coming from kernel.
-
-If a (syzkaller generated) ruleset doesn't have the underflow/policy
-stored as the last rule in the base chain, then iptables will abort()
-because it doesn't find the chain policy.
-
-libiptc assumes that the policy is the last rule in the basechain, which
-is only true for iptables-generated rulesets.
-
-Unfortunately this needs code duplication -- the functions need the
-struct layout of the rule head, but that is different for
-ip/ip6/arptables.
-
-NB: pr_warn could be pr_debug but in case this break rulesets somehow its
-useful to know why blob was rejected.
-
-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, 48 insertions(+), 3 deletions(-)
-
---- a/net/ipv4/netfilter/arp_tables.c
-+++ b/net/ipv4/netfilter/arp_tables.c
-@@ -309,10 +309,13 @@ static int mark_source_chains(const stru
- for (hook = 0; hook < NF_ARP_NUMHOOKS; hook++) {
- unsigned int pos = newinfo->hook_entry[hook];
- struct arpt_entry *e = entry0 + pos;
-+ unsigned int last_pos, depth;
-
- if (!(valid_hooks & (1 << hook)))
- continue;
-
-+ depth = 0;
-+ last_pos = pos;
- /* Set initial back pointer. */
- e->counters.pcnt = pos;
-
-@@ -348,6 +351,8 @@ static int mark_source_chains(const stru
- pos = e->counters.pcnt;
- e->counters.pcnt = 0;
-
-+ if (depth)
-+ --depth;
- /* We're at the start. */
- if (pos == oldpos)
- goto next;
-@@ -372,6 +377,9 @@ static int mark_source_chains(const stru
- if (!xt_find_jump_offset(offsets, newpos,
- newinfo->number))
- return 0;
-+
-+ if (entry0 + newpos != arpt_next_entry(e))
-+ ++depth;
- } else {
- /* ... this is a fallthru */
- newpos = pos + e->next_offset;
-@@ -382,8 +390,15 @@ static int mark_source_chains(const stru
- e->counters.pcnt = pos;
- pos = newpos;
- }
-+ if (depth == 0)
-+ last_pos = pos;
-+ }
-+next:
-+ if (last_pos != newinfo->underflow[hook]) {
-+ pr_err_ratelimited("last base chain position %u doesn't match underflow %u (hook %u)\n",
-+ last_pos, newinfo->underflow[hook], hook);
-+ return 0;
- }
--next: ;
- }
- return 1;
- }
---- a/net/ipv4/netfilter/ip_tables.c
-+++ b/net/ipv4/netfilter/ip_tables.c
-@@ -378,10 +378,13 @@ mark_source_chains(const struct xt_table
- for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
- unsigned int pos = newinfo->hook_entry[hook];
- struct ipt_entry *e = entry0 + pos;
-+ unsigned int last_pos, depth;
-
- if (!(valid_hooks & (1 << hook)))
- continue;
-
-+ depth = 0;
-+ last_pos = pos;
- /* Set initial back pointer. */
- e->counters.pcnt = pos;
-
-@@ -415,6 +418,8 @@ mark_source_chains(const struct xt_table
- pos = e->counters.pcnt;
- e->counters.pcnt = 0;
-
-+ if (depth)
-+ --depth;
- /* We're at the start. */
- if (pos == oldpos)
- goto next;
-@@ -439,6 +444,9 @@ mark_source_chains(const struct xt_table
- if (!xt_find_jump_offset(offsets, newpos,
- newinfo->number))
- return 0;
-+
-+ if (entry0 + newpos != ipt_next_entry(e))
-+ ++depth;
- } else {
- /* ... this is a fallthru */
- newpos = pos + e->next_offset;
-@@ -449,8 +457,15 @@ mark_source_chains(const struct xt_table
- e->counters.pcnt = pos;
- pos = newpos;
- }
-+ if (depth == 0)
-+ last_pos = pos;
-+ }
-+next:
-+ if (last_pos != newinfo->underflow[hook]) {
-+ pr_err_ratelimited("last base chain position %u doesn't match underflow %u (hook %u)\n",
-+ last_pos, newinfo->underflow[hook], hook);
-+ return 0;
- }
--next: ;
- }
- return 1;
- }
---- a/net/ipv6/netfilter/ip6_tables.c
-+++ b/net/ipv6/netfilter/ip6_tables.c
-@@ -396,10 +396,13 @@ mark_source_chains(const struct xt_table
- for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
- unsigned int pos = newinfo->hook_entry[hook];
- struct ip6t_entry *e = entry0 + pos;
-+ unsigned int last_pos, depth;
-
- if (!(valid_hooks & (1 << hook)))
- continue;
-
-+ depth = 0;
-+ last_pos = pos;
- /* Set initial back pointer. */
- e->counters.pcnt = pos;
-
-@@ -433,6 +436,8 @@ mark_source_chains(const struct xt_table
- pos = e->counters.pcnt;
- e->counters.pcnt = 0;
-
-+ if (depth)
-+ --depth;
- /* We're at the start. */
- if (pos == oldpos)
- goto next;
-@@ -457,6 +462,9 @@ mark_source_chains(const struct xt_table
- if (!xt_find_jump_offset(offsets, newpos,
- newinfo->number))
- return 0;
-+
-+ if (entry0 + newpos != ip6t_next_entry(e))
-+ ++depth;
- } else {
- /* ... this is a fallthru */
- newpos = pos + e->next_offset;
-@@ -467,8 +475,15 @@ mark_source_chains(const struct xt_table
- e->counters.pcnt = pos;
- pos = newpos;
- }
-+ if (depth == 0)
-+ last_pos = pos;
-+ }
-+next:
-+ if (last_pos != newinfo->underflow[hook]) {
-+ pr_err_ratelimited("last base chain position %u doesn't match underflow %u (hook %u)\n",
-+ last_pos, newinfo->underflow[hook], hook);
-+ return 0;
- }
--next: ;
- }
- return 1;
- }