Skip to content

Commit e688a7f

Browse files
committed
netfilter: nf_tables: safe RCU iteration on list when dumping
The dump operation through netlink is not protected by the nfnl_lock. Thus, a reader process can be dumping any of the existing object lists while another process can be updating the list content. This patch resolves this situation by protecting all the object lists with RCU in the netlink dump path which is the reader side. The updater path is already protected via nfnl_lock, so use list manipulation RCU-safe operations. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent 63283dd commit e688a7f

File tree

1 file changed

+53
-41
lines changed

1 file changed

+53
-41
lines changed

net/netfilter/nf_tables_api.c

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ int nft_register_afinfo(struct net *net, struct nft_af_info *afi)
3535
{
3636
INIT_LIST_HEAD(&afi->tables);
3737
nfnl_lock(NFNL_SUBSYS_NFTABLES);
38-
list_add_tail(&afi->list, &net->nft.af_info);
38+
list_add_tail_rcu(&afi->list, &net->nft.af_info);
3939
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
4040
return 0;
4141
}
@@ -51,7 +51,7 @@ EXPORT_SYMBOL_GPL(nft_register_afinfo);
5151
void nft_unregister_afinfo(struct nft_af_info *afi)
5252
{
5353
nfnl_lock(NFNL_SUBSYS_NFTABLES);
54-
list_del(&afi->list);
54+
list_del_rcu(&afi->list);
5555
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
5656
}
5757
EXPORT_SYMBOL_GPL(nft_unregister_afinfo);
@@ -277,11 +277,12 @@ static int nf_tables_dump_tables(struct sk_buff *skb,
277277
struct net *net = sock_net(skb->sk);
278278
int family = nfmsg->nfgen_family;
279279

280-
list_for_each_entry(afi, &net->nft.af_info, list) {
280+
rcu_read_lock();
281+
list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
281282
if (family != NFPROTO_UNSPEC && family != afi->family)
282283
continue;
283284

284-
list_for_each_entry(table, &afi->tables, list) {
285+
list_for_each_entry_rcu(table, &afi->tables, list) {
285286
if (idx < s_idx)
286287
goto cont;
287288
if (idx > s_idx)
@@ -299,6 +300,7 @@ static int nf_tables_dump_tables(struct sk_buff *skb,
299300
}
300301
}
301302
done:
303+
rcu_read_unlock();
302304
cb->args[0] = idx;
303305
return skb->len;
304306
}
@@ -517,7 +519,7 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
517519
module_put(afi->owner);
518520
return err;
519521
}
520-
list_add_tail(&table->list, &afi->tables);
522+
list_add_tail_rcu(&table->list, &afi->tables);
521523
return 0;
522524
}
523525

@@ -549,7 +551,7 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
549551
if (err < 0)
550552
return err;
551553

552-
list_del(&table->list);
554+
list_del_rcu(&table->list);
553555
return 0;
554556
}
555557

