Skip to content

Commit d472e98

Browse files
committed
netfilter: nf_tables: register hooks last when adding new chain/flowtable
Register hooks last when adding chain/flowtable to ensure that packets do not walk over datastructure that is being released in the error path without waiting for the rcu grace period. Fixes: 91c7b38 ("netfilter: nf_tables: use new transaction infrastructure to handle chain") Fixes: 3b49e2e ("netfilter: nf_tables: add flow table netlink frontend") Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 8762785 commit d472e98

File tree

1 file changed

+40
-38
lines changed

1 file changed

+40
-38
lines changed

net/netfilter/nf_tables_api.c

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -684,15 +684,16 @@ static int nft_delobj(struct nft_ctx *ctx, struct nft_object *obj)
684684
return err;
685685
}
686686

687-
static int nft_trans_flowtable_add(struct nft_ctx *ctx, int msg_type,
688-
struct nft_flowtable *flowtable)
687+
static struct nft_trans *
688+
nft_trans_flowtable_add(struct nft_ctx *ctx, int msg_type,
689+
struct nft_flowtable *flowtable)
689690
{
690691
struct nft_trans *trans;
691692

692693
trans = nft_trans_alloc(ctx, msg_type,
693694
sizeof(struct nft_trans_flowtable));
694695
if (trans == NULL)
695-
return -ENOMEM;
696+
return ERR_PTR(-ENOMEM);
696697

697698
if (msg_type == NFT_MSG_NEWFLOWTABLE)
698699
nft_activate_next(ctx->net, flowtable);
@@ -701,22 +702,22 @@ static int nft_trans_flowtable_add(struct nft_ctx *ctx, int msg_type,
701702
nft_trans_flowtable(trans) = flowtable;
702703
nft_trans_commit_list_add_tail(ctx->net, trans);
703704

704-
return 0;
705+
return trans;
705706
}
706707

707708
static int nft_delflowtable(struct nft_ctx *ctx,
708709
struct nft_flowtable *flowtable)
709710
{
710-
int err;
711+
struct nft_trans *trans;
711712

712-
err = nft_trans_flowtable_add(ctx, NFT_MSG_DELFLOWTABLE, flowtable);
713-
if (err < 0)
714-
return err;
713+
trans = nft_trans_flowtable_add(ctx, NFT_MSG_DELFLOWTABLE, flowtable);
714+
if (IS_ERR(trans))
715+
return PTR_ERR(trans);
715716

716717
nft_deactivate_next(ctx->net, flowtable);
717718
nft_use_dec(&ctx->table->use);
718719

719-
return err;
720+
return 0;
720721
}
721722

722723
static void __nft_reg_track_clobber(struct nft_regs_track *track, u8 dreg)
@@ -2504,37 +2505,38 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
25042505
RCU_INIT_POINTER(chain->blob_gen_0, blob);
25052506
RCU_INIT_POINTER(chain->blob_gen_1, blob);
25062507

2507-
err = nf_tables_register_hook(net, table, chain);
2508-
if (err < 0)
2509-
goto err_destroy_chain;
2510-
25112508
if (!nft_use_inc(&table->use)) {
25122509
err = -EMFILE;
2513-
goto err_use;
2510+
goto err_destroy_chain;
25142511
}
25152512

25162513
trans = nft_trans_chain_add(ctx, NFT_MSG_NEWCHAIN);
25172514
if (IS_ERR(trans)) {
25182515
err = PTR_ERR(trans);
2519-
goto err_unregister_hook;
2516+
goto err_trans;
25202517
}
25212518

25222519
nft_trans_chain_policy(trans) = NFT_CHAIN_POLICY_UNSET;
25232520
if (nft_is_base_chain(chain))
25242521
nft_trans_chain_policy(trans) = policy;
25252522

25262523
err = nft_chain_add(table, chain);
2527-
if (err < 0) {
2528-
nft_trans_destroy(trans);
2529-
goto err_unregister_hook;
2530-
}
2524+
if (err < 0)
2525+
goto err_chain_add;
2526+
2527+
/* This must be LAST to ensure no packets are walking over this chain. */
2528+
err = nf_tables_register_hook(net, table, chain);
2529+
if (err < 0)
2530+
goto err_register_hook;
25312531

25322532
return 0;
25332533

2534-
err_unregister_hook:
2534+
err_register_hook:
2535+
nft_chain_del(chain);
2536+
err_chain_add:
2537+
nft_trans_destroy(trans);
2538+
err_trans:
25352539
nft_use_dec_restore(&table->use);
2536-
err_use:
2537-
nf_tables_unregister_hook(net, table, chain);
25382540
err_destroy_chain:
25392541
nf_tables_chain_destroy(ctx);
25402542

@@ -8456,9 +8458,9 @@ static int nf_tables_newflowtable(struct sk_buff *skb,
84568458
u8 family = info->nfmsg->nfgen_family;
84578459
const struct nf_flowtable_type *type;
84588460
struct nft_flowtable *flowtable;
8459-
struct nft_hook *hook, *next;
84608461
struct net *net = info->net;
84618462
struct nft_table *table;
8463+
struct nft_trans *trans;
84628464
struct nft_ctx ctx;
84638465
int err;
84648466

@@ -8538,34 +8540,34 @@ static int nf_tables_newflowtable(struct sk_buff *skb,
85388540
err = nft_flowtable_parse_hook(&ctx, nla, &flowtable_hook, flowtable,
85398541
extack, true);
85408542
if (err < 0)
8541-
goto err4;
8543+
goto err_flowtable_parse_hooks;
85428544

85438545
list_splice(&flowtable_hook.list, &flowtable->hook_list);
85448546
flowtable->data.priority = flowtable_hook.priority;
85458547
flowtable->hooknum = flowtable_hook.num;
85468548

8549+
trans = nft_trans_flowtable_add(&ctx, NFT_MSG_NEWFLOWTABLE, flowtable);
8550+
if (IS_ERR(trans)) {
8551+
err = PTR_ERR(trans);
8552+
goto err_flowtable_trans;
8553+
}
8554+
8555+
/* This must be LAST to ensure no packets are walking over this flowtable. */
85478556
err = nft_register_flowtable_net_hooks(ctx.net, table,
85488557
&flowtable->hook_list,
85498558
flowtable);
8550-
if (err < 0) {
8551-
nft_hooks_destroy(&flowtable->hook_list);
8552-
goto err4;
8553-
}
8554-
8555-
err = nft_trans_flowtable_add(&ctx, NFT_MSG_NEWFLOWTABLE, flowtable);
85568559
if (err < 0)
8557-
goto err5;
8560+
goto err_flowtable_hooks;
85588561

85598562
list_add_tail_rcu(&flowtable->list, &table->flowtables);
85608563

85618564
return 0;
8562-
err5:
8563-
list_for_each_entry_safe(hook, next, &flowtable->hook_list, list) {
8564-
nft_unregister_flowtable_hook(net, flowtable, hook);
8565-
list_del_rcu(&hook->list);
8566-
kfree_rcu(hook, rcu);
8567-
}
8568-
err4:
8565+
8566+
err_flowtable_hooks:
8567+
nft_trans_destroy(trans);
8568+
err_flowtable_trans:
8569+
nft_hooks_destroy(&flowtable->hook_list);
8570+
err_flowtable_parse_hooks:
85698571
flowtable->data.type->free(&flowtable->data);
85708572
err3:
85718573
module_put(type->owner);

0 commit comments

Comments
 (0)