Blame SOURCES/0009-scanner-don-t-pop-active-flex-scanner-scope.patch

1d03cd
From ec8483bd878fa1c8a7b53b243e5d7017634df65e Mon Sep 17 00:00:00 2001
1d03cd
From: Phil Sutter <psutter@redhat.com>
1d03cd
Date: Thu, 9 Feb 2023 10:18:10 +0100
1d03cd
Subject: [PATCH] scanner: don't pop active flex scanner scope
1d03cd
1d03cd
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2113874
1d03cd
Upstream Status: nftables commit 8623772af0610
1d03cd
1d03cd
commit 8623772af06103ed4ccca3d07e55afbf3d952d6d
1d03cd
Author: Florian Westphal <fw@strlen.de>
1d03cd
Date:   Thu Jun 23 19:56:19 2022 +0200
1d03cd
1d03cd
    scanner: don't pop active flex scanner scope
1d03cd
1d03cd
    Currently we can pop a flex scope that is still active, i.e. the
1d03cd
    scanner_pop_start_cond() for the scope has not been done.
1d03cd
1d03cd
    Example:
1d03cd
      counter ipsec out ip daddr 192.168.1.2 counter name "ipsec_out"
1d03cd
1d03cd
    Here, parser fails because 'daddr' is parsed as STRING, not as DADDR token.
1d03cd
1d03cd
    Bug is as follows:
1d03cd
    COUNTER changes scope to COUNTER. (COUNTER).
1d03cd
    Next, IPSEC scope gets pushed, stack is: COUNTER, IPSEC.
1d03cd
1d03cd
    Then, the 'COUNTER' scope close happens.  Because active scope has changed,
1d03cd
    we cannot pop (we would pop the 'ipsec' scope in flex).
1d03cd
    The pop operation gets delayed accordingly.
1d03cd
1d03cd
    Next, IP gets pushed, stack is: COUNTER, IPSEC, IP, plus the information
1d03cd
    that one scope closure/pop was delayed.
1d03cd
1d03cd
    Then, the IP scope is closed.  Because a pop operation was delayed, we pop again,
1d03cd
    which brings us back to COUNTER state.
1d03cd
1d03cd
    This is bogus: The pop operation CANNOT be done yet, because the ipsec scope
1d03cd
    is still open, but the existing code lacks the information to detect this.
1d03cd
1d03cd
    After popping the IP scope, we must remain in IPSEC scope until bison
1d03cd
    parser calls scanner_pop_start_cond(, IPSEC).
1d03cd
1d03cd
    This adds a counter per flex scope so that we can detect this case.
1d03cd
    In above case, after the IP scope gets closed, the "new" (previous)
1d03cd
    scope (IPSEC) will be treated as active and its close is attempted again
1d03cd
    on the next call to scanner_pop_start_cond().
1d03cd
1d03cd
    After this patch, transition in above rule is:
1d03cd
1d03cd
    push counter (COUNTER)
1d03cd
    push IPSEC (COUNTER, IPSEC)
1d03cd
    pop COUNTER (delayed: COUNTER, IPSEC, pending-pop for COUNTER),
1d03cd
    push IP (COUNTER, IPSEC, IP, pending-pop for COUNTER)
1d03cd
    pop IP (COUNTER, IPSEC, pending-pop for COUNTER)
