diff --git a/kernel.spec b/kernel.spec index db233e529..252dcb9fe 100644 --- a/kernel.spec +++ b/kernel.spec @@ -806,6 +806,9 @@ Patch25045: netfilter-nf_conntrack_dccp-fix-skb_header_pointer-A.patch #rhbz 1078894 Patch25046: mm-readahead.c-fix-do_readahead-for-no-readpage-s.patch +#CVE-2014-0131 rhbz 1074589 1079006 +Patch25048: skbuff-zero-copy.patch + # END OF PATCH DEFINITIONS %endif @@ -1568,6 +1571,9 @@ ApplyPatch netfilter-nf_conntrack_dccp-fix-skb_header_pointer-A.patch #rhbz 1078894 ApplyPatch mm-readahead.c-fix-do_readahead-for-no-readpage-s.patch +#CVE-2014-0131 rhbz 1074589 1079006 +ApplyPatch skbuff-zero-copy.patch + # END OF PATCH APPLICATIONS %endif @@ -2380,6 +2386,7 @@ fi # || || %changelog * Thu Mar 20 2014 Josh Boyer +- CVE-2014-0131: skbuff: use-after-free during segmentation with zerocopy (rhbz 1074589 1079006) - Fix readahead semantics on pipes and sockets (rhbz 1078894) * Mon Mar 17 2014 Josh Boyer diff --git a/skbuff-zero-copy.patch b/skbuff-zero-copy.patch new file mode 100644 index 000000000..4e8e83905 --- /dev/null +++ b/skbuff-zero-copy.patch @@ -0,0 +1,449 @@ +Bugzilla: 1079006 +Upstream-status: 3.14 and queued for stable + +From 8cb19905e9287a93ce7c2cbbdf742a060b00e219 Mon Sep 17 00:00:00 2001 +From: "Michael S. Tsirkin" +Date: Mon, 10 Mar 2014 18:29:04 +0200 +Subject: [PATCH 1/5] skbuff: skb_segment: s/frag/nskb_frag/ + +frag points at nskb, so name it appropriately + +Signed-off-by: Michael S. Tsirkin +Signed-off-by: David S. Miller +--- + net/core/skbuff.c | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index 5d6236d..60e8cd7 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -2876,7 +2876,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) + + do { + struct sk_buff *nskb; +- skb_frag_t *frag; ++ skb_frag_t *nskb_frag; + int hsize; + int size; + +@@ -2969,7 +2969,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) + continue; + } + +- frag = skb_shinfo(nskb)->frags; ++ nskb_frag = skb_shinfo(nskb)->frags; + + skb_copy_from_linear_data_offset(skb, offset, + skb_put(nskb, hsize), hsize); +@@ -2997,13 +2997,13 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) + goto err; + } + +- *frag = *skb_frag; +- __skb_frag_ref(frag); +- size = skb_frag_size(frag); ++ *nskb_frag = *skb_frag; ++ __skb_frag_ref(nskb_frag); ++ size = skb_frag_size(nskb_frag); + + if (pos < offset) { +- frag->page_offset += offset - pos; +- skb_frag_size_sub(frag, offset - pos); ++ nskb_frag->page_offset += offset - pos; ++ skb_frag_size_sub(nskb_frag, offset - pos); + } + + skb_shinfo(nskb)->nr_frags++; +@@ -3013,11 +3013,11 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) + skb_frag++; + pos += size; + } else { +- skb_frag_size_sub(frag, pos + size - (offset + len)); ++ skb_frag_size_sub(nskb_frag, pos + size - (offset + len)); + goto skip_fraglist; + } + +- frag++; ++ nskb_frag++; + } + + skip_fraglist: +-- +1.8.5.3 + + +From 4e1beba12d094c6c761ba5c49032b9b9e46380e8 Mon Sep 17 00:00:00 2001 +From: "Michael S. Tsirkin" +Date: Mon, 10 Mar 2014 18:29:14 +0200 +Subject: [PATCH 2/5] skbuff: skb_segment: s/skb_frag/frag/ + +skb_frag can in fact point at either skb +or fskb so rename it generally "frag". + +Signed-off-by: Michael S. Tsirkin +Signed-off-by: David S. Miller +--- + net/core/skbuff.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index 60e8cd7..d788a98 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -2850,7 +2850,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) + struct sk_buff *segs = NULL; + struct sk_buff *tail = NULL; + struct sk_buff *fskb = skb_shinfo(skb)->frag_list; +- skb_frag_t *skb_frag = skb_shinfo(skb)->frags; ++ skb_frag_t *frag = skb_shinfo(skb)->frags; + unsigned int mss = skb_shinfo(skb)->gso_size; + unsigned int doffset = skb->data - skb_mac_header(skb); + unsigned int offset = doffset; +@@ -2896,19 +2896,19 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) + + i = 0; + nfrags = skb_shinfo(fskb)->nr_frags; +- skb_frag = skb_shinfo(fskb)->frags; ++ frag = skb_shinfo(fskb)->frags; + pos += skb_headlen(fskb); + + while (pos < offset + len) { + BUG_ON(i >= nfrags); + +- size = skb_frag_size(skb_frag); ++ size = skb_frag_size(frag); + if (pos + size > offset + len) + break; + + i++; + pos += size; +- skb_frag++; ++ frag++; + } + + nskb = skb_clone(fskb, GFP_ATOMIC); +@@ -2982,7 +2982,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) + + i = 0; + nfrags = skb_shinfo(fskb)->nr_frags; +- skb_frag = skb_shinfo(fskb)->frags; ++ frag = skb_shinfo(fskb)->frags; + + BUG_ON(!nfrags); + +@@ -2997,7 +2997,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) + goto err; + } + +- *nskb_frag = *skb_frag; ++ *nskb_frag = *frag; + __skb_frag_ref(nskb_frag); + size = skb_frag_size(nskb_frag); + +@@ -3010,7 +3010,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) + + if (pos + size <= offset + len) { + i++; +- skb_frag++; ++ frag++; + pos += size; + } else { + skb_frag_size_sub(nskb_frag, pos + size - (offset + len)); +-- +1.8.5.3 + + +From df5771ffefb13f8af5392bd54fd7e2b596a3a357 Mon Sep 17 00:00:00 2001 +From: "Michael S. Tsirkin" +Date: Mon, 10 Mar 2014 18:29:19 +0200 +Subject: [PATCH 3/5] skbuff: skb_segment: s/skb/head_skb/ + +rename local variable to make it easier to tell at a glance that we are +dealing with a head skb. + +Signed-off-by: Michael S. Tsirkin +Signed-off-by: David S. Miller +--- + net/core/skbuff.c | 46 ++++++++++++++++++++++++---------------------- + 1 file changed, 24 insertions(+), 22 deletions(-) + +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index d788a98..fdc065d 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -2838,41 +2838,42 @@ EXPORT_SYMBOL_GPL(skb_pull_rcsum); + + /** + * skb_segment - Perform protocol segmentation on skb. +- * @skb: buffer to segment ++ * @head_skb: buffer to segment + * @features: features for the output path (see dev->features) + * + * This function performs segmentation on the given skb. It returns + * a pointer to the first in a list of new skbs for the segments. + * In case of error it returns ERR_PTR(err). + */ +-struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) ++struct sk_buff *skb_segment(struct sk_buff *head_skb, ++ netdev_features_t features) + { + struct sk_buff *segs = NULL; + struct sk_buff *tail = NULL; +- struct sk_buff *fskb = skb_shinfo(skb)->frag_list; +- skb_frag_t *frag = skb_shinfo(skb)->frags; +- unsigned int mss = skb_shinfo(skb)->gso_size; +- unsigned int doffset = skb->data - skb_mac_header(skb); ++ struct sk_buff *fskb = 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); + unsigned int offset = doffset; +- unsigned int tnl_hlen = skb_tnl_header_len(skb); ++ unsigned int tnl_hlen = skb_tnl_header_len(head_skb); + unsigned int headroom; + unsigned int len; + __be16 proto; + bool csum; + int sg = !!(features & NETIF_F_SG); +- int nfrags = skb_shinfo(skb)->nr_frags; ++ int nfrags = skb_shinfo(head_skb)->nr_frags; + int err = -ENOMEM; + int i = 0; + int pos; + +- proto = skb_network_protocol(skb); ++ proto = skb_network_protocol(head_skb); + if (unlikely(!proto)) + return ERR_PTR(-EINVAL); + + csum = !!can_checksum_protocol(features, proto); +- __skb_push(skb, doffset); +- headroom = skb_headroom(skb); +- pos = skb_headlen(skb); ++ __skb_push(head_skb, doffset); ++ headroom = skb_headroom(head_skb); ++ pos = skb_headlen(head_skb); + + do { + struct sk_buff *nskb; +@@ -2880,11 +2881,11 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) + int hsize; + int size; + +- len = skb->len - offset; ++ len = head_skb->len - offset; + if (len > mss) + len = mss; + +- hsize = skb_headlen(skb) - offset; ++ hsize = skb_headlen(head_skb) - offset; + if (hsize < 0) + hsize = 0; + if (hsize > len || !sg) +@@ -2933,7 +2934,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) + __skb_push(nskb, doffset); + } else { + nskb = __alloc_skb(hsize + doffset + headroom, +- GFP_ATOMIC, skb_alloc_rx_flag(skb), ++ GFP_ATOMIC, skb_alloc_rx_flag(head_skb), + NUMA_NO_NODE); + + if (unlikely(!nskb)) +@@ -2949,12 +2950,12 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) + segs = nskb; + tail = nskb; + +- __copy_skb_header(nskb, skb); +- nskb->mac_len = skb->mac_len; ++ __copy_skb_header(nskb, head_skb); ++ nskb->mac_len = head_skb->mac_len; + + skb_headers_offset_update(nskb, skb_headroom(nskb) - headroom); + +- skb_copy_from_linear_data_offset(skb, -tnl_hlen, ++ skb_copy_from_linear_data_offset(head_skb, -tnl_hlen, + nskb->data - tnl_hlen, + doffset + tnl_hlen); + +@@ -2963,7 +2964,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) + + if (!sg) { + nskb->ip_summed = CHECKSUM_NONE; +- nskb->csum = skb_copy_and_csum_bits(skb, offset, ++ nskb->csum = skb_copy_and_csum_bits(head_skb, offset, + skb_put(nskb, len), + len, 0); + continue; +@@ -2971,10 +2972,11 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) + + nskb_frag = skb_shinfo(nskb)->frags; + +- skb_copy_from_linear_data_offset(skb, offset, ++ skb_copy_from_linear_data_offset(head_skb, offset, + skb_put(nskb, hsize), hsize); + +- skb_shinfo(nskb)->tx_flags = skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG; ++ skb_shinfo(nskb)->tx_flags = skb_shinfo(head_skb)->tx_flags & ++ SKBTX_SHARED_FRAG; + + while (pos < offset + len) { + if (i >= nfrags) { +@@ -3031,7 +3033,7 @@ perform_csum_check: + nskb->len - doffset, 0); + nskb->ip_summed = CHECKSUM_NONE; + } +- } while ((offset += len) < skb->len); ++ } while ((offset += len) < head_skb->len); + + return segs; + +-- +1.8.5.3 + + +From 1a4cedaf65491e66e1e55b8428c89209da729209 Mon Sep 17 00:00:00 2001 +From: "Michael S. Tsirkin" +Date: Mon, 10 Mar 2014 19:27:59 +0200 +Subject: [PATCH 4/5] skbuff: skb_segment: s/fskb/list_skb/ + +fskb is unrelated to frag: it's coming from +frag_list. Rename it list_skb to avoid confusion. + +Signed-off-by: Michael S. Tsirkin +Signed-off-by: David S. Miller +--- + net/core/skbuff.c | 26 +++++++++++++------------- + 1 file changed, 13 insertions(+), 13 deletions(-) + +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index fdc065d..dc4f768 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -2850,7 +2850,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, + { + struct sk_buff *segs = NULL; + struct sk_buff *tail = NULL; +- struct sk_buff *fskb = skb_shinfo(head_skb)->frag_list; ++ 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); +@@ -2891,14 +2891,14 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, + if (hsize > len || !sg) + hsize = len; + +- if (!hsize && i >= nfrags && skb_headlen(fskb) && +- (skb_headlen(fskb) == len || sg)) { +- BUG_ON(skb_headlen(fskb) > len); ++ if (!hsize && i >= nfrags && skb_headlen(list_skb) && ++ (skb_headlen(list_skb) == len || sg)) { ++ BUG_ON(skb_headlen(list_skb) > len); + + i = 0; +- nfrags = skb_shinfo(fskb)->nr_frags; +- frag = skb_shinfo(fskb)->frags; +- pos += skb_headlen(fskb); ++ nfrags = skb_shinfo(list_skb)->nr_frags; ++ frag = skb_shinfo(list_skb)->frags; ++ pos += skb_headlen(list_skb); + + while (pos < offset + len) { + BUG_ON(i >= nfrags); +@@ -2912,8 +2912,8 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, + frag++; + } + +- nskb = skb_clone(fskb, GFP_ATOMIC); +- fskb = fskb->next; ++ nskb = skb_clone(list_skb, GFP_ATOMIC); ++ list_skb = list_skb->next; + + if (unlikely(!nskb)) + goto err; +@@ -2980,15 +2980,15 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, + + while (pos < offset + len) { + if (i >= nfrags) { +- BUG_ON(skb_headlen(fskb)); ++ BUG_ON(skb_headlen(list_skb)); + + i = 0; +- nfrags = skb_shinfo(fskb)->nr_frags; +- frag = skb_shinfo(fskb)->frags; ++ nfrags = skb_shinfo(list_skb)->nr_frags; ++ frag = skb_shinfo(list_skb)->frags; + + BUG_ON(!nfrags); + +- fskb = fskb->next; ++ list_skb = list_skb->next; + } + + if (unlikely(skb_shinfo(nskb)->nr_frags >= +-- +1.8.5.3 + + +From 1fd819ecb90cc9b822cd84d3056ddba315d3340f Mon Sep 17 00:00:00 2001 +From: "Michael S. Tsirkin" +Date: Mon, 10 Mar 2014 19:28:08 +0200 +Subject: [PATCH 5/5] 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 +Acked-by: Herbert Xu +Signed-off-by: David S. Miller +--- + net/core/skbuff.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index dc4f768..869c7af 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -2854,6 +2854,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, + 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; +@@ -2898,6 +2899,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, + 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) { +@@ -2985,6 +2987,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, + i = 0; + nfrags = skb_shinfo(list_skb)->nr_frags; + frag = skb_shinfo(list_skb)->frags; ++ frag_skb = list_skb; + + BUG_ON(!nfrags); + +@@ -2999,6 +3002,9 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, + 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); +-- +1.8.5.3 +