@@ -764,12 +766,13 @@ static int nf_tables_dump_chains(struct sk_buff *skb,
764766
struct net *net = sock_net(skb->sk);
765767
int family = nfmsg->nfgen_family;
766768

767-
list_for_each_entry(afi, &net->nft.af_info, list) {
769+
rcu_read_lock();
770+
list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
768771
if (family != NFPROTO_UNSPEC && family != afi->family)
769772
continue;
770773

771-
list_for_each_entry(table, &afi->tables, list) {
772-
list_for_each_entry(chain, &table->chains, list) {
774+
list_for_each_entry_rcu(table, &afi->tables, list) {
775+
list_for_each_entry_rcu(chain, &table->chains, list) {
773776
if (idx < s_idx)
774777
goto cont;
775778
if (idx > s_idx)
@@ -787,11 +790,11 @@ static int nf_tables_dump_chains(struct sk_buff *skb,
787790
}
788791
}
789792
done:
793+
rcu_read_unlock();
790794
cb->args[0] = idx;
791795
return skb->len;
792796
}
793797

794-
795798
static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
796799
const struct nlmsghdr *nlh,
797800
const struct nlattr * const nla[])
@@ -1133,7 +1136,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
11331136
goto err2;
11341137

11351138
table->use++;
1136-
list_add_tail(&chain->list, &table->chains);
1139+
list_add_tail_rcu(&chain->list, &table->chains);
11371140
return 0;
11381141
err2:
11391142
if (!(table->flags & NFT_TABLE_F_DORMANT) &&
@@ -1183,7 +1186,7 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
11831186
return err;
11841187

11851188
table->use--;
1186-
list_del(&chain->list);
1189+
list_del_rcu(&chain->list);
11871190
return 0;
11881191
}
11891192

@@ -1202,9 +1205,9 @@ int nft_register_expr(struct nft_expr_type *type)
12021205
{
12031206
nfnl_lock(NFNL_SUBSYS_NFTABLES);
12041207
if (type->family == NFPROTO_UNSPEC)
1205-
list_add_tail(&type->list, &nf_tables_expressions);
1208+
list_add_tail_rcu(&type->list, &nf_tables_expressions);
12061209
else
1207-
list_add(&type->list, &nf_tables_expressions);
1210+
list_add_rcu(&type->list, &nf_tables_expressions);
12081211
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
12091212
return 0;
12101213
}
@@ -1219,7 +1222,7 @@ EXPORT_SYMBOL_GPL(nft_register_expr);
12191222
void nft_unregister_expr(struct nft_expr_type *type)
12201223
{
12211224
nfnl_lock(NFNL_SUBSYS_NFTABLES);
1222-
list_del(&type->list);
1225+
list_del_rcu(&type->list);
12231226
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
12241227
}
12251228
EXPORT_SYMBOL_GPL(nft_unregister_expr);
@@ -1555,13 +1558,14 @@ static int nf_tables_dump_rules(struct sk_buff *skb,
15551558
u8 genctr = ACCESS_ONCE(net->nft.genctr);
15561559
u8 gencursor = ACCESS_ONCE(net->nft.gencursor);
15571560

1558-
list_for_each_entry(afi, &net->nft.af_info, list) {
1561+
rcu_read_lock();
1562+
list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
15591563
if (family != NFPROTO_UNSPEC && family != afi->family)
15601564
continue;
15611565

1562-
list_for_each_entry(table, &afi->tables, list) {
1563-
list_for_each_entry(chain, &table->chains, list) {
1564-
list_for_each_entry(rule, &chain->rules, list) {
1566+
list_for_each_entry_rcu(table, &afi->tables, list) {
1567+
list_for_each_entry_rcu(chain, &table->chains, list) {
1568+
list_for_each_entry_rcu(rule, &chain->rules, list) {
15651569
if (!nft_rule_is_active(net, rule))
15661570
goto cont;
15671571
if (idx < s_idx)
@@ -1582,6 +1586,8 @@ static int nf_tables_dump_rules(struct sk_buff *skb,
15821586
}
15831587
}
15841588
done:
1589+
rcu_read_unlock();
1590+
15851591
/* Invalidate this dump, a transition to the new generation happened */
15861592
if (gencursor != net->nft.gencursor || genctr != net->nft.genctr)
15871593
return -EBUSY;
@@ -1935,7 +1941,7 @@ static LIST_HEAD(nf_tables_set_ops);
19351941
int nft_register_set(struct nft_set_ops *ops)
19361942
{
19371943
nfnl_lock(NFNL_SUBSYS_NFTABLES);
1938-
list_add_tail(&ops->list, &nf_tables_set_ops);
1944+
list_add_tail_rcu(&ops->list, &nf_tables_set_ops);
19391945
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
19401946
return 0;
19411947
}
@@ -1944,7 +1950,7 @@ EXPORT_SYMBOL_GPL(nft_register_set);
19441950
void nft_unregister_set(struct nft_set_ops *ops)
19451951
{
19461952
nfnl_lock(NFNL_SUBSYS_NFTABLES);
1947-
list_del(&ops->list);
1953+
list_del_rcu(&ops->list);
19481954
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
19491955
}
19501956
EXPORT_SYMBOL_GPL(nft_unregister_set);
@@ -2237,7 +2243,8 @@ static int nf_tables_dump_sets_table(struct nft_ctx *ctx, struct sk_buff *skb,
22372243
if (cb->args[1])
22382244
return skb->len;
22392245

2240-
list_for_each_entry(set, &ctx->table->sets, list) {
2246+
rcu_read_lock();
2247+
list_for_each_entry_rcu(set, &ctx->table->sets, list) {
22412248
if (idx < s_idx)
22422249
goto cont;
22432250
if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET,
@@ -2250,6 +2257,7 @@ static int nf_tables_dump_sets_table(struct nft_ctx *ctx, struct sk_buff *skb,
22502257
}
22512258
cb->args[1] = 1;
22522259
done:
2260+
rcu_read_unlock();
22532261
return skb->len;
22542262
}
22552263

@@ -2263,7 +2271,8 @@ static int nf_tables_dump_sets_family(struct nft_ctx *ctx, struct sk_buff *skb,
22632271
if (cb->args[1])
22642272
return skb->len;
22652273

2266-
list_for_each_entry(table, &ctx->afi->tables, list) {
2274+
rcu_read_lock();
2275+
list_for_each_entry_rcu(table, &ctx->afi->tables, list) {
22672276
if (cur_table) {
22682277
if (cur_table != table)
22692278
continue;
@@ -2272,7 +2281,7 @@ static int nf_tables_dump_sets_family(struct nft_ctx *ctx, struct sk_buff *skb,
22722281
}
22732282
ctx->table = table;
22742283
idx = 0;
2275-
list_for_each_entry(set, &ctx->table->sets, list) {
2284+
list_for_each_entry_rcu(set, &ctx->table->sets, list) {
22762285
if (idx < s_idx)
22772286
goto cont;
22782287
if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET,
@@ -2287,6 +2296,7 @@ static int nf_tables_dump_sets_family(struct nft_ctx *ctx, struct sk_buff *skb,
22872296
}
22882297
cb->args[1] = 1;
22892298
done:
2299+
rcu_read_unlock();
22902300
return skb->len;
22912301
}
22922302

@@ -2303,15 +2313,16 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
23032313
if (cb->args[1])
23042314
return skb->len;
23052315

2306-
list_for_each_entry(afi, &net->nft.af_info, list) {
2316+
rcu_read_lock();
2317+
list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
23072318
if (cur_family) {
23082319
if (afi->family != cur_family)
23092320
continue;
23102321

23112322
cur_family = 0;
23122323
}
23132324

2314-
list_for_each_entry(table, &afi->tables, list) {
2325+
list_for_each_entry_rcu(table, &afi->tables, list) {
23152326
if (cur_table) {
23162327
if (cur_table != table)
23172328
continue;
@@ -2322,7 +2333,7 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
23222333
ctx->table = table;
23232334
ctx->afi = afi;
23242335
idx = 0;
2325-
list_for_each_entry(set, &ctx->table->sets, list) {
2336+
list_for_each_entry_rcu(set, &ctx->table->sets, list) {
23262337
if (idx < s_idx)
23272338
goto cont;
23282339
if (nf_tables_fill_set(skb, ctx, set,
@@ -2342,6 +2353,7 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
23422353
}
23432354
cb->args[1] = 1;
23442355
done:
2356+
rcu_read_unlock();
23452357
return skb->len;
23462358
}
23472359

@@ -2600,7 +2612,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
26002612
if (err < 0)
26012613
goto err2;
26022614

2603-
list_add_tail(&set->list, &table->sets);
2615+
list_add_tail_rcu(&set->list, &table->sets);
26042616
table->use++;
26052617
return 0;
26062618

@@ -2620,7 +2632,7 @@ static void nft_set_destroy(struct nft_set *set)
26202632

26212633
static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
26222634
{
2623-
list_del(&set->list);
2635+
list_del_rcu(&set->list);
26242636
nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC);
26252637
nft_set_destroy(set);
26262638
}
@@ -2655,7 +2667,7 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
26552667
if (err < 0)
26562668
return err;
26572669

2658-
list_del(&set->list);
2670+
list_del_rcu(&set->list);
26592671
ctx.table->use--;
26602672
return 0;
26612673
}
@@ -2707,14 +2719,14 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
27072719
}
27082720
bind:
27092721
binding->chain = ctx->chain;
2710-
list_add_tail(&binding->list, &set->bindings);
2722+
list_add_tail_rcu(&binding->list, &set->bindings);
27112723
return 0;
27122724
}
27132725

27142726
void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
27152727
struct nft_set_binding *binding)
27162728
{
2717-
list_del(&binding->list);
2729+
list_del_rcu(&binding->list);
27182730

27192731
if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS &&
27202732
!(set->flags & NFT_SET_INACTIVE))
@@ -3494,12 +3506,12 @@ static int nf_tables_abort(struct sk_buff *skb)
34943506
}
34953507
nft_trans_destroy(trans);
34963508
} else {
3497-
list_del(&trans->ctx.table->list);
3509+
list_del_rcu(&trans->ctx.table->list);
34983510
}
34993511
break;
35003512
case NFT_MSG_DELTABLE:
3501-
list_add_tail(&trans->ctx.table->list,
3502-
&trans->ctx.afi->tables);
3513+
list_add_tail_rcu(&trans->ctx.table->list,
3514+
&trans->ctx.afi->tables);
35033515
nft_trans_destroy(trans);
35043516
break;
35053517
case NFT_MSG_NEWCHAIN:
@@ -3510,7 +3522,7 @@ static int nf_tables_abort(struct sk_buff *skb)
35103522
nft_trans_destroy(trans);
35113523
} else {
35123524
trans->ctx.table->use--;
3513-
list_del(&trans->ctx.chain->list);
3525+
list_del_rcu(&trans->ctx.chain->list);
35143526
if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
35153527
trans->ctx.chain->flags & NFT_BASE_CHAIN) {
35163528
nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops,
@@ -3520,8 +3532,8 @@ static int nf_tables_abort(struct sk_buff *skb)
35203532
break;
35213533
case NFT_MSG_DELCHAIN:
35223534
trans->ctx.table->use++;
3523-
list_add_tail(&trans->ctx.chain->list,
3524-
&trans->ctx.table->chains);
3535+
list_add_tail_rcu(&trans->ctx.chain->list,
3536+
&trans->ctx.table->chains);
35253537
nft_trans_destroy(trans);
35263538
break;
35273539
case NFT_MSG_NEWRULE:
@@ -3535,12 +3547,12 @@ static int nf_tables_abort(struct sk_buff *skb)
35353547
break;
35363548
case NFT_MSG_NEWSET:
35373549
trans->ctx.table->use--;
3538-
list_del(&nft_trans_set(trans)->list);
3550+
list_del_rcu(&nft_trans_set(trans)->list);
35393551
break;
35403552
case NFT_MSG_DELSET:
35413553
trans->ctx.table->use++;
3542-
list_add_tail(&nft_trans_set(trans)->list,
3543-
&trans->ctx.table->sets);
3554+
list_add_tail_rcu(&nft_trans_set(trans)->list,
3555+
&trans->ctx.table->sets);
35443556
nft_trans_destroy(trans);
35453557
break;
35463558
case NFT_MSG_NEWSETELEM:

0 commit comments

Comments
 (0)