Skip to content

Commit fa80360

Browse files
Liping Zhangummakynes
Liping Zhang
authored andcommitted
netfilter: nf_tables: can't assume lock is acquired when dumping set elems
When dumping the elements related to a specified set, we may invoke the nf_tables_dump_set with the NFNL_SUBSYS_NFTABLES lock not acquired. So we should use the proper rcu operation to avoid race condition, just like other nft dump operations. Signed-off-by: Liping Zhang <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 87e94db commit fa80360

File tree

2 files changed

+57
-23
lines changed

2 files changed

+57
-23
lines changed

net/netfilter/nf_tables_api.c

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3367,35 +3367,50 @@ static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
33673367
return nf_tables_fill_setelem(args->skb, set, elem);
33683368
}
33693369

3370+
struct nft_set_dump_ctx {
3371+
const struct nft_set *set;
3372+
struct nft_ctx ctx;
3373+
};
3374+
33703375
static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
33713376
{
3377+
struct nft_set_dump_ctx *dump_ctx = cb->data;
33723378
struct net *net = sock_net(skb->sk);
3373-
u8 genmask = nft_genmask_cur(net);
3379+
struct nft_af_info *afi;
3380+
struct nft_table *table;
33743381
struct nft_set *set;
33753382
struct nft_set_dump_args args;
3376-
struct nft_ctx ctx;
3377-
struct nlattr *nla[NFTA_SET_ELEM_LIST_MAX + 1];
3383+
bool set_found = false;
33783384
struct nfgenmsg *nfmsg;
33793385
struct nlmsghdr *nlh;
33803386
struct nlattr *nest;
33813387
u32 portid, seq;
3382-
int event, err;
3388+
int event;
33833389

3384-
err = nlmsg_parse(cb->nlh, sizeof(struct nfgenmsg), nla,
3385-
NFTA_SET_ELEM_LIST_MAX, nft_set_elem_list_policy,
3386-
NULL);
3387-
if (err < 0)
3388-
return err;
3390+
rcu_read_lock();
3391+
list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
3392+
if (afi != dump_ctx->ctx.afi)
3393+
continue;
33893394

3390-
err = nft_ctx_init_from_elemattr(&ctx, net, cb->skb, cb->nlh,
3391-
(void *)nla, genmask);
3392-
if (err < 0)
3393-
return err;
3395+
list_for_each_entry_rcu(table, &afi->tables, list) {
3396+
if (table != dump_ctx->ctx.table)
3397+
continue;
33943398

3395-
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET],
3396-
genmask);
3397-
if (IS_ERR(set))
3398-
return PTR_ERR(set);
3399+
list_for_each_entry_rcu(set, &table->sets, list) {
3400+
if (set == dump_ctx->set) {
3401+
set_found = true;
3402+
break;
3403+
}
3404+
}
3405+
break;
3406+
}
3407+
break;
3408+
}
3409+
3410+
if (!set_found) {
3411+
rcu_read_unlock();
3412+
return -ENOENT;
3413+
}
33993414

34003415
event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWSETELEM);
34013416
portid = NETLINK_CB(cb->skb).portid;
@@ -3407,11 +3422,11 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
34073422
goto nla_put_failure;
34083423

34093424
nfmsg = nlmsg_data(nlh);
3410-
nfmsg->nfgen_family = ctx.afi->family;
3425+
nfmsg->nfgen_family = afi->family;
34113426
nfmsg->version = NFNETLINK_V0;
3412-
nfmsg->res_id = htons(ctx.net->nft.base_seq & 0xffff);
3427+
nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
34133428

3414-
if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, ctx.table->name))
3429+
if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, table->name))
34153430
goto nla_put_failure;
34163431
if (nla_put_string(skb, NFTA_SET_ELEM_LIST_SET, set->name))
34173432
goto nla_put_failure;
@@ -3422,12 +3437,13 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
34223437

34233438
args.cb = cb;
34243439
args.skb = skb;
3425-
args.iter.genmask = nft_genmask_cur(ctx.net);
3440+
args.iter.genmask = nft_genmask_cur(net);
34263441
args.iter.skip = cb->args[0];
34273442
args.iter.count = 0;
34283443
args.iter.err = 0;
34293444
args.iter.fn = nf_tables_dump_setelem;
3430-
set->ops->walk(&ctx, set, &args.iter);
3445+
set->ops->walk(&dump_ctx->ctx, set, &args.iter);
3446+
rcu_read_unlock();
34313447

34323448
nla_nest_end(skb, nest);
34333449
nlmsg_end(skb, nlh);
@@ -3441,9 +3457,16 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
34413457
return skb->len;
34423458

34433459
nla_put_failure:
3460+
rcu_read_unlock();
34443461
return -ENOSPC;
34453462
}
34463463

3464+
static int nf_tables_dump_set_done(struct netlink_callback *cb)
3465+
{
3466+
kfree(cb->data);
3467+
return 0;
3468+
}
3469+
34473470
static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
34483471
struct sk_buff *skb, const struct nlmsghdr *nlh,
34493472
const struct nlattr * const nla[])
@@ -3465,7 +3488,18 @@ static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
34653488
if (nlh->nlmsg_flags & NLM_F_DUMP) {
34663489
struct netlink_dump_control c = {
34673490
.dump = nf_tables_dump_set,
3491+
.done = nf_tables_dump_set_done,
34683492
};
3493+
struct nft_set_dump_ctx *dump_ctx;
3494+
3495+
dump_ctx = kmalloc(sizeof(*dump_ctx), GFP_KERNEL);
3496+
if (!dump_ctx)
3497+
return -ENOMEM;
3498+
3499+
dump_ctx->set = set;
3500+
dump_ctx->ctx = ctx;
3501+
3502+
c.data = dump_ctx;
34693503
return netlink_dump_start(nlsk, skb, nlh, &c);
34703504
}
34713505
return -EOPNOTSUPP;

net/netfilter/nft_set_hash.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ static void nft_hash_walk(const struct nft_ctx *ctx, struct nft_set *set,
222222
struct nft_set_elem elem;
223223
int err;
224224

225-
err = rhashtable_walk_init(&priv->ht, &hti, GFP_KERNEL);
225+
err = rhashtable_walk_init(&priv->ht, &hti, GFP_ATOMIC);
226226
iter->err = err;
227227
if (err)
228228
return;

0 commit comments

Comments
 (0)