diff options
| author | Eric Dumazet <edumazet@google.com> | 2017-04-27 03:15:40 +0300 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2017-04-28 23:05:22 +0300 | 
| commit | 7162fb242cb8322beb558828fd26b33c3e9fc805 (patch) | |
| tree | 1d4240e28de77ffaf2180f17f3314b26a7bd1258 /net/ipv4/tcp_output.c | |
| parent | 19cdead3e2ef8ed765c5d1ce48057ca9d97b5094 (diff) | |
| download | linux-7162fb242cb8322beb558828fd26b33c3e9fc805.tar.xz | |
tcp: do not underestimate skb->truesize in tcp_trim_head()
Andrey found a way to trigger the WARN_ON_ONCE(delta < len) in
skb_try_coalesce() using syzkaller and a filter attached to a TCP
socket over loopback interface.
I believe one issue with looped skbs is that tcp_trim_head() can end up
producing skb with under estimated truesize.
It hardly matters for normal conditions, since packets sent over
loopback are never truncated.
Bytes trimmed from skb->head should not change skb truesize, since
skb->head is not reallocated.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Andrey Konovalov <andreyknvl@google.com>
Tested-by: Andrey Konovalov <andreyknvl@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_output.c')
| -rw-r--r-- | net/ipv4/tcp_output.c | 19 | 
1 files changed, 12 insertions, 7 deletions
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index c3c082ed3879..a85d863c4419 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1267,7 +1267,7 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len,   * eventually). The difference is that pulled data not copied, but   * immediately discarded.   */ -static void __pskb_trim_head(struct sk_buff *skb, int len) +static int __pskb_trim_head(struct sk_buff *skb, int len)  {  	struct skb_shared_info *shinfo;  	int i, k, eat; @@ -1277,7 +1277,7 @@ static void __pskb_trim_head(struct sk_buff *skb, int len)  		__skb_pull(skb, eat);  		len -= eat;  		if (!len) -			return; +			return 0;  	}  	eat = len;  	k = 0; @@ -1303,23 +1303,28 @@ static void __pskb_trim_head(struct sk_buff *skb, int len)  	skb_reset_tail_pointer(skb);  	skb->data_len -= len;  	skb->len = skb->data_len; +	return len;  }  /* Remove acked data from a packet in the transmit queue. */  int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len)  { +	u32 delta_truesize; +  	if (skb_unclone(skb, GFP_ATOMIC))  		return -ENOMEM; -	__pskb_trim_head(skb, len); +	delta_truesize = __pskb_trim_head(skb, len);  	TCP_SKB_CB(skb)->seq += len;  	skb->ip_summed = CHECKSUM_PARTIAL; -	skb->truesize	     -= len; -	sk->sk_wmem_queued   -= len; -	sk_mem_uncharge(sk, len); -	sock_set_flag(sk, SOCK_QUEUE_SHRUNK); +	if (delta_truesize) { +		skb->truesize	   -= delta_truesize; +		sk->sk_wmem_queued -= delta_truesize; +		sk_mem_uncharge(sk, delta_truesize); +		sock_set_flag(sk, SOCK_QUEUE_SHRUNK); +	}  	/* Any change of skb->len requires recalculation of tso factor. */  	if (tcp_skb_pcount(skb) > 1)  | 
