Patch "net: Clone skb before setting peeked flag" has been added to the 3.10-stable tree
Subsystems (2)
This is a note to let you know that I've just added the patch titled
net: Clone skb before setting peeked flag
to the 3.10-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
net-clone-skb-before-setting-peeked-flag.patch
and it can be found in the queue-3.10 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From foo@baz Sat Sep 26 11:20:32 PDT 2015
From: Herbert Xu <[email protected]>
Date: Mon, 13 Jul 2015 16:04:13 +0800
Subject: net: Clone skb before setting peeked flag
From: Herbert Xu <[email protected]>
[ Upstream commit 738ac1ebb96d02e0d23bc320302a6ea94c612dec ]
Shared skbs must not be modified and this is crucial for broadcast and/or multicast paths where we use it as an optimisation to avoid unnecessary cloning.
The function skb_recv_datagram breaks this rule by setting peeked without cloning the skb first. This causes funky races which leads to double-free.
This patch fixes this by cloning the skb and replacing the skb in the list when setting skb->peeked.
--- net/core/datagram.c | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-)
net/core/datagram.c +38 -3
@@ -128,6 +128,35 @@ out_noerr: goto out; } +static int skb_set_peeked (struct sk_buff *skb) +{ +struct sk_buff *nskb; + +if (skb->peeked) +return 0 ; + +/* We have to unshare an skb before modifying it. */ +if (!skb_shared (skb)) + goto done; + + nskb =skb_clone (skb,GFP_ATOMIC ); +if (!nskb) +return -ENOMEM ; + + skb->prev->next = nskb; + skb->next->prev = nskb; + nskb->prev = skb->prev; + nskb->next = skb->next; + +consume_skb (skb); + skb = nskb; + +done: + skb->peeked =1 ; + +return 0 ; +} +/** * __skb_recv_datagram - Receive a datagram skbuff * @sk: socket
@@ -162,7 +191,9 @@ out_noerr:struct sk_buff *__skb_recv_datagram (struct sock *sk,unsigned int flags,int *peeked,int *off,int *err) { +struct sk_buff_head *queue = &sk->sk_receive_queue;struct sk_buff *skb, *last; +unsigned long cpu_flags;long timeo;/* * Caller is allowed not to check sk->sk_err before skb_recv_datagram()
@@ -181,8 +212,6 @@ struct sk_buff *__skb_recv_datagram(stru* Look at current nfs client by the way... * However, this function was correct in any case. 8) */ -unsigned long cpu_flags; -struct sk_buff_head *queue = &sk->sk_receive_queue;int _off = *off; last = (struct sk_buff *)queue;
@@ -196,7 +225,11 @@ struct sk_buff *__skb_recv_datagram(stru _off -= skb->len;continue ; } - skb->peeked =1 ; + + error =skb_set_peeked (skb); +if (error) + goto unlock_err; +atomic_inc (&skb->users); }else __skb_unlink (skb, queue);
@@ -216,6 +249,8 @@ struct sk_buff *__skb_recv_datagram(strureturn NULL ; +unlock_err: +spin_unlock_irqrestore (&queue->lock, cpu_flags); no_packet: *err = error;return NULL ;
Patches currently in stable-queue which might be from [email protected] are
queue-3.10/net-fix-skb_set_peeked-use-after-free-bug.patch queue-3.10/ipv6-lock-socket-in-ip6_datagram_connect.patch queue-3.10/net-fix-skb-csum-races-when-peeking.patch queue-3.10/net-clone-skb-before-setting-peeked-flag.patch