Skip to content

Commit 3d483fa

Browse files
Phil Sutterummakynes
authored andcommitted
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 <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent f649be6 commit 3d483fa

File tree

1 file changed

+81
-17
lines changed

1 file changed

+81
-17
lines changed

net/netfilter/nf_tables_api.c

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5817,10 +5817,6 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
58175817
nla_nest_end(skb, nest);
58185818
nlmsg_end(skb, nlh);
58195819

5820-
if (dump_ctx->reset && args.iter.count > args.iter.skip)
5821-
audit_log_nft_set_reset(table, cb->seq,
5822-
args.iter.count - args.iter.skip);
5823-
58245820
rcu_read_unlock();
58255821

58265822
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)
58365832
return -ENOSPC;
58375833
}
58385834

5835+
static int nf_tables_dumpreset_set(struct sk_buff *skb,
5836+
struct netlink_callback *cb)
5837+
{
5838+
struct nftables_pernet *nft_net = nft_pernet(sock_net(skb->sk));
5839+
struct nft_set_dump_ctx *dump_ctx = cb->data;
5840+
int ret, skip = cb->args[0];
5841+
5842+
mutex_lock(&nft_net->commit_mutex);
5843+
5844+
ret = nf_tables_dump_set(skb, cb);
5845+
5846+
if (cb->args[0] > skip)
5847+
audit_log_nft_set_reset(dump_ctx->ctx.table, cb->seq,
5848+
cb->args[0] - skip);
5849+
5850+
mutex_unlock(&nft_net->commit_mutex);
5851+
5852+
return ret;
5853+
}
5854+
58395855
static int nf_tables_dump_set_start(struct netlink_callback *cb)
58405856
{
58415857
struct nft_set_dump_ctx *dump_ctx = cb->data;
@@ -6079,13 +6095,8 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
60796095
{
60806096
struct netlink_ext_ack *extack = info->extack;
60816097
struct nft_set_dump_ctx dump_ctx;
6082-
int rem, err = 0, nelems = 0;
6083-
struct net *net = info->net;
60846098
struct nlattr *attr;
6085-
bool reset = false;
6086-
6087-
if (NFNL_MSG_TYPE(info->nlh->nlmsg_type) == NFT_MSG_GETSETELEM_RESET)
6088-
reset = true;
6099+
int rem, err = 0;
60896100

60906101
if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
60916102
struct netlink_dump_control c = {
@@ -6095,7 +6106,7 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
60956106
.module = THIS_MODULE,
60966107
};
60976108

6098-
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, reset);
6109+
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, false);
60996110
if (err)
61006111
return err;
61016112

@@ -6106,22 +6117,75 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
61066117
if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
61076118
return -EINVAL;
61086119

6109-
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, reset);
6120+
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, false);
61106121
if (err)
61116122
return err;
61126123

61136124
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
6114-
err = nft_get_set_elem(&dump_ctx.ctx, dump_ctx.set, attr, reset);
6125+
err = nft_get_set_elem(&dump_ctx.ctx, dump_ctx.set, attr, false);
6126+
if (err < 0) {
6127+
NL_SET_BAD_ATTR(extack, attr);
6128+
break;
6129+
}
6130+
}
6131+
6132+
return err;
6133+
}
6134+
6135+
static int nf_tables_getsetelem_reset(struct sk_buff *skb,
6136+
const struct nfnl_info *info,
6137+
const struct nlattr * const nla[])
6138+
{
6139+
struct nftables_pernet *nft_net = nft_pernet(info->net);
6140+
struct netlink_ext_ack *extack = info->extack;
6141+
struct nft_set_dump_ctx dump_ctx;
6142+
int rem, err = 0, nelems = 0;
6143+
struct nlattr *attr;
6144+
6145+
if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
6146+
struct netlink_dump_control c = {
6147+
.start = nf_tables_dump_set_start,
6148+
.dump = nf_tables_dumpreset_set,
6149+
.done = nf_tables_dump_set_done,
6150+
.module = THIS_MODULE,
6151+
};
6152+
6153+
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, true);
6154+
if (err)
6155+
return err;
6156+
6157+
c.data = &dump_ctx;
6158+
return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
6159+
}
6160+
6161+
if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
6162+
return -EINVAL;
6163+
6164+
if (!try_module_get(THIS_MODULE))
6165+
return -EINVAL;
6166+
rcu_read_unlock();
6167+
mutex_lock(&nft_net->commit_mutex);
6168+
rcu_read_lock();
6169+
6170+
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, true);
6171+
if (err)
6172+
goto out_unlock;
6173+
6174+
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
6175+
err = nft_get_set_elem(&dump_ctx.ctx, dump_ctx.set, attr, true);
61156176
if (err < 0) {
61166177
NL_SET_BAD_ATTR(extack, attr);
61176178
break;
61186179
}
61196180
nelems++;
61206181
}
6182+
audit_log_nft_set_reset(dump_ctx.ctx.table, nft_net->base_seq, nelems);
61216183

6122-
if (reset)
6123-
audit_log_nft_set_reset(dump_ctx.ctx.table, nft_pernet(net)->base_seq,
6124-
nelems);
6184+
out_unlock:
6185+
rcu_read_unlock();
6186+
mutex_unlock(&nft_net->commit_mutex);
6187+
rcu_read_lock();
6188+
module_put(THIS_MODULE);
61256189

61266190
return err;
61276191
}
@@ -9095,7 +9159,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
90959159
.policy = nft_set_elem_list_policy,
90969160
},
90979161
[NFT_MSG_GETSETELEM_RESET] = {
9098-
.call = nf_tables_getsetelem,
9162+
.call = nf_tables_getsetelem_reset,
90999163
.type = NFNL_CB_RCU,
91009164
.attr_count = NFTA_SET_ELEM_LIST_MAX,
91019165
.policy = nft_set_elem_list_policy,

0 commit comments

Comments
 (0)