CVE-2014-0131: Use After Free (CWE-416)
value: Use-after-free vulnerability in the skb_segment function in net/core/skbuff.c in the Linux kernel through 3.13.6 allows attackers to obtain sensitive information from kernel memory by leveraging the absence of a certain orphaning operation.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
Published: 2014-03-24T16:40Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: net/core/skbuff.c
Commit date: 2014-03-11 16:26:38 -0400
Commit message: skbuff: skb_segment: orphan frags before copying

skb_segment copies frags around, so we need
to copy them carefully to avoid accessing
user memory after reporting completion to userspace
through a callback.

skb_segment doesn't normally happen on datapath:
TSO needs to be disabled - so disabling zero copy
in this case does not look like a big deal.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>

Diff: diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index dc4f7683ff5271..869c7afe3b0705 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2852,10 +2852,11 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
 	struct sk_buff *tail = NULL;
 	struct sk_buff *list_skb = skb_shinfo(head_skb)->frag_list;
 	skb_frag_t *frag = skb_shinfo(head_skb)->frags;
 	unsigned int mss = skb_shinfo(head_skb)->gso_size;
 	unsigned int doffset = head_skb->data - skb_mac_header(head_skb);
+	struct sk_buff *frag_skb = head_skb;
 	unsigned int offset = doffset;
 	unsigned int tnl_hlen = skb_tnl_header_len(head_skb);
 	unsigned int headroom;
 	unsigned int len;
 	__be16 proto;
@@ -2896,10 +2897,11 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
 			BUG_ON(skb_headlen(list_skb) > len);
 
 			i = 0;
 			nfrags = skb_shinfo(list_skb)->nr_frags;
 			frag = skb_shinfo(list_skb)->frags;
+			frag_skb = list_skb;
 			pos += skb_headlen(list_skb);
 
 			while (pos < offset + len) {
 				BUG_ON(i >= nfrags);
 
@@ -2983,10 +2985,11 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
 				BUG_ON(skb_headlen(list_skb));
 
 				i = 0;
 				nfrags = skb_shinfo(list_skb)->nr_frags;
 				frag = skb_shinfo(list_skb)->frags;
+				frag_skb = list_skb;
 
 				BUG_ON(!nfrags);
 
 				list_skb = list_skb->next;
 			}
@@ -2997,10 +3000,13 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
 					"skb_segment: too many frags: %u %u\n",
 					pos, mss);
 				goto err;
 			}
 
+			if (unlikely(skb_orphan_frags(frag_skb, GFP_ATOMIC)))
+				goto err;
+
 			*nskb_frag = *frag;
 			__skb_frag_ref(nskb_frag);
 			size = skb_frag_size(nskb_frag);
 
 			if (pos < offset) {

Fixed code: 	struct sk_buff *frag_skb = head_skb;
			frag_skb = list_skb;
				frag_skb = list_skb;
			if (unlikely(skb_orphan_frags(frag_skb, GFP_ATOMIC)))
				goto err;