Skip to content

Commit bd662c4

Browse files
Phil Sutterummakynes
authored andcommitted
netfilter: nf_tables: Add locking for NFT_MSG_GETOBJ_RESET requests
Objects' dump callbacks are not concurrency-safe per-se with reset bit set. If two CPUs perform a reset at the same time, at least counter and quota objects suffer from value underrun. Prevent this by introducing dedicated locking callbacks for nfnetlink and the asynchronous dump handling to serialize access. Fixes: 43da04a ("netfilter: nf_tables: atomic dump and reset for stateful objects") Signed-off-by: Phil Sutter <[email protected]> Reviewed-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 69fc3e9 commit bd662c4

File tree

1 file changed

+59
-13
lines changed

1 file changed

+59
-13
lines changed

net/netfilter/nf_tables_api.c

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8020,6 +8020,19 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb)
80208020
return skb->len;
80218021
}
80228022

8023+
static int nf_tables_dumpreset_obj(struct sk_buff *skb,
8024+
struct netlink_callback *cb)
8025+
{
8026+
struct nftables_pernet *nft_net = nft_pernet(sock_net(skb->sk));
8027+
int ret;
8028+
8029+
mutex_lock(&nft_net->commit_mutex);
8030+
ret = nf_tables_dump_obj(skb, cb);
8031+
mutex_unlock(&nft_net->commit_mutex);
8032+
8033+
return ret;
8034+
}
8035+
80238036
static int nf_tables_dump_obj_start(struct netlink_callback *cb)
80248037
{
80258038
struct nft_obj_dump_ctx *ctx = (void *)cb->ctx;
@@ -8036,12 +8049,18 @@ static int nf_tables_dump_obj_start(struct netlink_callback *cb)
80368049
if (nla[NFTA_OBJ_TYPE])
80378050
ctx->type = ntohl(nla_get_be32(nla[NFTA_OBJ_TYPE]));
80388051

8039-
if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == NFT_MSG_GETOBJ_RESET)
8040-
ctx->reset = true;
8041-
80428052
return 0;
80438053
}
80448054

8055+
static int nf_tables_dumpreset_obj_start(struct netlink_callback *cb)
8056+
{
8057+
struct nft_obj_dump_ctx *ctx = (void *)cb->ctx;
8058+
8059+
ctx->reset = true;
8060+
8061+
return nf_tables_dump_obj_start(cb);
8062+
}
8063+
80458064
static int nf_tables_dump_obj_done(struct netlink_callback *cb)
80468065
{
80478066
struct nft_obj_dump_ctx *ctx = (void *)cb->ctx;
@@ -8100,18 +8119,43 @@ nf_tables_getobj_single(u32 portid, const struct nfnl_info *info,
81008119

81018120
static int nf_tables_getobj(struct sk_buff *skb, const struct nfnl_info *info,
81028121
const struct nlattr * const nla[])
8122+
{
8123+
u32 portid = NETLINK_CB(skb).portid;
8124+
struct sk_buff *skb2;
8125+
8126+
if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
8127+
struct netlink_dump_control c = {
8128+
.start = nf_tables_dump_obj_start,
8129+
.dump = nf_tables_dump_obj,
8130+
.done = nf_tables_dump_obj_done,
8131+
.module = THIS_MODULE,
8132+
.data = (void *)nla,
8133+
};
8134+
8135+
return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
8136+
}
8137+
8138+
skb2 = nf_tables_getobj_single(portid, info, nla, false);
8139+
if (IS_ERR(skb2))
8140+
return PTR_ERR(skb2);
8141+
8142+
return nfnetlink_unicast(skb2, info->net, portid);
8143+
}
8144+
8145+
static int nf_tables_getobj_reset(struct sk_buff *skb,
8146+
const struct nfnl_info *info,
8147+
const struct nlattr * const nla[])
81038148
{
81048149
struct nftables_pernet *nft_net = nft_pernet(info->net);
81058150
u32 portid = NETLINK_CB(skb).portid;
81068151
struct net *net = info->net;
81078152
struct sk_buff *skb2;
8108-
bool reset = false;
81098153
char *buf;
81108154

81118155
if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
81128156
struct netlink_dump_control c = {
8113-
.start = nf_tables_dump_obj_start,
8114-
.dump = nf_tables_dump_obj,
8157+
.start = nf_tables_dumpreset_obj_start,
8158+
.dump = nf_tables_dumpreset_obj,
81158159
.done = nf_tables_dump_obj_done,
81168160
.module = THIS_MODULE,
81178161
.data = (void *)nla,
@@ -8120,16 +8164,18 @@ static int nf_tables_getobj(struct sk_buff *skb, const struct nfnl_info *info,
81208164
return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
81218165
}
81228166

8123-
if (NFNL_MSG_TYPE(info->nlh->nlmsg_type) == NFT_MSG_GETOBJ_RESET)
8124-
reset = true;
8167+
if (!try_module_get(THIS_MODULE))
8168+
return -EINVAL;
8169+
rcu_read_unlock();
8170+
mutex_lock(&nft_net->commit_mutex);
8171+
skb2 = nf_tables_getobj_single(portid, info, nla, true);
8172+
mutex_unlock(&nft_net->commit_mutex);
8173+
rcu_read_lock();
8174+
module_put(THIS_MODULE);
81258175

8126-
skb2 = nf_tables_getobj_single(portid, info, nla, reset);
81278176
if (IS_ERR(skb2))
81288177
return PTR_ERR(skb2);
81298178

8130-
if (!reset)
8131-
return nfnetlink_unicast(skb2, net, NETLINK_CB(skb).portid);
8132-
81338179
buf = kasprintf(GFP_ATOMIC, "%.*s:%u",
81348180
nla_len(nla[NFTA_OBJ_TABLE]),
81358181
(char *)nla_data(nla[NFTA_OBJ_TABLE]),
@@ -9421,7 +9467,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
94219467
.policy = nft_obj_policy,
94229468
},
94239469
[NFT_MSG_GETOBJ_RESET] = {
9424-
.call = nf_tables_getobj,
9470+
.call = nf_tables_getobj_reset,
94259471
.type = NFNL_CB_RCU,
94269472
.attr_count = NFTA_OBJ_MAX,
94279473
.policy = nft_obj_policy,

0 commit comments

Comments
 (0)