mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:24:47 +01:00
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: 35d0ac9070 ("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>
This commit is contained in:
parent
b824c3e16c
commit
def602e498
1 changed files with 16 additions and 14 deletions
|
|
@ -7170,6 +7170,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;
|
||||
|
|
@ -7461,10 +7462,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);
|
||||
|
|
@ -7516,7 +7525,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7534,23 +7543,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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue