107 lines
3.4 KiB
Diff
107 lines
3.4 KiB
Diff
From 573d852abb5341047873d1257065ea6ad9174da4 Mon Sep 17 00:00:00 2001
|
|
From: Florian Westphal <fwestpha@redhat.com>
|
|
Date: Wed, 13 May 2026 17:15:52 +0200
|
|
Subject: [PATCH] netfilter: nf_tables: unconditionally bump set->nelems before
|
|
insertion
|
|
|
|
JIRA: https://redhat.atlassian.net/browse/RHEL-168848
|
|
Upstream Status: commit def602e498a4
|
|
|
|
commit def602e498a4f951da95c95b1b8ce8ae68aa733a
|
|
Author: Pablo Neira Ayuso <pablo@netfilter.org>
|
|
Date: Mon Mar 2 23:12:37 2026 +0100
|
|
|
|
netfilter: nf_tables: unconditionally bump set->nelems before insertion
|
|
|
|
In case that the set is full, a new element gets published then removed
|
|
without waiting for the RCU grace period, while RCU reader can be
|
|
walking over it already.
|
|
|
|
To address this issue, add the element transaction even if set is full,
|
|
but toggle the set_full flag to report -ENFILE so the abort path safely
|
|
unwinds the set to its previous state.
|
|
|
|
As for element updates, decrement set->nelems to restore it.
|
|
|
|
A simpler fix is to call synchronize_rcu() in the error path.
|
|
However, with a large batch adding elements to already maxed-out set,
|
|
this could cause noticeable slowdown of such batches.
|
|
|
|
Fixes: 35d0ac9070ef ("netfilter: nf_tables: fix set->nelems counting with no NLM_F_EXCL")
|
|
Reported-by: Inseo An <y0un9sa@gmail.com>
|
|
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
|
Signed-off-by: Florian Westphal <fw@strlen.de>
|
|
|
|
Signed-off-by: Florian Westphal <fwestpha@redhat.com>
|
|
|
|
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
|
|
index 55434ea..f2d6ac3 100644
|
|
--- a/net/netfilter/nf_tables_api.c
|
|
+++ b/net/netfilter/nf_tables_api.c
|
|
@@ -7281,6 +7281,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;
|
|
+ bool set_full = false;
|
|
u64 expiration;
|
|
u64 timeout;
|
|
int err, i;
|
|
@@ -7567,10 +7568,18 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
|
if (err < 0)
|
|
goto err_elem_free;
|
|
|
|
+ if (!(flags & NFT_SET_ELEM_CATCHALL)) {
|
|
+ unsigned int max = nft_set_maxsize(set), nelems;
|
|
+
|
|
+ nelems = atomic_inc_return(&set->nelems);
|
|
+ if (nelems > max)
|
|
+ set_full = true;
|
|
+ }
|
|
+
|
|
trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
|
|
if (trans == NULL) {
|
|
err = -ENOMEM;
|
|
- goto err_elem_free;
|
|
+ goto err_set_size;
|
|
}
|
|
|
|
ext->genmask = nft_genmask_cur(ctx->net);
|
|
@@ -7622,7 +7631,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
|
|
|
ue->priv = elem_priv;
|
|
nft_trans_commit_list_add_elem(ctx->net, trans);
|
|
- goto err_elem_free;
|
|
+ goto err_set_size;
|
|
}
|
|
}
|
|
}
|
|
@@ -7635,23 +7644,16 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
|
goto err_element_clash;
|
|
}
|
|
|
|
- if (!(flags & NFT_SET_ELEM_CATCHALL)) {
|
|
- unsigned int max = nft_set_maxsize(set);
|
|
-
|
|
- if (!atomic_add_unless(&set->nelems, 1, max)) {
|
|
- err = -ENFILE;
|
|
- goto err_set_full;
|
|
- }
|
|
- }
|
|
-
|
|
nft_trans_container_elem(trans)->elems[0].priv = elem.priv;
|
|
nft_trans_commit_list_add_elem(ctx->net, trans);
|
|
- return 0;
|
|
|
|
-err_set_full:
|
|
- nft_setelem_remove(ctx->net, set, elem.priv);
|
|
+ return set_full ? -ENFILE : 0;
|
|
+
|
|
err_element_clash:
|
|
kfree(trans);
|
|
+err_set_size:
|
|
+ if (!(flags & NFT_SET_ELEM_CATCHALL))
|
|
+ atomic_dec(&set->nelems);
|
|
err_elem_free:
|
|
nf_tables_set_elem_destroy(ctx, set, elem.priv);
|
|
err_parse_data:
|