aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2014-03-11 16:26:46 -0400
committerDavid S. Miller <davem@davemloft.net>2014-03-11 16:26:46 -0400
commitc7b76f854e01cfdfdfaeb22b2ba1350be5d09ad7 (patch)
treea6ebab17f5c69d2c9ef08aa5fa1426201adfa7e7
parent9d79b3c7aae582df9eebcea8797c41cd5d5fc39c (diff)
parent1fd819ecb90cc9b822cd84d3056ddba315d3340f (diff)
Merge branch 'skb_frags'
Michael S. Tsirkin says: ==================== skbuff: fix skb_segment with zero copy skbs This fixes a bug in skb_segment where it moves frags between skbs without orphaning them. This causes userspace to assume it's safe to reuse the buffer, and receiver gets corrupted data. This further might leak information from the transmitter on the wire. To fix track which skb does a copied frag belong to, and orphan frags when copying them. As we are tracking multiple skbs here, using short names (skb,nskb,fskb,skb_frag,frag) becomes confusing. So before adding another one, I refactor these names slightly. Patch is split out to make it easier to verify that all trasformations are trivially correct. The problem was observed in the field, so I think that the patch is necessary on stable as well. ==================== Signed-off-by: David S. Miller <davem@davemloft.net> Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
-rw-r--r--net/core/skbuff.c100
1 files changed, 54 insertions, 46 deletions
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 5d6236d9fdce..869c7afe3b07 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2838,81 +2838,84 @@ EXPORT_SYMBOL_GPL(skb_pull_rcsum);
2838 2838
2839/** 2839/**
2840 * skb_segment - Perform protocol segmentation on skb. 2840 * skb_segment - Perform protocol segmentation on skb.
2841 * @skb: buffer to segment 2841 * @head_skb: buffer to segment
2842 * @features: features for the output path (see dev->features) 2842 * @features: features for the output path (see dev->features)
2843 * 2843 *
2844 * This function performs segmentation on the given skb. It returns 2844 * This function performs segmentation on the given skb. It returns
2845 * a pointer to the first in a list of new skbs for the segments. 2845 * a pointer to the first in a list of new skbs for the segments.
2846 * In case of error it returns ERR_PTR(err). 2846 * In case of error it returns ERR_PTR(err).
2847 */ 2847 */
2848struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) 2848struct sk_buff *skb_segment(struct sk_buff *head_skb,
2849 netdev_features_t features)
2849{ 2850{
2850 struct sk_buff *segs = NULL; 2851 struct sk_buff *segs = NULL;
2851 struct sk_buff *tail = NULL; 2852 struct sk_buff *tail = NULL;
2852 struct sk_buff *fskb = skb_shinfo(skb)->frag_list; 2853 struct sk_buff *list_skb = skb_shinfo(head_skb)->frag_list;
2853 skb_frag_t *skb_frag = skb_shinfo(skb)->frags; 2854 skb_frag_t *frag = skb_shinfo(head_skb)->frags;
2854 unsigned int mss = skb_shinfo(skb)->gso_size; 2855 unsigned int mss = skb_shinfo(head_skb)->gso_size;
2855 unsigned int doffset = skb->data - skb_mac_header(skb); 2856 unsigned int doffset = head_skb->data - skb_mac_header(head_skb);
2857 struct sk_buff *frag_skb = head_skb;
2856 unsigned int offset = doffset; 2858 unsigned int offset = doffset;
2857 unsigned int tnl_hlen = skb_tnl_header_len(skb); 2859 unsigned int tnl_hlen = skb_tnl_header_len(head_skb);
2858 unsigned int headroom; 2860 unsigned int headroom;
2859 unsigned int len; 2861 unsigned int len;
2860 __be16 proto; 2862 __be16 proto;
2861 bool csum; 2863 bool csum;
2862 int sg = !!(features & NETIF_F_SG); 2864 int sg = !!(features & NETIF_F_SG);
2863 int nfrags = skb_shinfo(skb)->nr_frags; 2865 int nfrags = skb_shinfo(head_skb)->nr_frags;
2864 int err = -ENOMEM; 2866 int err = -ENOMEM;
2865 int i = 0; 2867 int i = 0;
2866 int pos; 2868 int pos;
2867 2869
2868 proto = skb_network_protocol(skb); 2870 proto = skb_network_protocol(head_skb);
2869 if (unlikely(!proto)) 2871 if (unlikely(!proto))
2870 return ERR_PTR(-EINVAL); 2872 return ERR_PTR(-EINVAL);
2871 2873
2872 csum = !!can_checksum_protocol(features, proto); 2874 csum = !!can_checksum_protocol(features, proto);
2873 __skb_push(skb, doffset); 2875 __skb_push(head_skb, doffset);
2874 headroom = skb_headroom(skb); 2876 headroom = skb_headroom(head_skb);
2875 pos = skb_headlen(skb); 2877 pos = skb_headlen(head_skb);
2876 2878
2877 do { 2879 do {
2878 struct sk_buff *nskb; 2880 struct sk_buff *nskb;
2879 skb_frag_t *frag; 2881 skb_frag_t *nskb_frag;
2880 int hsize; 2882 int hsize;
2881 int size; 2883 int size;
2882 2884
2883 len = skb->len - offset; 2885 len = head_skb->len - offset;
2884 if (len > mss) 2886 if (len > mss)
2885 len = mss; 2887 len = mss;
2886 2888
2887 hsize = skb_headlen(skb) - offset; 2889 hsize = skb_headlen(head_skb) - offset;
2888 if (hsize < 0) 2890 if (hsize < 0)
2889 hsize = 0; 2891 hsize = 0;
2890 if (hsize > len || !sg) 2892 if (hsize > len || !sg)
2891 hsize = len; 2893 hsize = len;
2892 2894
2893 if (!hsize && i >= nfrags && skb_headlen(fskb) && 2895 if (!hsize && i >= nfrags && skb_headlen(list_skb) &&
2894 (skb_headlen(fskb) == len || sg)) { 2896 (skb_headlen(list_skb) == len || sg)) {
2895 BUG_ON(skb_headlen(fskb) > len); 2897 BUG_ON(skb_headlen(list_skb) > len);
2896 2898
2897 i = 0; 2899 i = 0;
2898 nfrags = skb_shinfo(fskb)->nr_frags; 2900 nfrags = skb_shinfo(list_skb)->nr_frags;
2899 skb_frag = skb_shinfo(fskb)->frags; 2901 frag = skb_shinfo(list_skb)->frags;
2900 pos += skb_headlen(fskb); 2902 frag_skb = list_skb;
2903 pos += skb_headlen(list_skb);
2901 2904
2902 while (pos < offset + len) { 2905 while (pos < offset + len) {
2903 BUG_ON(i >= nfrags); 2906 BUG_ON(i >= nfrags);
2904 2907
2905 size = skb_frag_size(skb_frag); 2908 size = skb_frag_size(frag);
2906 if (pos + size > offset + len) 2909 if (pos + size > offset + len)
2907 break; 2910 break;
2908 2911
2909 i++; 2912 i++;
2910 pos += size; 2913 pos += size;
2911 skb_frag++; 2914 frag++;
2912 } 2915 }
2913 2916
2914 nskb = skb_clone(fskb, GFP_ATOMIC); 2917 nskb = skb_clone(list_skb, GFP_ATOMIC);
2915 fskb = fskb->next; 2918 list_skb = list_skb->next;
2916 2919
2917 if (unlikely(!nskb)) 2920 if (unlikely(!nskb))
2918 goto err; 2921 goto err;
@@ -2933,7 +2936,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
2933 __skb_push(nskb, doffset); 2936 __skb_push(nskb, doffset);
2934 } else { 2937 } else {
2935 nskb = __alloc_skb(hsize + doffset + headroom, 2938 nskb = __alloc_skb(hsize + doffset + headroom,
2936 GFP_ATOMIC, skb_alloc_rx_flag(skb), 2939 GFP_ATOMIC, skb_alloc_rx_flag(head_skb),
2937 NUMA_NO_NODE); 2940 NUMA_NO_NODE);
2938 2941
2939 if (unlikely(!nskb)) 2942 if (unlikely(!nskb))
@@ -2949,12 +2952,12 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
2949 segs = nskb; 2952 segs = nskb;
2950 tail = nskb; 2953 tail = nskb;
2951 2954
2952 __copy_skb_header(nskb, skb); 2955 __copy_skb_header(nskb, head_skb);
2953 nskb->mac_len = skb->mac_len; 2956 nskb->mac_len = head_skb->mac_len;
2954 2957
2955 skb_headers_offset_update(nskb, skb_headroom(nskb) - headroom); 2958 skb_headers_offset_update(nskb, skb_headroom(nskb) - headroom);
2956 2959
2957 skb_copy_from_linear_data_offset(skb, -tnl_hlen, 2960 skb_copy_from_linear_data_offset(head_skb, -tnl_hlen,
2958 nskb->data - tnl_hlen, 2961 nskb->data - tnl_hlen,
2959 doffset + tnl_hlen); 2962 doffset + tnl_hlen);
2960 2963
@@ -2963,30 +2966,32 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
2963 2966
2964 if (!sg) { 2967 if (!sg) {
2965 nskb->ip_summed = CHECKSUM_NONE; 2968 nskb->ip_summed = CHECKSUM_NONE;
2966 nskb->csum = skb_copy_and_csum_bits(skb, offset, 2969 nskb->csum = skb_copy_and_csum_bits(head_skb, offset,
2967 skb_put(nskb, len), 2970 skb_put(nskb, len),
2968 len, 0); 2971 len, 0);
2969 continue; 2972 continue;
2970 } 2973 }
2971 2974
2972 frag = skb_shinfo(nskb)->frags; 2975 nskb_frag = skb_shinfo(nskb)->frags;
2973 2976
2974 skb_copy_from_linear_data_offset(skb, offset, 2977 skb_copy_from_linear_data_offset(head_skb, offset,
2975 skb_put(nskb, hsize), hsize); 2978 skb_put(nskb, hsize), hsize);
2976 2979
2977 skb_shinfo(nskb)->tx_flags = skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG; 2980 skb_shinfo(nskb)->tx_flags = skb_shinfo(head_skb)->tx_flags &
2981 SKBTX_SHARED_FRAG;
2978 2982
2979 while (pos < offset + len) { 2983 while (pos < offset + len) {
2980 if (i >= nfrags) { 2984 if (i >= nfrags) {
2981 BUG_ON(skb_headlen(fskb)); 2985 BUG_ON(skb_headlen(list_skb));
2982 2986
2983 i = 0; 2987 i = 0;
2984 nfrags = skb_shinfo(fskb)->nr_frags; 2988 nfrags = skb_shinfo(list_skb)->nr_frags;
2985 skb_frag = skb_shinfo(fskb)->frags; 2989 frag = skb_shinfo(list_skb)->frags;
2990 frag_skb = list_skb;
2986 2991
2987 BUG_ON(!nfrags); 2992 BUG_ON(!nfrags);
2988 2993
2989 fskb = fskb->next; 2994 list_skb = list_skb->next;
2990 } 2995 }
2991 2996
2992 if (unlikely(skb_shinfo(nskb)->nr_frags >= 2997 if (unlikely(skb_shinfo(nskb)->nr_frags >=
@@ -2997,27 +3002,30 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
2997 goto err; 3002 goto err;
2998 } 3003 }
2999 3004
3000 *frag = *skb_frag; 3005 if (unlikely(skb_orphan_frags(frag_skb, GFP_ATOMIC)))
3001 __skb_frag_ref(frag); 3006 goto err;
3002 size = skb_frag_size(frag); 3007
3008 *nskb_frag = *frag;
3009 __skb_frag_ref(nskb_frag);
3010 size = skb_frag_size(nskb_frag);
3003 3011
3004 if (pos < offset) { 3012 if (pos < offset) {
3005 frag->page_offset += offset - pos; 3013 nskb_frag->page_offset += offset - pos;
3006 skb_frag_size_sub(frag, offset - pos); 3014 skb_frag_size_sub(nskb_frag, offset - pos);
3007 } 3015 }
3008 3016
3009 skb_shinfo(nskb)->nr_frags++; 3017 skb_shinfo(nskb)->nr_frags++;
3010 3018
3011 if (pos + size <= offset + len) { 3019 if (pos + size <= offset + len) {
3012 i++; 3020 i++;
3013 skb_frag++; 3021 frag++;
3014 pos += size; 3022 pos += size;
3015 } else { 3023 } else {
3016 skb_frag_size_sub(frag, pos + size - (offset + len)); 3024 skb_frag_size_sub(nskb_frag, pos + size - (offset + len));
3017 goto skip_fraglist; 3025 goto skip_fraglist;
3018 } 3026 }
3019 3027
3020 frag++; 3028 nskb_frag++;
3021 } 3029 }
3022 3030
3023skip_fraglist: 3031skip_fraglist:
@@ -3031,7 +3039,7 @@ perform_csum_check:
3031 nskb->len - doffset, 0); 3039 nskb->len - doffset, 0);
3032 nskb->ip_summed = CHECKSUM_NONE; 3040 nskb->ip_summed = CHECKSUM_NONE;
3033 } 3041 }
3034 } while ((offset += len) < skb->len); 3042 } while ((offset += len) < head_skb->len);
3035 3043
3036 return segs; 3044 return segs;
3037 3045