Commit 7162fb24 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

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: default avatarEric Dumazet <edumazet@google.com>
Reported-by: default avatarAndrey Konovalov <andreyknvl@google.com>
Tested-by: default avatarAndrey Konovalov <andreyknvl@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 19cdead3
...@@ -1267,7 +1267,7 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, ...@@ -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 * eventually). The difference is that pulled data not copied, but
* immediately discarded. * 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; struct skb_shared_info *shinfo;
int i, k, eat; int i, k, eat;
...@@ -1277,7 +1277,7 @@ static void __pskb_trim_head(struct sk_buff *skb, int len) ...@@ -1277,7 +1277,7 @@ static void __pskb_trim_head(struct sk_buff *skb, int len)
__skb_pull(skb, eat); __skb_pull(skb, eat);
len -= eat; len -= eat;
if (!len) if (!len)
return; return 0;
} }
eat = len; eat = len;
k = 0; k = 0;
...@@ -1303,23 +1303,28 @@ static void __pskb_trim_head(struct sk_buff *skb, int len) ...@@ -1303,23 +1303,28 @@ static void __pskb_trim_head(struct sk_buff *skb, int len)
skb_reset_tail_pointer(skb); skb_reset_tail_pointer(skb);
skb->data_len -= len; skb->data_len -= len;
skb->len = skb->data_len; skb->len = skb->data_len;
return len;
} }
/* Remove acked data from a packet in the transmit queue. */ /* Remove acked data from a packet in the transmit queue. */
int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len)
{ {
u32 delta_truesize;
if (skb_unclone(skb, GFP_ATOMIC)) if (skb_unclone(skb, GFP_ATOMIC))
return -ENOMEM; return -ENOMEM;
__pskb_trim_head(skb, len); delta_truesize = __pskb_trim_head(skb, len);
TCP_SKB_CB(skb)->seq += len; TCP_SKB_CB(skb)->seq += len;
skb->ip_summed = CHECKSUM_PARTIAL; skb->ip_summed = CHECKSUM_PARTIAL;
skb->truesize -= len; if (delta_truesize) {
sk->sk_wmem_queued -= len; skb->truesize -= delta_truesize;
sk_mem_uncharge(sk, len); sk->sk_wmem_queued -= delta_truesize;
sock_set_flag(sk, SOCK_QUEUE_SHRUNK); sk_mem_uncharge(sk, delta_truesize);
sock_set_flag(sk, SOCK_QUEUE_SHRUNK);
}
/* Any change of skb->len requires recalculation of tso factor. */ /* Any change of skb->len requires recalculation of tso factor. */
if (tcp_skb_pcount(skb) > 1) if (tcp_skb_pcount(skb) > 1)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment