summaryrefslogtreecommitdiffstats
path: root/net/netfilter/nft_set_hash.c
diff options
context:
space:
mode:
authorLiping Zhang2016-10-22 12:51:26 +0200
committerPablo Neira Ayuso2016-10-27 18:22:02 +0200
commitdab45060a56a9732b027d2031c1b6100bc75eea2 (patch)
tree6eb28be6f001aac951f25ccf471d5ff21dfe9ada /net/netfilter/nft_set_hash.c
parentnetfilter: nf_tables: fix *leak* when expr clone fail (diff)
downloadkernel-qcow2-linux-dab45060a56a9732b027d2031c1b6100bc75eea2.tar.gz
kernel-qcow2-linux-dab45060a56a9732b027d2031c1b6100bc75eea2.tar.xz
kernel-qcow2-linux-dab45060a56a9732b027d2031c1b6100bc75eea2.zip
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/nft_set_hash.c')
-rw-r--r--net/netfilter/nft_set_hash.c15
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;