Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tcp: take care of misalignments

We discovered that TCP stack could retransmit misaligned skbs if a
malicious peer acknowledged sub MSS frame. This currently can happen
only if output interface is non SG enabled : If SG is enabled, tcp
builds headless skbs (all payload is included in fragments), so the tcp
trimming process only removes parts of skb fragments, header stay
aligned.

Some arches cant handle misalignments, so force a head reallocation and
shrink headroom to MAX_TCP_HEADER.

Dont care about misaligments on x86 and PPC (or other arches setting
NET_IP_ALIGN to 0)

This patch introduces __pskb_copy() which can specify the headroom of
new head, and pskb_copy() becomes a wrapper on top of __pskb_copy()

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eric Dumazet and committed by
David S. Miller
117632e6 c2e4e25a

+24 -8
+9 -2
include/linux/skbuff.h
··· 568 568 gfp_t priority); 569 569 extern struct sk_buff *skb_copy(const struct sk_buff *skb, 570 570 gfp_t priority); 571 - extern struct sk_buff *pskb_copy(struct sk_buff *skb, 572 - gfp_t gfp_mask); 571 + extern struct sk_buff *__pskb_copy(struct sk_buff *skb, 572 + int headroom, gfp_t gfp_mask); 573 + 573 574 extern int pskb_expand_head(struct sk_buff *skb, 574 575 int nhead, int ntail, 575 576 gfp_t gfp_mask); ··· 1798 1797 { 1799 1798 return dma_map_page(dev, skb_frag_page(frag), 1800 1799 frag->page_offset + offset, size, dir); 1800 + } 1801 + 1802 + static inline struct sk_buff *pskb_copy(struct sk_buff *skb, 1803 + gfp_t gfp_mask) 1804 + { 1805 + return __pskb_copy(skb, skb_headroom(skb), gfp_mask); 1801 1806 } 1802 1807 1803 1808 /**
+6 -5
net/core/skbuff.c
··· 840 840 EXPORT_SYMBOL(skb_copy); 841 841 842 842 /** 843 - * pskb_copy - create copy of an sk_buff with private head. 843 + * __pskb_copy - create copy of an sk_buff with private head. 844 844 * @skb: buffer to copy 845 + * @headroom: headroom of new skb 845 846 * @gfp_mask: allocation priority 846 847 * 847 848 * Make a copy of both an &sk_buff and part of its data, located ··· 853 852 * The returned buffer has a reference count of 1. 854 853 */ 855 854 856 - struct sk_buff *pskb_copy(struct sk_buff *skb, gfp_t gfp_mask) 855 + struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask) 857 856 { 858 - unsigned int size = skb_end_pointer(skb) - skb->head; 857 + unsigned int size = skb_headlen(skb) + headroom; 859 858 struct sk_buff *n = alloc_skb(size, gfp_mask); 860 859 861 860 if (!n) 862 861 goto out; 863 862 864 863 /* Set the data pointer */ 865 - skb_reserve(n, skb_headroom(skb)); 864 + skb_reserve(n, headroom); 866 865 /* Set the tail pointer and length */ 867 866 skb_put(n, skb_headlen(skb)); 868 867 /* Copy the bytes */ ··· 898 897 out: 899 898 return n; 900 899 } 901 - EXPORT_SYMBOL(pskb_copy); 900 + EXPORT_SYMBOL(__pskb_copy); 902 901 903 902 /** 904 903 * pskb_expand_head - reallocate header of &sk_buff
+9 -1
net/ipv4/tcp_output.c
··· 2147 2147 */ 2148 2148 TCP_SKB_CB(skb)->when = tcp_time_stamp; 2149 2149 2150 - err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC); 2150 + /* make sure skb->data is aligned on arches that require it */ 2151 + if (unlikely(NET_IP_ALIGN && ((unsigned long)skb->data & 3))) { 2152 + struct sk_buff *nskb = __pskb_copy(skb, MAX_TCP_HEADER, 2153 + GFP_ATOMIC); 2154 + err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) : 2155 + -ENOBUFS; 2156 + } else { 2157 + err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC); 2158 + } 2151 2159 2152 2160 if (err == 0) { 2153 2161 /* Update global TCP statistics. */