diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2012-04-05 22:17:46 +0000 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-04-27 09:51:19 -0700 |
commit | 19a8321ccebc1db80a75d32d0235f2beb646d8f7 (patch) | |
tree | 6f013ba8b378bc033fd5c1f58e3252b40750c7fd /net | |
parent | 589336f3c7731b608e860e984f753909b00efad7 (diff) |
netlink: fix races after skb queueing
[ Upstream commit 4a7e7c2ad540e54c75489a70137bf0ec15d3a127 ]
As soon as an skb is queued into socket receive_queue, another thread
can consume it, so we are not allowed to reference skb anymore, or risk
use after free.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/netlink/af_netlink.c | 24 |
1 files changed, 13 insertions, 11 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 6ef64adf7362..24bc620b539a 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -830,12 +830,19 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb, return 0; } -int netlink_sendskb(struct sock *sk, struct sk_buff *skb) +static int __netlink_sendskb(struct sock *sk, struct sk_buff *skb) { int len = skb->len; skb_queue_tail(&sk->sk_receive_queue, skb); sk->sk_data_ready(sk, len); + return len; +} + +int netlink_sendskb(struct sock *sk, struct sk_buff *skb) +{ + int len = __netlink_sendskb(sk, skb); + sock_put(sk); return len; } @@ -960,8 +967,7 @@ static inline int netlink_broadcast_deliver(struct sock *sk, if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf && !test_bit(0, &nlk->state)) { skb_set_owner_r(skb, sk); - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk, skb->len); + __netlink_sendskb(sk, skb); return atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf; } return -1; @@ -1682,10 +1688,8 @@ static int netlink_dump(struct sock *sk) if (sk_filter(sk, skb)) kfree_skb(skb); - else { - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk, skb->len); - } + else + __netlink_sendskb(sk, skb); return 0; } @@ -1697,10 +1701,8 @@ static int netlink_dump(struct sock *sk) if (sk_filter(sk, skb)) kfree_skb(skb); - else { - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk, skb->len); - } + else + __netlink_sendskb(sk, skb); if (cb->done) cb->done(cb); |