mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 08:33:17 +02:00
netfilter: nf_tables: Add locking for NFT_MSG_GETSETELEM_RESET requests
Set expressions' dump callbacks are not concurrency-safe per-se with reset bit set. If two CPUs reset the same element at the same time, values may underrun at least with element-attached counters and quotas. Prevent this by introducing dedicated callbacks for nfnetlink and the asynchronous dump handling to serialize access. Signed-off-by: Phil Sutter <phil@nwl.cc> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
parent
f649be6d9c
commit
3d483faa66
|
|
@ -5817,10 +5817,6 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
|
||||||
nla_nest_end(skb, nest);
|
nla_nest_end(skb, nest);
|
||||||
nlmsg_end(skb, nlh);
|
nlmsg_end(skb, nlh);
|
||||||
|
|
||||||
if (dump_ctx->reset && args.iter.count > args.iter.skip)
|
|
||||||
audit_log_nft_set_reset(table, cb->seq,
|
|
||||||
args.iter.count - args.iter.skip);
|
|
||||||
|
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
if (args.iter.err && args.iter.err != -EMSGSIZE)
|
if (args.iter.err && args.iter.err != -EMSGSIZE)
|
||||||
|
|
@ -5836,6 +5832,26 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nf_tables_dumpreset_set(struct sk_buff *skb,
|
||||||
|
struct netlink_callback *cb)
|
||||||
|
{
|
||||||
|
struct nftables_pernet *nft_net = nft_pernet(sock_net(skb->sk));
|
||||||
|
struct nft_set_dump_ctx *dump_ctx = cb->data;
|
||||||
|
int ret, skip = cb->args[0];
|
||||||
|
|
||||||
|
mutex_lock(&nft_net->commit_mutex);
|
||||||
|
|
||||||
|
ret = nf_tables_dump_set(skb, cb);
|
||||||
|
|
||||||
|
if (cb->args[0] > skip)
|
||||||
|
audit_log_nft_set_reset(dump_ctx->ctx.table, cb->seq,
|
||||||
|
cb->args[0] - skip);
|
||||||
|
|
||||||
|
mutex_unlock(&nft_net->commit_mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int nf_tables_dump_set_start(struct netlink_callback *cb)
|
static int nf_tables_dump_set_start(struct netlink_callback *cb)
|
||||||
{
|
{
|
||||||
struct nft_set_dump_ctx *dump_ctx = cb->data;
|
struct nft_set_dump_ctx *dump_ctx = cb->data;
|
||||||
|
|
@ -6079,13 +6095,8 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
|
||||||
{
|
{
|
||||||
struct netlink_ext_ack *extack = info->extack;
|
struct netlink_ext_ack *extack = info->extack;
|
||||||
struct nft_set_dump_ctx dump_ctx;
|
struct nft_set_dump_ctx dump_ctx;
|
||||||
int rem, err = 0, nelems = 0;
|
|
||||||
struct net *net = info->net;
|
|
||||||
struct nlattr *attr;
|
struct nlattr *attr;
|
||||||
bool reset = false;
|
int rem, err = 0;
|
||||||
|
|
||||||
if (NFNL_MSG_TYPE(info->nlh->nlmsg_type) == NFT_MSG_GETSETELEM_RESET)
|
|
||||||
reset = true;
|
|
||||||
|
|
||||||
if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
|
if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
|
||||||
struct netlink_dump_control c = {
|
struct netlink_dump_control c = {
|
||||||
|
|
@ -6095,7 +6106,7 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
|
||||||
.module = THIS_MODULE,
|
.module = THIS_MODULE,
|
||||||
};
|
};
|
||||||
|
|
||||||
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, reset);
|
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, false);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
|
@ -6106,22 +6117,75 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
|
||||||
if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
|
if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, reset);
|
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, false);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
|
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
|
||||||
err = nft_get_set_elem(&dump_ctx.ctx, dump_ctx.set, attr, reset);
|
err = nft_get_set_elem(&dump_ctx.ctx, dump_ctx.set, attr, false);
|
||||||
|
if (err < 0) {
|
||||||
|
NL_SET_BAD_ATTR(extack, attr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nf_tables_getsetelem_reset(struct sk_buff *skb,
|
||||||
|
const struct nfnl_info *info,
|
||||||
|
const struct nlattr * const nla[])
|
||||||
|
{
|
||||||
|
struct nftables_pernet *nft_net = nft_pernet(info->net);
|
||||||
|
struct netlink_ext_ack *extack = info->extack;
|
||||||
|
struct nft_set_dump_ctx dump_ctx;
|
||||||
|
int rem, err = 0, nelems = 0;
|
||||||
|
struct nlattr *attr;
|
||||||
|
|
||||||
|
if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
|
||||||
|
struct netlink_dump_control c = {
|
||||||
|
.start = nf_tables_dump_set_start,
|
||||||
|
.dump = nf_tables_dumpreset_set,
|
||||||
|
.done = nf_tables_dump_set_done,
|
||||||
|
.module = THIS_MODULE,
|
||||||
|
};
|
||||||
|
|
||||||
|
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, true);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
c.data = &dump_ctx;
|
||||||
|
return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!try_module_get(THIS_MODULE))
|
||||||
|
return -EINVAL;
|
||||||
|
rcu_read_unlock();
|
||||||
|
mutex_lock(&nft_net->commit_mutex);
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, true);
|
||||||
|
if (err)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
|
||||||
|
err = nft_get_set_elem(&dump_ctx.ctx, dump_ctx.set, attr, true);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
NL_SET_BAD_ATTR(extack, attr);
|
NL_SET_BAD_ATTR(extack, attr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
nelems++;
|
nelems++;
|
||||||
}
|
}
|
||||||
|
audit_log_nft_set_reset(dump_ctx.ctx.table, nft_net->base_seq, nelems);
|
||||||
|
|
||||||
if (reset)
|
out_unlock:
|
||||||
audit_log_nft_set_reset(dump_ctx.ctx.table, nft_pernet(net)->base_seq,
|
rcu_read_unlock();
|
||||||
nelems);
|
mutex_unlock(&nft_net->commit_mutex);
|
||||||
|
rcu_read_lock();
|
||||||
|
module_put(THIS_MODULE);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
@ -9095,7 +9159,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
|
||||||
.policy = nft_set_elem_list_policy,
|
.policy = nft_set_elem_list_policy,
|
||||||
},
|
},
|
||||||
[NFT_MSG_GETSETELEM_RESET] = {
|
[NFT_MSG_GETSETELEM_RESET] = {
|
||||||
.call = nf_tables_getsetelem,
|
.call = nf_tables_getsetelem_reset,
|
||||||
.type = NFNL_CB_RCU,
|
.type = NFNL_CB_RCU,
|
||||||
.attr_count = NFTA_SET_ELEM_LIST_MAX,
|
.attr_count = NFTA_SET_ELEM_LIST_MAX,
|
||||||
.policy = nft_set_elem_list_policy,
|
.policy = nft_set_elem_list_policy,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user