From: Pablo Neira Ayuso <pablo@netfilter.org>
To: joasantonio108@gmail.com
Cc: Florian Westphal <fw@strlen.de>, Phil Sutter <phil@nwl.cc>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Simon Horman <horms@kernel.org>,
netfilter-devel@vger.kernel.org, coreteam@netfilter.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH nf] netfilter: nft_set_rbtree: do not pair an expired start with another interval's end
Date: Sat, 31 Oct 2026 18:50:02 +0100 [thread overview]
Message-ID: <auYqSpBFtC0nTDDd@chamomile> (raw)
In-Reply-To: <auYpCzdrOsoWr84Z@chamomile>
On Sat, Oct 31, 2026 at 06:44:43PM +0100, Pablo Neira Ayuso wrote:
> On Sat, Oct 31, 2026 at 03:54:05PM +0000, Joas Antonio dos Santos via B4 Relay wrote:
> > From: Joas Antonio dos Santos <joasantonio108@gmail.com>
> >
> > Garbage collection of an expired interval start element also removes its
> > end element. Both the insertion path, nft_rbtree_gc_elem(), and the
> > commit path, nft_rbtree_gc_scan(), find that end element by its position
> > in the tree, and both can pick the end element of a different, live
> > interval:
> >
> > - nft_rbtree_gc_elem() walks towards higher keys and skips end elements
> > that are not active in both generations. If the end element of the
> > expired interval is being deleted in the same transaction, the walk
> > skips it, passes the start element of the next interval and takes that
> > interval's end element instead.
> >
> > - nft_rbtree_gc_scan() remembers the last end element it visited in
> > rbe_end, but does not clear it when it visits a live start element. An
> > expired start element without an end element (a start-only element, or
> > an interval whose end element was already removed) then takes the end
> > element of the live interval above it.
> >
> > In both cases the live interval [S, E) loses E: the set no longer holds
> > the interval that userspace added, and deleting [S, E) later fails with
> > ENOENT.
> >
> > In the insertion path, stop the walk at the first element that is active
> > in the current generation. Elements added by this transaction are still
> > skipped, as intended by commit 60c0c230c6f0 ("netfilter: nft_set_rbtree:
> > skip end interval element from gc"). If that element is a start element,
> > the expired interval has no end element. If it is an end element that
> > this transaction is removing, leave it to the transaction. In the commit
> > path, clear rbe_end when a live start element is visited.
> >
> > Reproduced on current mainline under QEMU (arm64, KASAN), from an
> > unprivileged user in a new user and network namespace:
> >
> > - insertion path: add [10,20) with a 50ms timeout and [30,40), wait,
> > then in one batch delete the end element 20 and add [12,13).
>
> I don't follow.
>
> You add [10,20) with 50ms...
>
> and [30,40) with... what timeout?
>
> you wait how long?
>
> You follow up with another batch an you only delete 20?
>
> > Before: a GET for 45 finds an element, the end element 40 is gone and
> > deleting [30,40) fails with ENOENT. After: 45 is not found, 40 is
> > present and the delete succeeds.
> >
> > - commit path: add [30,40) and a start-only element 10 with a 50ms
> > timeout to a set with a 100ms gc interval, wait, then add [200,201),
> > whose insertion walk stops at the highest element, so only the
> > commit-time scan reaches 10. Before: the end element 40 is gone and
> > deleting [30,40) fails with ENOENT. After: both work.
> >
> > Normal expiry, and a new interval whose end element shares the key of an
> > expired start element (the case handled by 60c0c230c6f0), behave the same
> > before and after. About 39000 random batches on interval sets with
> > timeouts, with and without slab/page allocation fault injection, showed
> > no KASAN, lockdep or WARN reports with the patch applied.
Hm, no KASAN, no splat, what's the issue, really?
> > The insertion path issue was found with a small nfnetlink fuzzer. Both
> > paths had been pointed out earlier by the netdev AI review of commit
> > 86b6471e690c ("netfilter: nft_set_rbtree: skip transaction elements
> > during GC"), see the Link below.
> >
> > Fixes: f718863aca46 ("netfilter: nft_set_rbtree: fix overlap expiration walk")
> > Fixes: 8d8540c4f5e0 ("netfilter: nft_set_rbtree: add timeout support")
> > Link: https://lore.kernel.org/all/179063970372.3145.17414936987144600093@kernel.org/
> > Signed-off-by: Joas Antonio dos Santos <joasantonio108@gmail.com>
> > ---
> > Tool-use note: the fuzzer, the directed tests and the analysis were
> > prepared with help from an LLM-based assistant; I reviewed the change
> > and built and ran all tests myself. Test harness available on request.
> >
> > This is a resend through the kernel.org web endpoint: my earlier
> > submission of this patch was blocked by my mail provider and never
> > reached the lists.
> > ---
> > net/netfilter/nft_set_rbtree.c | 23 +++++++++++++++++++----
> > 1 file changed, 19 insertions(+), 4 deletions(-)
> >
> > diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c
> > index fbe1750f1..086823067 100644
> > --- a/net/netfilter/nft_set_rbtree.c
> > +++ b/net/netfilter/nft_set_rbtree.c
> > @@ -215,17 +215,25 @@ nft_rbtree_gc_elem(const struct nft_set *__set, struct nft_rbtree *priv,
> > struct nft_set *set = (struct nft_set *)__set;
> > struct rb_node *prev = rb_prev(&rbe->node);
> > struct net *net = read_pnet(&set->net);
> > + u8 cur_genmask = nft_genmask_cur(net);
> > struct nft_rbtree_elem *rbe_prev;
> >
> > /* search for end interval coming before this element.
> > * end intervals don't carry a timeout extension, they
> > * are coupled with the interval start element.
> > + *
> > + * Skip elements added in this transaction. The first element
> > + * that is active in the current generation is either the end
> > + * element of this interval or the start element of another
> > + * interval, in which case this interval has no end element.
> > */
> > while (prev) {
> > rbe_prev = rb_entry(prev, struct nft_rbtree_elem, node);
> > - if (nft_rbtree_interval_end(rbe_prev) &&
> > - nft_set_elem_active(&rbe_prev->ext, NFT_GENMASK_ANY))
> > + if (nft_set_elem_active(&rbe_prev->ext, cur_genmask)) {
> > + if (!nft_rbtree_interval_end(rbe_prev))
> > + prev = NULL;
> > break;
> > + }
> >
> > prev = rb_prev(prev);
> > }
> > @@ -233,7 +241,11 @@ nft_rbtree_gc_elem(const struct nft_set *__set, struct nft_rbtree *priv,
> > rbe_prev = NULL;
> > if (prev) {
> > rbe_prev = rb_entry(prev, struct nft_rbtree_elem, node);
> > - nft_rbtree_gc_elem_move(net, set, priv, rbe_prev);
> > + /* end element is being removed by this transaction */
> > + if (nft_set_elem_active(&rbe_prev->ext, NFT_GENMASK_ANY))
> > + nft_rbtree_gc_elem_move(net, set, priv, rbe_prev);
> > + else
> > + rbe_prev = NULL;
> > }
> >
> > nft_rbtree_gc_elem_move(net, set, priv, rbe);
> > @@ -911,8 +923,11 @@ static void nft_rbtree_gc_scan(struct nft_set *set)
> > rbe_end = rbe;
> > continue;
> > }
> > - if (!__nft_set_elem_expired(&rbe->ext, tstamp))
> > + if (!__nft_set_elem_expired(&rbe->ext, tstamp)) {
> > + /* rbe_end belongs to this live interval */
> > + rbe_end = NULL;
> > continue;
> > + }
> >
> > /* end element needs to be removed first, it has
> > * no timeout extension.
> >
> > ---
> > base-commit: c339f4010dea8b0d754f84ace70cd1b089878ef8
> > change-id: 20261031-b4-nft-rbtree-gc-1d9c72dba2bd
> >
> > Best regards,
> > --
> > Joas Antonio dos Santos <joasantonio108@gmail.com>
> >
> >
>
next prev parent reply other threads:[~2026-10-31 17:50 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-31 15:54 Joas Antonio dos Santos via B4 Relay
2026-10-31 17:44 ` Pablo Neira Ayuso
2026-10-31 17:50 ` Pablo Neira Ayuso [this message]
2026-10-31 17:55 ` Joas Antonio
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=auYqSpBFtC0nTDDd@chamomile \
--to=pablo@netfilter.org \
--cc=coreteam@netfilter.org \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=fw@strlen.de \
--cc=horms@kernel.org \
--cc=joasantonio108@gmail.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=netfilter-devel@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=phil@nwl.cc \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®