]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
netfilter: nf_tables: reject duplicate device on updates
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 17 Nov 2025 21:39:58 +0000 (21:39 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 6 Dec 2025 21:12:34 +0000 (06:12 +0900)
commit cf5fb87fcdaaaafec55dcc0dc5a9e15ead343973 upstream.

A chain/flowtable update with duplicated devices in the same batch is
possible. Unfortunately, netdev event path only removes the first
device that is found, leaving unregistered the hook of the duplicated
device.

Check if a duplicated device exists in the transaction batch, bail out
with EEXIST in such case.

WARNING is hit when unregistering the hook:

 [49042.221275] WARNING: CPU: 4 PID: 8425 at net/netfilter/core.c:340 nf_hook_entry_head+0xaa/0x150
 [49042.221375] CPU: 4 UID: 0 PID: 8425 Comm: nft Tainted: G S                  6.16.0+ #170 PREEMPT(full)
 [...]
 [49042.221382] RIP: 0010:nf_hook_entry_head+0xaa/0x150

Fixes: 78d9f48f7f44 ("netfilter: nf_tables: add devices to existing flowtable")
Fixes: b9703ed44ffb ("netfilter: nf_tables: support for adding new devices to an existing netdev chain")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Sasha Levin <sashal@kernel.org>
net/netfilter/nf_tables_api.c

index df83224bef06ceff8bfee0cc9696fe95a2e872bc..b278f493cc93c9e06b5b3fbaca0a06394775cf8c 100644 (file)
@@ -8078,6 +8078,7 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
 {
        const struct nlattr * const *nla = ctx->nla;
        struct nft_flowtable_hook flowtable_hook;
+       struct nftables_pernet *nft_net;
        struct nft_hook *hook, *next;
        struct nft_trans *trans;
        bool unregister = false;
@@ -8093,6 +8094,20 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
                if (nft_hook_list_find(&flowtable->hook_list, hook)) {
                        list_del(&hook->list);
                        kfree(hook);
+                       continue;
+               }
+
+               nft_net = nft_pernet(ctx->net);
+               list_for_each_entry(trans, &nft_net->commit_list, list) {
+                       if (trans->msg_type != NFT_MSG_NEWFLOWTABLE ||
+                           trans->ctx.table != ctx->table ||
+                           !nft_trans_flowtable_update(trans))
+                               continue;
+
+                       if (nft_hook_list_find(&nft_trans_flowtable_hooks(trans), hook)) {
+                               err = -EEXIST;
+                               goto err_flowtable_update_hook;
+                       }
                }
        }