Skip to content

Commit 5038517

Browse files
Kadlecsik Józsefummakynes
authored andcommitted
netfilter: ipset: fix suspicious RCU usage in find_set_and_id
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]>
1 parent 44efc78 commit 5038517

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
@@ -1483,31 +1483,34 @@ ip_set_dump_policy[IPSET_ATTR_CMD_MAX + 1] = {
14831483
};
14841484

14851485
static int
1486-
dump_init(struct netlink_callback *cb, struct ip_set_net *inst)
1486+
ip_set_dump_start(struct netlink_callback *cb)
14871487
{
14881488
struct nlmsghdr *nlh = nlmsg_hdr(cb->skb);
14891489
int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
14901490
struct nlattr *cda[IPSET_ATTR_CMD_MAX + 1];
14911491
struct nlattr *attr = (void *)nlh + min_len;
1492+
struct sk_buff *skb = cb->skb;
1493+
struct ip_set_net *inst = ip_set_pernet(sock_net(skb->sk));
14921494
u32 dump_type;
1493-
ip_set_id_t index;
14941495
int ret;
14951496

14961497
ret = nla_parse(cda, IPSET_ATTR_CMD_MAX, attr,
14971498
nlh->nlmsg_len - min_len,
14981499
ip_set_dump_policy, NULL);
14991500
if (ret)
1500-
return ret;
1501+
goto error;
15011502

15021503
cb->args[IPSET_CB_PROTO] = nla_get_u8(cda[IPSET_ATTR_PROTOCOL]);
15031504
if (cda[IPSET_ATTR_SETNAME]) {
1505+
ip_set_id_t index;
15041506
struct ip_set *set;
15051507

15061508
set = find_set_and_id(inst, nla_data(cda[IPSET_ATTR_SETNAME]),
15071509
&index);
1508-
if (!set)
1509-
return -ENOENT;
1510-
1510+
if (!set) {
1511+
ret = -ENOENT;
1512+
goto error;
1513+
}
15111514
dump_type = DUMP_ONE;
15121515
cb->args[IPSET_CB_INDEX] = index;
15131516
} else {
@@ -1523,10 +1526,17 @@ dump_init(struct netlink_callback *cb, struct ip_set_net *inst)
15231526
cb->args[IPSET_CB_DUMP] = dump_type;
15241527

15251528
return 0;
1529+
1530+
error:
1531+
/* We have to create and send the error message manually :-( */
1532+
if (nlh->nlmsg_flags & NLM_F_ACK) {
1533+
netlink_ack(cb->skb, nlh, ret, NULL);
1534+
}
1535+
return ret;
15261536
}
15271537

15281538
static int
1529-
ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
1539+
ip_set_dump_do(struct sk_buff *skb, struct netlink_callback *cb)
15301540
{
15311541
ip_set_id_t index = IPSET_INVALID_ID, max;
15321542
struct ip_set *set = NULL;
@@ -1537,18 +1547,8 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
15371547
bool is_destroyed;
15381548
int ret = 0;
15391549

1540-
if (!cb->args[IPSET_CB_DUMP]) {
1541-
ret = dump_init(cb, inst);
1542-
if (ret < 0) {
1543-
nlh = nlmsg_hdr(cb->skb);
1544-
/* We have to create and send the error message
1545-
* manually :-(
1546-
*/
1547-
if (nlh->nlmsg_flags & NLM_F_ACK)
1548-
netlink_ack(cb->skb, nlh, ret, NULL);
1549-
return ret;
1550-
}
1551-
}
1550+
if (!cb->args[IPSET_CB_DUMP])
1551+
return -EINVAL;
15521552

15531553
if (cb->args[IPSET_CB_INDEX] >= inst->ip_set_max)
15541554
goto out;
@@ -1684,7 +1684,8 @@ static int ip_set_dump(struct net *net, struct sock *ctnl, struct sk_buff *skb,
16841684

16851685
{
16861686
struct netlink_dump_control c = {
1687-
.dump = ip_set_dump_start,
1687+
.start = ip_set_dump_start,
1688+
.dump = ip_set_dump_do,
16881689
.done = ip_set_dump_done,
16891690
};
16901691
return netlink_dump_start(ctnl, skb, nlh, &c);

0 commit comments

Comments
 (0)