If a match is found in a rbtree, set the interval at the very end to avoid the element being inactive when finishing the traversal.
Signed-off-by: Gabriel Goller <[email protected]> --- ...t_rbtree-continue-traversal-if-eleme.patch | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 patches/kernel/0015-netfilter-nft_set_rbtree-continue-traversal-if-eleme.patch diff --git a/patches/kernel/0015-netfilter-nft_set_rbtree-continue-traversal-if-eleme.patch b/patches/kernel/0015-netfilter-nft_set_rbtree-continue-traversal-if-eleme.patch new file mode 100644 index 000000000000..9e4d4d687003 --- /dev/null +++ b/patches/kernel/0015-netfilter-nft_set_rbtree-continue-traversal-if-eleme.patch @@ -0,0 +1,88 @@ +From 2af0ed300431a3c5675cd6a7219424430fa9651b Mon Sep 17 00:00:00 2001 +From: Gabriel Goller <[email protected]> +Date: Wed, 10 Sep 2025 12:08:56 +0200 +Subject: [PATCH 2/5] netfilter: nft_set_rbtree: continue traversal if element + is inactive + +When the rbtree lookup function finds a match in the rbtree, it sets the +range start interval to a potentially inactive element. + +Then, after tree lookup, if the matching element is inactive, it returns +NULL and suppresses a matching result. + +This is wrong and leads to false negative matches when a transaction has +already entered the commit phase. + +cpu0 cpu1 + has added new elements to clone + has marked elements as being + inactive in new generation + perform lookup in the set + enters commit phase: +I) increments the genbit + A) observes new genbit + B) finds matching range + C) returns no match: found + range invalid in new generation +II) removes old elements from the tree + C New nft_lookup happening now + will find matching element, + because it is no longer + obscured by old, inactive one. + +Consider a packet matching range r1-r2: + +cpu0 processes following transaction: +1. remove r1-r2 +2. add r1-r3 + +P is contained in both ranges. Therefore, cpu1 should always find a match +for P. Due to above race, this is not the case: + +cpu1 does find r1-r2, but then ignores it due to the genbit indicating +the range has been removed. It does NOT test for further matches. + +The situation persists for all lookups until after cpu0 hits II) after +which r1-r3 range start node is tested for the first time. + +Move the "interval start is valid" check ahead so that tree traversal +continues if the starting interval is not valid in this generation. + +Thanks to Stefan Hanreich for providing an initial reproducer for this +bug. + +Reported-by: Stefan Hanreich <[email protected]> +Fixes: c1eda3c6394f ("netfilter: nft_rbtree: ignore inactive matching element with no descendants") +Signed-off-by: Florian Westphal <[email protected]> +Signed-off-by: Gabriel Goller <[email protected]> +--- + net/netfilter/nft_set_rbtree.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c +index 2e8ef16ff191..c4eb94258e24 100644 +--- a/net/netfilter/nft_set_rbtree.c ++++ b/net/netfilter/nft_set_rbtree.c +@@ -77,7 +77,9 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set + nft_rbtree_interval_end(rbe) && + nft_rbtree_interval_start(interval)) + continue; +- interval = rbe; ++ if (nft_set_elem_active(&rbe->ext, genmask) && ++ !nft_rbtree_elem_expired(rbe)) ++ interval = rbe; + } else if (d > 0) + parent = rcu_dereference_raw(parent->rb_right); + else { +@@ -103,8 +105,6 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set + } + + if (set->flags & NFT_SET_INTERVAL && interval != NULL && +- nft_set_elem_active(&interval->ext, genmask) && +- !nft_rbtree_elem_expired(interval) && + nft_rbtree_interval_start(interval)) { + *ext = &interval->ext; + return true; +-- +2.47.3 + -- 2.47.3 _______________________________________________ pve-devel mailing list [email protected] https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
