diff options
author | Eric Dumazet <edumazet@google.com> | 2017-04-26 20:15:40 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-04-28 16:05:22 -0400 |
commit | 7162fb242cb8322beb558828fd26b33c3e9fc805 (patch) | |
tree | 1d4240e28de77ffaf2180f17f3314b26a7bd1258 /net/ipv4/tcp_output.c | |
parent | 19cdead3e2ef8ed765c5d1ce48057ca9d97b5094 (diff) |
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, | |||
1267 | * eventually). The difference is that pulled data not copied, but | 1267 | * eventually). The difference is that pulled data not copied, but |
1268 | * immediately discarded. | 1268 | * immediately discarded. |
1269 | */ | 1269 | */ |
1270 | static void __pskb_trim_head(struct sk_buff *skb, int len) | 1270 | static int __pskb_trim_head(struct sk_buff *skb, int len) |
1271 | { | 1271 | { |
1272 | struct skb_shared_info *shinfo; | 1272 | struct skb_shared_info *shinfo; |
1273 | int i, k, eat; | 1273 | int i, k, eat; |
@@ -1277,7 +1277,7 @@ static void __pskb_trim_head(struct sk_buff *skb, int len) | |||
1277 | __skb_pull(skb, eat); | 1277 | __skb_pull(skb, eat); |
1278 | len -= eat; | 1278 | len -= eat; |
1279 | if (!len) | 1279 | if (!len) |
1280 | return; | 1280 | return 0; |
1281 | } | 1281 | } |
1282 | eat = len; | 1282 | eat = len; |
1283 | k = 0; | 1283 | k = 0; |
@@ -1303,23 +1303,28 @@ static void __pskb_trim_head(struct sk_buff *skb, int len) | |||
1303 | skb_reset_tail_pointer(skb); | 1303 | skb_reset_tail_pointer(skb); |
1304 | skb->data_len -= len; | 1304 | skb->data_len -= len; |
1305 | skb->len = skb->data_len; | 1305 | skb->len = skb->data_len; |
1306 | return len; | ||
1306 | } | 1307 | } |
1307 | 1308 | ||
1308 | /* Remove acked data from a packet in the transmit queue. */ | 1309 | /* Remove acked data from a packet in the transmit queue. */ |
1309 | int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) | 1310 | int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) |
1310 | { | 1311 | { |
1312 | u32 delta_truesize; | ||
1313 | |||
1311 | if (skb_unclone(skb, GFP_ATOMIC)) | 1314 | if (skb_unclone(skb, GFP_ATOMIC)) |
1312 | return -ENOMEM; | 1315 | return -ENOMEM; |
1313 | 1316 | ||
1314 | __pskb_trim_head(skb, len); | 1317 | delta_truesize = __pskb_trim_head(skb, len); |
1315 | 1318 | ||
1316 | TCP_SKB_CB(skb)->seq += len; | 1319 | TCP_SKB_CB(skb)->seq += len; |
1317 | skb->ip_summed = CHECKSUM_PARTIAL; | 1320 | skb->ip_summed = CHECKSUM_PARTIAL; |
1318 | 1321 | ||
1319 | skb->truesize -= len; | 1322 | if (delta_truesize) { |
1320 | sk->sk_wmem_queued -= len; | 1323 | skb->truesize -= delta_truesize; |
1321 | sk_mem_uncharge(sk, len); | 1324 | sk->sk_wmem_queued -= delta_truesize; |
1322 | sock_set_flag(sk, SOCK_QUEUE_SHRUNK); | 1325 | sk_mem_uncharge(sk, delta_truesize); |
1326 | sock_set_flag(sk, SOCK_QUEUE_SHRUNK); | ||
1327 | } | ||
1323 | 1328 | ||
1324 | /* Any change of skb->len requires recalculation of tso factor. */ | 1329 | /* Any change of skb->len requires recalculation of tso factor. */ |
1325 | if (tcp_skb_pcount(skb) > 1) | 1330 | if (tcp_skb_pcount(skb) > 1) |