]>
Commit | Line | Data |
---|---|---|
8dced6ec GKH |
1 | From foo@baz Thu May 11 11:08:24 CEST 2017 |
2 | From: Eric Dumazet <edumazet@google.com> | |
3 | Date: Wed, 26 Apr 2017 17:15:40 -0700 | |
4 | Subject: tcp: do not underestimate skb->truesize in tcp_trim_head() | |
5 | ||
6 | From: Eric Dumazet <edumazet@google.com> | |
7 | ||
8 | ||
9 | [ Upstream commit 7162fb242cb8322beb558828fd26b33c3e9fc805 ] | |
10 | ||
11 | Andrey found a way to trigger the WARN_ON_ONCE(delta < len) in | |
12 | skb_try_coalesce() using syzkaller and a filter attached to a TCP | |
13 | socket over loopback interface. | |
14 | ||
15 | I believe one issue with looped skbs is that tcp_trim_head() can end up | |
16 | producing skb with under estimated truesize. | |
17 | ||
18 | It hardly matters for normal conditions, since packets sent over | |
19 | loopback are never truncated. | |
20 | ||
21 | Bytes trimmed from skb->head should not change skb truesize, since | |
22 | skb->head is not reallocated. | |
23 | ||
24 | Signed-off-by: Eric Dumazet <edumazet@google.com> | |
25 | Reported-by: Andrey Konovalov <andreyknvl@google.com> | |
26 | Tested-by: Andrey Konovalov <andreyknvl@google.com> | |
27 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
28 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
29 | --- | |
30 | net/ipv4/tcp_output.c | 19 ++++++++++++------- | |
31 | 1 file changed, 12 insertions(+), 7 deletions(-) | |
32 | ||
33 | --- a/net/ipv4/tcp_output.c | |
34 | +++ b/net/ipv4/tcp_output.c | |
35 | @@ -1246,7 +1246,7 @@ int tcp_fragment(struct sock *sk, struct | |
36 | * eventually). The difference is that pulled data not copied, but | |
37 | * immediately discarded. | |
38 | */ | |
39 | -static void __pskb_trim_head(struct sk_buff *skb, int len) | |
40 | +static int __pskb_trim_head(struct sk_buff *skb, int len) | |
41 | { | |
42 | struct skb_shared_info *shinfo; | |
43 | int i, k, eat; | |
44 | @@ -1256,7 +1256,7 @@ static void __pskb_trim_head(struct sk_b | |
45 | __skb_pull(skb, eat); | |
46 | len -= eat; | |
47 | if (!len) | |
48 | - return; | |
49 | + return 0; | |
50 | } | |
51 | eat = len; | |
52 | k = 0; | |
53 | @@ -1282,23 +1282,28 @@ static void __pskb_trim_head(struct sk_b | |
54 | skb_reset_tail_pointer(skb); | |
55 | skb->data_len -= len; | |
56 | skb->len = skb->data_len; | |
57 | + return len; | |
58 | } | |
59 | ||
60 | /* Remove acked data from a packet in the transmit queue. */ | |
61 | int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) | |
62 | { | |
63 | + u32 delta_truesize; | |
64 | + | |
65 | if (skb_unclone(skb, GFP_ATOMIC)) | |
66 | return -ENOMEM; | |
67 | ||
68 | - __pskb_trim_head(skb, len); | |
69 | + delta_truesize = __pskb_trim_head(skb, len); | |
70 | ||
71 | TCP_SKB_CB(skb)->seq += len; | |
72 | skb->ip_summed = CHECKSUM_PARTIAL; | |
73 | ||
74 | - skb->truesize -= len; | |
75 | - sk->sk_wmem_queued -= len; | |
76 | - sk_mem_uncharge(sk, len); | |
77 | - sock_set_flag(sk, SOCK_QUEUE_SHRUNK); | |
78 | + if (delta_truesize) { | |
79 | + skb->truesize -= delta_truesize; | |
80 | + sk->sk_wmem_queued -= delta_truesize; | |
81 | + sk_mem_uncharge(sk, delta_truesize); | |
82 | + sock_set_flag(sk, SOCK_QUEUE_SHRUNK); | |
83 | + } | |
84 | ||
85 | /* Any change of skb->len requires recalculation of tso factor. */ | |
86 | if (tcp_skb_pcount(skb) > 1) |