diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2010-07-26 16:09:16 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-07-26 16:09:16 -0400 |
commit | 1235f504aaba2ebeabc863fdb3ceac764a317d47 (patch) | |
tree | 58969a45226b9d35bbfc7ecc3dab2823c117db39 /net/netlink | |
parent | 7b5e078cf04ea5b8b7ccffaf9a3607097f5c2359 (diff) |
netlink: netlink_recvmsg() fix
commit 1dacc76d0014
(net/compat/wext: send different messages to compat tasks)
introduced a race condition on netlink, in case MSG_PEEK is used.
An skb given by skb_recv_datagram() might be shared, we must copy it
before any modification, or risk fatal corruption.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netlink')
-rw-r--r-- | net/netlink/af_netlink.c | 22 |
1 files changed, 16 insertions, 6 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 8648a9922aab..2cbf380377d5 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -1406,7 +1406,7 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, | |||
1406 | struct netlink_sock *nlk = nlk_sk(sk); | 1406 | struct netlink_sock *nlk = nlk_sk(sk); |
1407 | int noblock = flags&MSG_DONTWAIT; | 1407 | int noblock = flags&MSG_DONTWAIT; |
1408 | size_t copied; | 1408 | size_t copied; |
1409 | struct sk_buff *skb, *frag __maybe_unused = NULL; | 1409 | struct sk_buff *skb; |
1410 | int err; | 1410 | int err; |
1411 | 1411 | ||
1412 | if (flags&MSG_OOB) | 1412 | if (flags&MSG_OOB) |
@@ -1441,7 +1441,21 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, | |||
1441 | kfree_skb(skb); | 1441 | kfree_skb(skb); |
1442 | skb = compskb; | 1442 | skb = compskb; |
1443 | } else { | 1443 | } else { |
1444 | frag = skb_shinfo(skb)->frag_list; | 1444 | /* |
1445 | * Before setting frag_list to NULL, we must get a | ||
1446 | * private copy of skb if shared (because of MSG_PEEK) | ||
1447 | */ | ||
1448 | if (skb_shared(skb)) { | ||
1449 | struct sk_buff *nskb; | ||
1450 | |||
1451 | nskb = pskb_copy(skb, GFP_KERNEL); | ||
1452 | kfree_skb(skb); | ||
1453 | skb = nskb; | ||
1454 | err = -ENOMEM; | ||
1455 | if (!skb) | ||
1456 | goto out; | ||
1457 | } | ||
1458 | kfree_skb(skb_shinfo(skb)->frag_list); | ||
1445 | skb_shinfo(skb)->frag_list = NULL; | 1459 | skb_shinfo(skb)->frag_list = NULL; |
1446 | } | 1460 | } |
1447 | } | 1461 | } |
@@ -1478,10 +1492,6 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, | |||
1478 | if (flags & MSG_TRUNC) | 1492 | if (flags & MSG_TRUNC) |
1479 | copied = skb->len; | 1493 | copied = skb->len; |
1480 | 1494 | ||
1481 | #ifdef CONFIG_COMPAT_NETLINK_MESSAGES | ||
1482 | skb_shinfo(skb)->frag_list = frag; | ||
1483 | #endif | ||
1484 | |||
1485 | skb_free_datagram(sk, skb); | 1495 | skb_free_datagram(sk, skb); |
1486 | 1496 | ||
1487 | if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) | 1497 | if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) |