| From 918b02d5821c64b27782d314e6d8d413dc3b444e Mon Sep 17 00:00:00 2001 |
| From: Eric Dumazet <eric.dumazet@gmail.com> |
| Date: Thu, 5 Apr 2012 22:17:46 +0000 |
| Subject: [PATCH] netlink: fix races after skb queueing |
| |
| commit 4a7e7c2ad540e54c75489a70137bf0ec15d3a127 upstream. |
| |
| 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> |
| --- |
| net/netlink/af_netlink.c | 24 +++++++++++++----------- |
| 1 file changed, 13 insertions(+), 11 deletions(-) |
| |
| diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c |
| index ba9f6129ff32..1c889685d575 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 >> 1); |
| } |
| return -1; |
| @@ -1685,10 +1691,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; |
| } |
| |
| @@ -1700,10 +1704,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); |
| -- |
| 1.8.5.2 |
| |