Commit fa803605 authored by Liping Zhang's avatar Liping Zhang Committed by Pablo Neira Ayuso

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: default avatarLiping Zhang <zlpnobody@gmail.com>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 87e94dbc
...@@ -3367,35 +3367,50 @@ static int nf_tables_dump_setelem(const struct nft_ctx *ctx, ...@@ -3367,35 +3367,50 @@ static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
return nf_tables_fill_setelem(args->skb, set, elem); return nf_tables_fill_setelem(args->skb, set, elem);
} }
struct nft_set_dump_ctx {
const struct nft_set *set;
struct nft_ctx ctx;
};
static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
{ {
struct nft_set_dump_ctx *dump_ctx = cb->data;
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
u8 genmask = nft_genmask_cur(net); struct nft_af_info *afi;
struct nft_table *table;
struct nft_set *set; struct nft_set *set;
struct nft_set_dump_args args; struct nft_set_dump_args args;
struct nft_ctx ctx; bool set_found = false;
struct nlattr *nla[NFTA_SET_ELEM_LIST_MAX + 1];
struct nfgenmsg *nfmsg; struct nfgenmsg *nfmsg;
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
struct nlattr *nest; struct nlattr *nest;
u32 portid, seq; u32 portid, seq;
int event, err; int event;
err = nlmsg_parse(cb->nlh, sizeof(struct nfgenmsg), nla, rcu_read_lock();
NFTA_SET_ELEM_LIST_MAX, nft_set_elem_list_policy, list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
NULL); if (afi != dump_ctx->ctx.afi)
if (err < 0) continue;
return err;
err = nft_ctx_init_from_elemattr(&ctx, net, cb->skb, cb->nlh, list_for_each_entry_rcu(table, &afi->tables, list) {
(void *)nla, genmask); if (table != dump_ctx->ctx.table)
if (err < 0) continue;
return err;
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET], list_for_each_entry_rcu(set, &table->sets, list) {
genmask); if (set == dump_ctx->set) {
if (IS_ERR(set)) set_found = true;
return PTR_ERR(set); break;
}
}
break;
}
break;
}
if (!set_found) {
rcu_read_unlock();
return -ENOENT;
}
event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWSETELEM); event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWSETELEM);
portid = NETLINK_CB(cb->skb).portid; portid = NETLINK_CB(cb->skb).portid;
...@@ -3407,11 +3422,11 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -3407,11 +3422,11 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
goto nla_put_failure; goto nla_put_failure;
nfmsg = nlmsg_data(nlh); nfmsg = nlmsg_data(nlh);
nfmsg->nfgen_family = ctx.afi->family; nfmsg->nfgen_family = afi->family;
nfmsg->version = NFNETLINK_V0; nfmsg->version = NFNETLINK_V0;
nfmsg->res_id = htons(ctx.net->nft.base_seq & 0xffff); nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, ctx.table->name)) if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, table->name))
goto nla_put_failure; goto nla_put_failure;
if (nla_put_string(skb, NFTA_SET_ELEM_LIST_SET, set->name)) if (nla_put_string(skb, NFTA_SET_ELEM_LIST_SET, set->name))
goto nla_put_failure; goto nla_put_failure;
...@@ -3422,12 +3437,13 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -3422,12 +3437,13 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
args.cb = cb; args.cb = cb;
args.skb = skb; args.skb = skb;
args.iter.genmask = nft_genmask_cur(ctx.net); args.iter.genmask = nft_genmask_cur(net);
args.iter.skip = cb->args[0]; args.iter.skip = cb->args[0];
args.iter.count = 0; args.iter.count = 0;
args.iter.err = 0; args.iter.err = 0;
args.iter.fn = nf_tables_dump_setelem; args.iter.fn = nf_tables_dump_setelem;
set->ops->walk(&ctx, set, &args.iter); set->ops->walk(&dump_ctx->ctx, set, &args.iter);
rcu_read_unlock();
nla_nest_end(skb, nest); nla_nest_end(skb, nest);
nlmsg_end(skb, nlh); nlmsg_end(skb, nlh);
...@@ -3441,9 +3457,16 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -3441,9 +3457,16 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len; return skb->len;
nla_put_failure: nla_put_failure:
rcu_read_unlock();
return -ENOSPC; return -ENOSPC;
} }
static int nf_tables_dump_set_done(struct netlink_callback *cb)
{
kfree(cb->data);
return 0;
}
static int nf_tables_getsetelem(struct net *net, struct sock *nlsk, static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
struct sk_buff *skb, const struct nlmsghdr *nlh, struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const nla[]) const struct nlattr * const nla[])
...@@ -3465,7 +3488,18 @@ static int nf_tables_getsetelem(struct net *net, struct sock *nlsk, ...@@ -3465,7 +3488,18 @@ static int nf_tables_getsetelem(struct net *net, struct sock *nlsk,
if (nlh->nlmsg_flags & NLM_F_DUMP) { if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = { struct netlink_dump_control c = {
.dump = nf_tables_dump_set, .dump = nf_tables_dump_set,
.done = nf_tables_dump_set_done,
}; };
struct nft_set_dump_ctx *dump_ctx;
dump_ctx = kmalloc(sizeof(*dump_ctx), GFP_KERNEL);
if (!dump_ctx)
return -ENOMEM;
dump_ctx->set = set;
dump_ctx->ctx = ctx;
c.data = dump_ctx;
return netlink_dump_start(nlsk, skb, nlh, &c); return netlink_dump_start(nlsk, skb, nlh, &c);
} }
return -EOPNOTSUPP; return -EOPNOTSUPP;
......
...@@ -222,7 +222,7 @@ static void nft_hash_walk(const struct nft_ctx *ctx, struct nft_set *set, ...@@ -222,7 +222,7 @@ static void nft_hash_walk(const struct nft_ctx *ctx, struct nft_set *set,
struct nft_set_elem elem; struct nft_set_elem elem;
int err; int err;
err = rhashtable_walk_init(&priv->ht, &hti, GFP_KERNEL); err = rhashtable_walk_init(&priv->ht, &hti, GFP_ATOMIC);
iter->err = err; iter->err = err;
if (err) if (err)
return; return;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment