Skip to content

Commit a60f7bf

Browse files
author
Florian Westphal
committed
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: c1eda3c ("netfilter: nft_rbtree: ignore inactive matching element with no descendants") Signed-off-by: Florian Westphal <[email protected]>
1 parent c4eaca2 commit a60f7bf

File tree

1 file changed

+3
-3
lines changed

1 file changed

+3
-3
lines changed

net/netfilter/nft_set_rbtree.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ __nft_rbtree_lookup(const struct net *net, const struct nft_set *set,
7777
nft_rbtree_interval_end(rbe) &&
7878
nft_rbtree_interval_start(interval))
7979
continue;
80-
interval = rbe;
80+
if (nft_set_elem_active(&rbe->ext, genmask) &&
81+
!nft_rbtree_elem_expired(rbe))
82+
interval = rbe;
8183
} else if (d > 0)
8284
parent = rcu_dereference_raw(parent->rb_right);
8385
else {
@@ -102,8 +104,6 @@ __nft_rbtree_lookup(const struct net *net, const struct nft_set *set,
102104
}
103105

104106
if (set->flags & NFT_SET_INTERVAL && interval != NULL &&
105-
nft_set_elem_active(&interval->ext, genmask) &&
106-
!nft_rbtree_elem_expired(interval) &&
107107
nft_rbtree_interval_start(interval))
108108
return &interval->ext;
109109

0 commit comments

Comments
 (0)