Skip to content

Commit 8ce07d9

Browse files
Kadlecsik Józsefgregkh
authored andcommitted
netfilter: ipset: fix suspicious RCU usage in find_set_and_id
commit 5038517 upstream. find_set_and_id() is called when the NFNL_SUBSYS_IPSET mutex is held. However, in the error path there can be a follow-up recvmsg() without the mutex held. Use the start() function of struct netlink_dump_control instead of dump() to verify and report if the specified set does not exist. Thanks to Pablo Neira Ayuso for helping me to understand the subleties of the netlink protocol. Reported-by: [email protected] Signed-off-by: Jozsef Kadlecsik <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 7bad0dd commit 8ce07d9

File tree

1 file changed

+21
-20
lines changed

1 file changed

+21
-20
lines changed

net/netfilter/ipset/ip_set_core.c

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,31 +1293,34 @@ ip_set_dump_policy[IPSET_ATTR_CMD_MAX + 1] = {
12931293
};
12941294

12951295
static int
1296-
dump_init(struct netlink_callback *cb, struct ip_set_net *inst)
1296+
ip_set_dump_start(struct netlink_callback *cb)
12971297
{
12981298
struct nlmsghdr *nlh = nlmsg_hdr(cb->skb);
12991299
int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
13001300
struct nlattr *cda[IPSET_ATTR_CMD_MAX + 1];
13011301
struct nlattr *attr = (void *)nlh + min_len;
1302+
struct sk_buff *skb = cb->skb;
1303+
struct ip_set_net *inst = ip_set_pernet(sock_net(skb->sk));
13021304
u32 dump_type;
1303-
ip_set_id_t index;
13041305
int ret;
13051306

13061307
ret = nla_parse(cda, IPSET_ATTR_CMD_MAX, attr,
13071308
nlh->nlmsg_len - min_len,
13081309
ip_set_dump_policy, NULL);
13091310
if (ret)
1310-
return ret;
1311+
goto error;
13111312

13121313
cb->args[IPSET_CB_PROTO] = nla_get_u8(cda[IPSET_ATTR_PROTOCOL]);
13131314
if (cda[IPSET_ATTR_SETNAME]) {
1315+
ip_set_id_t index;
13141316
struct ip_set *set;
13151317

13161318
set = find_set_and_id(inst, nla_data(cda[IPSET_ATTR_SETNAME]),
13171319
&index);
1318-
if (!set)
1319-
return -ENOENT;
1320-
1320+
if (!set) {
1321+
ret = -ENOENT;
1322+
goto error;
1323+
}
13211324
dump_type = DUMP_ONE;
13221325
cb->args[IPSET_CB_INDEX] = index;
13231326
} else {
@@ -1333,10 +1336,17 @@ dump_init(struct netlink_callback *cb, struct ip_set_net *inst)
13331336
cb->args[IPSET_CB_DUMP] = dump_type;
13341337

13351338
return 0;
1339+
1340+
error:
1341+
/* We have to create and send the error message manually :-( */
1342+
if (nlh->nlmsg_flags & NLM_F_ACK) {
1343+
netlink_ack(cb->skb, nlh, ret, NULL);
1344+
}
1345+
return ret;
13361346
}
13371347

13381348
static int
1339-
ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
1349+
ip_set_dump_do(struct sk_buff *skb, struct netlink_callback *cb)
13401350
{
13411351
ip_set_id_t index = IPSET_INVALID_ID, max;
13421352
struct ip_set *set = NULL;
@@ -1347,18 +1357,8 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
13471357
bool is_destroyed;
13481358
int ret = 0;
13491359

1350-
if (!cb->args[IPSET_CB_DUMP]) {
1351-
ret = dump_init(cb, inst);
1352-
if (ret < 0) {
1353-
nlh = nlmsg_hdr(cb->skb);
1354-
/* We have to create and send the error message
1355-
* manually :-(
1356-
*/
1357-
if (nlh->nlmsg_flags & NLM_F_ACK)
1358-
netlink_ack(cb->skb, nlh, ret, NULL);
1359-
return ret;
1360-
}
1361-
}
1360+
if (!cb->args[IPSET_CB_DUMP])
1361+
return -EINVAL;
13621362

13631363
if (cb->args[IPSET_CB_INDEX] >= inst->ip_set_max)
13641364
goto out;
@@ -1494,7 +1494,8 @@ static int ip_set_dump(struct net *net, struct sock *ctnl, struct sk_buff *skb,
14941494

14951495
{
14961496
struct netlink_dump_control c = {
1497-
.dump = ip_set_dump_start,
1497+
.start = ip_set_dump_start,
1498+
.dump = ip_set_dump_do,
14981499
.done = ip_set_dump_done,
14991500
};
15001501
return netlink_dump_start(ctnl, skb, nlh, &c);

0 commit comments

Comments
 (0)