diff options
author | Liping Zhang <zlpnobody@gmail.com> | 2016-10-22 13:51:26 +0300 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2016-10-27 19:22:02 +0300 |
commit | dab45060a56a9732b027d2031c1b6100bc75eea2 (patch) | |
tree | 6eb28be6f001aac951f25ccf471d5ff21dfe9ada /net/netfilter | |
parent | 61f9e2924f4981d626b3a931fed935f2fa3cb4de (diff) | |
download | linux-dab45060a56a9732b027d2031c1b6100bc75eea2.tar.xz |
netfilter: nf_tables: fix race when create new element in dynset
Packets may race when create the new element in nft_hash_update:
CPU0 CPU1
lookup_fast - fail lookup_fast - fail
new - ok new - ok
insert - ok insert - fail(EEXIST)
So when race happened, we reuse the existing element. Otherwise,
these *racing* packets will not be handled properly.
Fixes: 22fe54d5fefc ("netfilter: nf_tables: add support for dynamic set updates")
Signed-off-by: Liping Zhang <zlpnobody@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net/netfilter')
-rw-r--r-- | net/netfilter/nft_set_hash.c | 15 |
1 files changed, 12 insertions, 3 deletions
diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c index 88d9fc8343e7..a3dface3e6e6 100644 --- a/net/netfilter/nft_set_hash.c +++ b/net/netfilter/nft_set_hash.c @@ -98,7 +98,7 @@ static bool nft_hash_update(struct nft_set *set, const u32 *key, const struct nft_set_ext **ext) { struct nft_hash *priv = nft_set_priv(set); - struct nft_hash_elem *he; + struct nft_hash_elem *he, *prev; struct nft_hash_cmp_arg arg = { .genmask = NFT_GENMASK_ANY, .set = set, @@ -112,9 +112,18 @@ static bool nft_hash_update(struct nft_set *set, const u32 *key, he = new(set, expr, regs); if (he == NULL) goto err1; - if (rhashtable_lookup_insert_key(&priv->ht, &arg, &he->node, - nft_hash_params)) + + prev = rhashtable_lookup_get_insert_key(&priv->ht, &arg, &he->node, + nft_hash_params); + if (IS_ERR(prev)) goto err2; + + /* Another cpu may race to insert the element with the same key */ + if (prev) { + nft_set_elem_destroy(set, he, true); + he = prev; + } + out: *ext = &he->ext; return true; |