diff options
author | Eric Dumazet <edumazet@google.com> | 2017-04-26 20:15:40 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-05-14 08:00:20 -0400 |
commit | f3235cbd5be15aa084d5561c2eb8492ed68cd7e5 (patch) | |
tree | c661126b93152af4b9fe70d2c04e1f143acbcdd3 | |
parent | 3b0129d4111e53927c2bc3c6b78a2b12ad71268b (diff) |
tcp: do not underestimate skb->truesize in tcp_trim_head()
[ Upstream commit 7162fb242cb8322beb558828fd26b33c3e9fc805 ]
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>
-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 65d6189140bc..dc4258fd15dc 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c | |||
@@ -1246,7 +1246,7 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, | |||
1246 | * eventually). The difference is that pulled data not copied, but | 1246 | * eventually). The difference is that pulled data not copied, but |
1247 | * immediately discarded. | 1247 | * immediately discarded. |
1248 | */ | 1248 | */ |
1249 | static void __pskb_trim_head(struct sk_buff *skb, int len) | 1249 | static int __pskb_trim_head(struct sk_buff *skb, int len) |
1250 | { | 1250 | { |
1251 | struct skb_shared_info *shinfo; | 1251 | struct skb_shared_info *shinfo; |
1252 | int i, k, eat; | 1252 | int i, k, eat; |
@@ -1256,7 +1256,7 @@ static void __pskb_trim_head(struct sk_buff *skb, int len) | |||
1256 | __skb_pull(skb, eat); | 1256 | __skb_pull(skb, eat); |
1257 | len -= eat; | 1257 | len -= eat; |
1258 | if (!len) | 1258 | if (!len) |
1259 | return; | 1259 | return 0; |
1260 | } | 1260 | } |
1261 | eat = len; | 1261 | eat = len; |
1262 | k = 0; | 1262 | k = 0; |
@@ -1282,23 +1282,28 @@ static void __pskb_trim_head(struct sk_buff *skb, int len) | |||
1282 | skb_reset_tail_pointer(skb); | 1282 | skb_reset_tail_pointer(skb); |
1283 | skb->data_len -= len; | 1283 | skb->data_len -= len; |
1284 | skb->len = skb->data_len; | 1284 | skb->len = skb->data_len; |
1285 | return len; | ||
1285 | } | 1286 | } |
1286 | 1287 | ||
1287 | /* Remove acked data from a packet in the transmit queue. */ | 1288 | /* Remove acked data from a packet in the transmit queue. */ |
1288 | int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) | 1289 | int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) |
1289 | { | 1290 | { |
1291 | u32 delta_truesize; | ||
1292 | |||
1290 | if (skb_unclone(skb, GFP_ATOMIC)) | 1293 | if (skb_unclone(skb, GFP_ATOMIC)) |
1291 | return -ENOMEM; | 1294 | return -ENOMEM; |
1292 | 1295 | ||
1293 | __pskb_trim_head(skb, len); | 1296 | delta_truesize = __pskb_trim_head(skb, len); |
1294 | 1297 | ||
1295 | TCP_SKB_CB(skb)->seq += len; | 1298 | TCP_SKB_CB(skb)->seq += len; |
1296 | skb->ip_summed = CHECKSUM_PARTIAL; | 1299 | skb->ip_summed = CHECKSUM_PARTIAL; |
1297 | 1300 | ||
1298 | skb->truesize -= len; | 1301 | if (delta_truesize) { |
1299 | sk->sk_wmem_queued -= len; | 1302 | skb->truesize -= delta_truesize; |
1300 | sk_mem_uncharge(sk, len); | 1303 | sk->sk_wmem_queued -= delta_truesize; |
1301 | sock_set_flag(sk, SOCK_QUEUE_SHRUNK); | 1304 | sk_mem_uncharge(sk, delta_truesize); |
1305 | sock_set_flag(sk, SOCK_QUEUE_SHRUNK); | ||
1306 | } | ||
1302 | 1307 | ||
1303 | /* Any change of skb->len requires recalculation of tso factor. */ | 1308 | /* Any change of skb->len requires recalculation of tso factor. */ |
1304 | if (tcp_skb_pcount(skb) > 1) | 1309 | if (tcp_skb_pcount(skb) > 1) |