Skip to content

Commit

Permalink
tcp: do not underestimate skb->truesize in tcp_trim_head()
Browse files Browse the repository at this point in the history
[ Upstream commit 7162fb2 ]

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Eric Dumazet authored and gregkh committed May 14, 2017
1 parent 3b0129d commit f3235cb
Showing 1 changed file with 12 additions and 7 deletions.
19 changes: 12 additions & 7 deletions net/ipv4/tcp_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -1246,7 +1246,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;
Expand All @@ -1256,7 +1256,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;
Expand All @@ -1282,23 +1282,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)
Expand Down

0 comments on commit f3235cb

Please sign in to comment.