FAILED: patch "[PATCH] netfilter: nf_tables: skip expired catchall elements on" failed to apply to 5.15-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x 70194dc37670bd08e44b471389861cc01bd3a3c9
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026092923-masses-playroom-aefc@gregkh' --subject-prefix 'PATCH 5.15.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 70194dc37670bd08e44b471389861cc01bd3a3c9 Mon Sep 17 00:00:00 2001
From: Aohan Mei <[email protected]>
Date: Mon, 14 Sep 2026 19:51:47 +0800
Subject: [PATCH] netfilter: nf_tables: skip expired catchall elements on
insert and delete
nft_setelem_catchall_insert() looks up duplicates with nft_set_elem_active() only, while nft_set_catchall_lookup() and the dump path additionally skip expired elements.
Once a catchall element with a timeout expires, this predicate drift makes it invisible to userspace dumps, yet it still blocks
re-insertion: with NLM_F_EXCL the request fails with -EEXIST, and without it the request reports success but silently inserts nothing. The stale entry only goes away when the (user-tunable) gc interval elapses, so the catchall rule may silently stop matching for an arbitrarily long time after its first expiration.
The delete path shows the same drift: nft_setelem_catchall_deactivate() picks the first active-next entry in the catchall list, so with an expired entry still pending GC it retires the stale entry instead of the fresh one, and it deactivates an element that userspace no longer sees instead of failing with -ENOENT.
Align both walks with the lookup and dump predicates: only an element that is active and not expired counts as a duplicate or delete candidate, using the per-netns timestamp taken at transaction start, in line with the set backend .insert/.deactivate and catchall GC sync paths.
net/netfilter/nf_tables_api.c +8 -2
@@ -6995,11 +6995,14 @@ static int nft_setelem_catchall_insert(const struct net *net, {struct nft_set_elem_catchall *catchall;u8 genmask =nft_genmask_next (net); +u64 tstamp =nft_net_tstamp (net);struct nft_set_ext *ext;list_for_each_entry (catchall, &set->catchall_list, list) { ext =nft_set_elem_ext (set, catchall->elem); -if (nft_set_elem_active (ext, genmask)) { +if (nft_set_elem_active (ext, genmask) && + !__nft_set_elem_expired (ext, tstamp) && + !nft_set_elem_is_dead (ext)) { *priv = catchall->elem;return -EEXIST ; }
@@ -7092,11 +7095,14 @@ static int nft_setelem_catchall_deactivate(const struct net *net, struct nft_set_elem *elem) {struct nft_set_elem_catchall *catchall; +u64 tstamp =nft_net_tstamp (net);struct nft_set_ext *ext;list_for_each_entry (catchall, &set->catchall_list, list) { ext =nft_set_elem_ext (set, catchall->elem); -if (!nft_is_active_next (net, ext)) +if (!nft_is_active_next (net, ext) || +__nft_set_elem_expired (ext, tstamp) || +nft_set_elem_is_dead (ext))continue ;kfree (elem->priv);
Sasha Levin
maintainerPATCH 1/2 netfilter: nf_tables: set->ops->insert returns opaque set element in case of EEXIST
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 078996fcd657e6e0c3a72b7d5806d04c32e74250 ]
Return struct nft_elem_priv instead of struct nft_set_ext for consistency with ("netfilter: nf_tables: expose opaque set element as struct nft_elem_priv") and to prepare the introduction of element timeout updates from control path.
Backport adaptation for Linux 5.15:
This tree represents opaque set elements as void pointers and does not have struct nft_elem_priv, the backend priv members, or nft_elem_priv_cast(). Use void ** for the insert output parameter and return the existing backend element pointer directly. Keep the frontend conversion through nft_set_elem_ext() when insertion returns -EEXIST.
Resolve the pipapo conflict by retaining its existing initialization of e at declaration and returning e directly. Update the output parameter's documentation and omit the unrelated timeout declaration reordering. No functions or element layout changes are introduced.
This preserves the catchall insertion context needed for target commit 70194dc37670bd08e44b471389861cc01bd3a3c9, whose two hunks apply unchanged.
Stable-dep-of: 70194dc37670 ("netfilter: nf_tables: skip expired catchall elements on insert and delete")
--- include/net/netfilter/nf_tables.h | 2 +- net/netfilter/nf_tables_api.c | 15 +++++++++------ net/netfilter/nft_set_bitmap.c | 4 ++-- net/netfilter/nft_set_hash.c | 8 ++++---- net/netfilter/nft_set_pipapo.c | 10 +++++----- net/netfilter/nft_set_rbtree.c | 10 +++++----- 6 files changed, 26 insertions(+), 23 deletions(-)
include/net/netfilter/nf_tables.h +1 -1
@@ -435,7 +435,7 @@ struct nft_set_ops {int (*insert)(const struct net *net,const struct nft_set *set,const struct nft_set_elem *elem, -struct nft_set_ext **ext); +void **priv);void (*activate)(const struct net *net,const struct nft_set *set,const struct nft_set_elem *elem);
net/netfilter/nf_tables_api.c +9 -6
@@ -6075,7 +6075,7 @@ EXPORT_SYMBOL_GPL(nft_set_catchall_lookup);static int nft_setelem_catchall_insert (const struct net *net,struct nft_set *set,const struct nft_set_elem *elem, -struct nft_set_ext **pext) +void **priv) {struct nft_set_elem_catchall *catchall;u8 genmask =nft_genmask_next (net);
@@ -6084,7 +6084,7 @@ static int nft_setelem_catchall_insert(const struct net *net,list_for_each_entry (catchall, &set->catchall_list, list) { ext =nft_set_elem_ext (set, catchall->elem);if (nft_set_elem_active (ext, genmask)) { - *pext = ext; + *priv = catchall->elem;return -EEXIST ; } }
@@ -6102,14 +6102,15 @@ static int nft_setelem_catchall_insert(const struct net *net,static int nft_setelem_insert (const struct net *net,struct nft_set *set,const struct nft_set_elem *elem, -struct nft_set_ext **ext,unsigned int flags) +void **elem_priv, +unsigned int flags) {int ret;if (flags &NFT_SET_ELEM_CATCHALL ) - ret =nft_setelem_catchall_insert (net, set, elem, ext); + ret =nft_setelem_catchall_insert (net, set, elem, elem_priv);else - ret = set->ops->insert (net, set, elem, ext); + ret = set->ops->insert (net, set, elem, elem_priv);return ret; }
@@ -6255,6 +6256,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,struct nft_data_desc desc;enum nft_registers dreg;struct nft_trans *trans; +void *elem_priv;u64 timeout;u64 expiration;int err, i;
@@ -6549,9 +6551,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ext->genmask =nft_genmask_cur (ctx->net); - err =nft_setelem_insert (ctx->net, set, &elem, &ext2, flags); + err =nft_setelem_insert (ctx->net, set, &elem, &elem_priv, flags); if (err) { if (err == -EEXIST) { + ext2 = nft_set_elem_ext(set, elem_priv); if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) ^ nft_set_ext_exists(ext2, NFT_SET_EXT_DATA) || nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) ^
net/netfilter/nft_set_bitmap.c +2 -2
@@ -123,7 +123,7 @@ static void *nft_bitmap_get(const struct net *net, const struct nft_set *set,static int nft_bitmap_insert (const struct net *net,const struct nft_set *set,const struct nft_set_elem *elem, -struct nft_set_ext **ext) +void **elem_priv) {struct nft_bitmap *priv =nft_set_priv (set);struct nft_bitmap_elem *new = elem->priv, *be;
@@ -132,7 +132,7 @@ static int nft_bitmap_insert(const struct net *net, const struct nft_set *set, be =nft_bitmap_elem_find (set, new, genmask);if (be) { - *ext = &be->ext; + *elem_priv = be;return -EEXIST ; }
net/netfilter/nft_set_hash.c +4 -4
@@ -168,7 +168,7 @@ static bool nft_rhash_update(struct nft_set *set, const u32 *key,static int nft_rhash_insert (const struct net *net,const struct nft_set *set,const struct nft_set_elem *elem, -struct nft_set_ext **ext) +void **elem_priv) {struct nft_rhash *priv =nft_set_priv (set);struct nft_rhash_elem *he = elem->priv;
@@ -185,7 +185,7 @@ static int nft_rhash_insert(const struct net *net, const struct nft_set *set,if (IS_ERR (prev))return PTR_ERR (prev);if (prev) { - *ext = &prev->ext; + *elem_priv = prev;return -EEXIST ; }return 0 ;
@@ -588,7 +588,7 @@ static u32 nft_jhash(const struct nft_set *set, const struct nft_hash *priv,static int nft_hash_insert (const struct net *net,const struct nft_set *set,const struct nft_set_elem *elem, -struct nft_set_ext **ext) +void **elem_priv) {struct nft_hash_elem *this = elem->priv, *he;struct nft_hash *priv =nft_set_priv (set);
@@ -600,7 +600,7 @@ static int nft_hash_insert(const struct net *net, const struct nft_set *set,if (!memcmp (nft_set_ext_key (&this->ext),nft_set_ext_key (&he->ext), set->klen) &&nft_set_elem_active (&he->ext, genmask)) { - *ext = &he->ext; + *elem_priv = he;return -EEXIST ; } }
net/netfilter/nft_set_pipapo.c +5 -5
@@ -1233,13 +1233,13 @@ static struct nft_pipapo_match *pipapo_maybe_clone(const struct nft_set *set)* @net: Network namespace * @set: nftables API set representation * @elem: nftables API element representation containing key data -* @ext2: Filled with pointer to &struct nft_set_ext in inserted element +* @elem_priv: Filled with private pointer to inserted or existing element * * Return: 0 on success, error pointer on failure. */ static int nft_pipapo_insert (const struct net *net,const struct nft_set *set,const struct nft_set_elem *elem, -struct nft_set_ext **ext2) +void **elem_priv) {const struct nft_set_ext *ext =nft_set_elem_ext (set, elem->priv);union nft_pipapo_map_bucket rulemap[NFT_PIPAPO_MAX_FIELDS ];
@@ -1274,7 +1274,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,if (!memcmp (start, dup_key->data,sizeof (*dup_key->data)) && !memcmp (end, dup_end->data,sizeof (*dup_end->data))) { - *ext2 = &dup->ext; + *elem_priv = dup;return -EEXIST ; }
@@ -1290,7 +1290,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,if (PTR_ERR (dup) != -ENOENT ) {if (IS_ERR (dup))return PTR_ERR (dup); - *ext2 = &dup->ext; + *elem_priv = dup;return -ENOTEMPTY ; }
@@ -1352,7 +1352,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,put_cpu_ptr (m->scratch); } - *ext2 = &e->ext; + *elem_priv = e;pipapo_map (m, rulemap, e);
net/netfilter/nft_set_rbtree.c +5 -5
@@ -319,7 +319,7 @@ static struct nft_rbtree_elem *nft_rbtree_prev_active(struct nft_rbtree_elem *rbstatic int __nft_rbtree_insert (const struct net *net,const struct nft_set *set,struct nft_rbtree_elem *new, -struct nft_set_ext **ext) +void **elem_priv) {struct nft_rbtree_elem *rbe, *rbe_le =NULL , *rbe_ge =NULL , *rbe_prev;struct rb_node *node, *next, *parent, **p, *first =NULL ;
@@ -437,7 +437,7 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,*/ if (rbe_ge && !nft_rbtree_cmp (set, new, rbe_ge) &&nft_rbtree_interval_start (rbe_ge) ==nft_rbtree_interval_start (new)) { - *ext = &rbe_ge->ext; + *elem_priv = rbe_ge;return -EEXIST ; }
@@ -446,7 +446,7 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,*/ if (rbe_le && !nft_rbtree_cmp (set, new, rbe_le) &&nft_rbtree_interval_end (rbe_le) ==nft_rbtree_interval_end (new)) { - *ext = &rbe_le->ext; + *elem_priv = rbe_le;return -EEXIST ; }
@@ -506,7 +506,7 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,static int nft_rbtree_insert (const struct net *net,const struct nft_set *set,const struct nft_set_elem *elem, -struct nft_set_ext **ext) +void **elem_priv) {struct nft_rbtree *priv =nft_set_priv (set);struct nft_rbtree_elem *rbe = elem->priv;
@@ -520,7 +520,7 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set,write_lock_bh (&priv->lock);write_seqcount_begin (&priv->count); - err =__nft_rbtree_insert (net, set, rbe, ext); + err =__nft_rbtree_insert (net, set, rbe, elem_priv);write_seqcount_end (&priv->count);write_unlock_bh (&priv->lock); }while (err == -EAGAIN );
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 netfilter: nf_tables: skip expired catchall elements on insert and delete
From: Aohan Mei <[email protected]>
[ Upstream commit 70194dc37670bd08e44b471389861cc01bd3a3c9 ]
nft_setelem_catchall_insert() looks up duplicates with nft_set_elem_active() only, while nft_set_catchall_lookup() and the dump path additionally skip expired elements.
Once a catchall element with a timeout expires, this predicate drift makes it invisible to userspace dumps, yet it still blocks
re-insertion: with NLM_F_EXCL the request fails with -EEXIST, and without it the request reports success but silently inserts nothing. The stale entry only goes away when the (user-tunable) gc interval elapses, so the catchall rule may silently stop matching for an arbitrarily long time after its first expiration.
The delete path shows the same drift: nft_setelem_catchall_deactivate() picks the first active-next entry in the catchall list, so with an expired entry still pending GC it retires the stale entry instead of the fresh one, and it deactivates an element that userspace no longer sees instead of failing with -ENOENT.
Align both walks with the lookup and dump predicates: only an element that is active and not expired counts as a duplicate or delete candidate, using the per-netns timestamp taken at transaction start, in line with the set backend .insert/.deactivate and catchall GC sync paths.
--- net/netfilter/nf_tables_api.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
net/netfilter/nf_tables_api.c +8 -2
@@ -6079,11 +6079,14 @@ static int nft_setelem_catchall_insert(const struct net *net, {struct nft_set_elem_catchall *catchall;u8 genmask =nft_genmask_next (net); +u64 tstamp =nft_net_tstamp (net);struct nft_set_ext *ext;list_for_each_entry (catchall, &set->catchall_list, list) { ext =nft_set_elem_ext (set, catchall->elem); -if (nft_set_elem_active (ext, genmask)) { +if (nft_set_elem_active (ext, genmask) && + !__nft_set_elem_expired (ext, tstamp) && + !nft_set_elem_is_dead (ext)) { *priv = catchall->elem;return -EEXIST ; }
@@ -6144,11 +6147,14 @@ static int nft_setelem_catchall_deactivate(const struct net *net, struct nft_set_elem *elem) {struct nft_set_elem_catchall *catchall; +u64 tstamp =nft_net_tstamp (net);struct nft_set_ext *ext;list_for_each_entry (catchall, &set->catchall_list, list) { ext =nft_set_elem_ext (set, catchall->elem); -if (!nft_is_active_next (net, ext)) +if (!nft_is_active_next (net, ext) || +__nft_set_elem_expired (ext, tstamp) || +nft_set_elem_is_dead (ext))continue ;kfree (elem->priv);
Signature · 1 line
2.53.0