Skip to content

Commit 1e9451c

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: nf_tables: fix nat hook table deletion
sybot came up with following transaction: add table ip syz0 add chain ip syz0 syz2 { type nat hook prerouting priority 0; policy accept; } add table ip syz0 { flags dormant; } delete chain ip syz0 syz2 delete table ip syz0 which yields: hook not found, pf 2 num 0 WARNING: CPU: 0 PID: 6775 at net/netfilter/core.c:413 __nf_unregister_net_hook+0x3e6/0x4a0 net/netfilter/core.c:413 [..] nft_unregister_basechain_hooks net/netfilter/nf_tables_api.c:206 [inline] nft_table_disable net/netfilter/nf_tables_api.c:835 [inline] nf_tables_table_disable net/netfilter/nf_tables_api.c:868 [inline] nf_tables_commit+0x32d3/0x4d70 net/netfilter/nf_tables_api.c:7550 nfnetlink_rcv_batch net/netfilter/nfnetlink.c:486 [inline] nfnetlink_rcv_skb_batch net/netfilter/nfnetlink.c:544 [inline] nfnetlink_rcv+0x14a5/0x1e50 net/netfilter/nfnetlink.c:562 netlink_unicast_kernel net/netlink/af_netlink.c:1303 [inline] Problem is that when I added ability to override base hook registration to make nat basechains register with the nat core instead of netfilter core, I forgot to update nft_table_disable() to use that instead of the 'raw' hook register interface. In syzbot transaction, the basechain is of 'nat' type. Its registered with the nat core. The switch to 'dormant mode' attempts to delete from netfilter core instead. After updating nft_table_disable/enable to use the correct helper, nft_(un)register_basechain_hooks can be folded into the only remaining caller. Because nft_trans_table_enable() won't do anything when the DORMANT flag is set, remove the flag first, then re-add it in case re-enablement fails, else this patch breaks sequence: add table ip x { flags dormant; } /* add base chains */ add table ip x The last 'add' will remove the dormant flags, but won't have any other effect -- base chains are not registered. Then, next 'set dormant flag' will create another 'hook not found' splat. Reported-by: [email protected] Fixes: 4e25ceb ("netfilter: nf_tables: allow chain type to override hook register") Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 1d61e21 commit 1e9451c

File tree

1 file changed

+14
-27
lines changed

1 file changed

+14
-27
lines changed

net/netfilter/nf_tables_api.c

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -188,24 +188,6 @@ static void nft_netdev_unregister_hooks(struct net *net,
188188
nf_unregister_net_hook(net, &hook->ops);
189189
}
190190

191-
static int nft_register_basechain_hooks(struct net *net, int family,
192-
struct nft_base_chain *basechain)
193-
{
194-
if (family == NFPROTO_NETDEV)
195-
return nft_netdev_register_hooks(net, &basechain->hook_list);
196-
197-
return nf_register_net_hook(net, &basechain->ops);
198-
}
199-
200-
static void nft_unregister_basechain_hooks(struct net *net, int family,
201-
struct nft_base_chain *basechain)
202-
{
203-
if (family == NFPROTO_NETDEV)
204-
nft_netdev_unregister_hooks(net, &basechain->hook_list);
205-
else
206-
nf_unregister_net_hook(net, &basechain->ops);
207-
}
208-
209191
static int nf_tables_register_hook(struct net *net,
210192
const struct nft_table *table,
211193
struct nft_chain *chain)
@@ -223,7 +205,10 @@ static int nf_tables_register_hook(struct net *net,
223205
if (basechain->type->ops_register)
224206
return basechain->type->ops_register(net, ops);
225207

226-
return nft_register_basechain_hooks(net, table->family, basechain);
208+
if (table->family == NFPROTO_NETDEV)
209+
return nft_netdev_register_hooks(net, &basechain->hook_list);
210+
211+
return nf_register_net_hook(net, &basechain->ops);
227212
}
228213

229214
static void nf_tables_unregister_hook(struct net *net,
@@ -242,7 +227,10 @@ static void nf_tables_unregister_hook(struct net *net,
242227
if (basechain->type->ops_unregister)
243228
return basechain->type->ops_unregister(net, ops);
244229

245-
nft_unregister_basechain_hooks(net, table->family, basechain);
230+
if (table->family == NFPROTO_NETDEV)
231+
nft_netdev_unregister_hooks(net, &basechain->hook_list);
232+
else
233+
nf_unregister_net_hook(net, &basechain->ops);
246234
}
247235

248236
static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
@@ -832,8 +820,7 @@ static void nft_table_disable(struct net *net, struct nft_table *table, u32 cnt)
832820
if (cnt && i++ == cnt)
833821
break;
834822

835-
nft_unregister_basechain_hooks(net, table->family,
836-
nft_base_chain(chain));
823+
nf_tables_unregister_hook(net, table, chain);
837824
}
838825
}
839826

@@ -848,8 +835,7 @@ static int nf_tables_table_enable(struct net *net, struct nft_table *table)
848835
if (!nft_is_base_chain(chain))
849836
continue;
850837

851-
err = nft_register_basechain_hooks(net, table->family,
852-
nft_base_chain(chain));
838+
err = nf_tables_register_hook(net, table, chain);
853839
if (err < 0)
854840
goto err_register_hooks;
855841

@@ -894,11 +880,12 @@ static int nf_tables_updtable(struct nft_ctx *ctx)
894880
nft_trans_table_enable(trans) = false;
895881
} else if (!(flags & NFT_TABLE_F_DORMANT) &&
896882
ctx->table->flags & NFT_TABLE_F_DORMANT) {
883+
ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
897884
ret = nf_tables_table_enable(ctx->net, ctx->table);
898-
if (ret >= 0) {
899-
ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
885+
if (ret >= 0)
900886
nft_trans_table_enable(trans) = true;
901-
}
887+
else
888+
ctx->table->flags |= NFT_TABLE_F_DORMANT;
902889
}
903890
if (ret < 0)
904891
goto err;

0 commit comments

Comments
 (0)