1d03cd
    parse DADDR (we're in IPSEC scope, its valid token)
1d03cd
    pop IPSEC (pops all remaining scopes).
1d03cd
1d03cd
    We could also resurrect the commit:
1d03cd
    "scanner: flags: move to own scope", the test case passes with the
1d03cd
    new scope closure logic.
1d03cd
1d03cd
    Fixes: bff106c5b277 ("scanner: add support for scope nesting")
1d03cd
    Signed-off-by: Florian Westphal <fw@strlen.de>
1d03cd
1d03cd
Signed-off-by: Phil Sutter <psutter@redhat.com>
1d03cd
---
1d03cd
 include/parser.h |  3 +++
1d03cd
 src/scanner.l    | 11 +++++++++++
1d03cd
 2 files changed, 14 insertions(+)
1d03cd
1d03cd
diff --git a/include/parser.h b/include/parser.h
1d03cd
index f32154c..5e5ad28 100644
1d03cd
--- a/include/parser.h
1d03cd
+++ b/include/parser.h
1d03cd
@@ -26,6 +26,7 @@ struct parser_state {
1d03cd
 	unsigned int			flex_state_pop;
1d03cd
 	unsigned int			startcond_type;
1d03cd
 	struct list_head		*cmds;
1d03cd
+	unsigned int			*startcond_active;
1d03cd
 };
1d03cd
 
1d03cd
 enum startcond_type {
1d03cd
@@ -82,6 +83,8 @@ enum startcond_type {
1d03cd
 	PARSER_SC_STMT_REJECT,
1d03cd
 	PARSER_SC_STMT_SYNPROXY,
1d03cd
 	PARSER_SC_STMT_TPROXY,
1d03cd
+
1d03cd
+	__SC_MAX
1d03cd
 };
1d03cd
 
1d03cd
 struct mnl_socket;
1d03cd
diff --git a/src/scanner.l b/src/scanner.l
1d03cd
index 2154281..ed7256b 100644
1d03cd
--- a/src/scanner.l
1d03cd
+++ b/src/scanner.l
1d03cd
@@ -1148,6 +1148,8 @@ void *scanner_init(struct parser_state *state)
1d03cd
 	yylex_init_extra(state, &scanner);
1d03cd
 	yyset_out(NULL, scanner);
1d03cd
 
1d03cd
+	state->startcond_active = xzalloc_array(__SC_MAX,
1d03cd
+						sizeof(*state->startcond_active));
1d03cd
 	return scanner;
1d03cd
 }
1d03cd
 
1d03cd
@@ -1177,6 +1179,8 @@ void scanner_destroy(struct nft_ctx *nft)
1d03cd
 	struct parser_state *state = yyget_extra(nft->scanner);
1d03cd
 
1d03cd
 	input_descriptor_list_destroy(state);
1d03cd
+	xfree(state->startcond_active);
1d03cd
+
1d03cd
 	yylex_destroy(nft->scanner);
1d03cd
 }
1d03cd
 
1d03cd
@@ -1185,6 +1189,7 @@ static void scanner_push_start_cond(void *scanner, enum startcond_type type)
1d03cd
 	struct parser_state *state = yyget_extra(scanner);
1d03cd
 
1d03cd
 	state->startcond_type = type;
1d03cd
+	state->startcond_active[type]++;
1d03cd
 
1d03cd
 	yy_push_state((int)type, scanner);
1d03cd
 }
1d03cd
@@ -1193,6 +1198,8 @@ void scanner_pop_start_cond(void *scanner, enum startcond_type t)
1d03cd
 {
1d03cd
 	struct parser_state *state = yyget_extra(scanner);
1d03cd
 
1d03cd
+	state->startcond_active[t]--;
1d03cd
+
1d03cd
 	if (state->startcond_type != t) {
1d03cd
 		state->flex_state_pop++;
1d03cd
 		return; /* Can't pop just yet! */
1d03cd
@@ -1202,6 +1209,10 @@ void scanner_pop_start_cond(void *scanner, enum startcond_type t)
1d03cd
 		state->flex_state_pop--;
1d03cd
 		state->startcond_type = yy_top_state(scanner);
1d03cd
 		yy_pop_state(scanner);
1d03cd
+
1d03cd
+		t = state->startcond_type;
1d03cd
+		if (state->startcond_active[t])
1d03cd
+			return;
1d03cd
 	}
1d03cd
 
1d03cd
 	state->startcond_type = yy_top_state(scanner);
1d03cd
-- 
1d03cd
2.39.1
1d03cd