aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp_output.c
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2017-04-26 20:15:40 -0400
committerDavid S. Miller <davem@davemloft.net>2017-04-28 16:05:22 -0400
commit7162fb242cb8322beb558828fd26b33c3e9fc805 (patch)
tree1d4240e28de77ffaf2180f17f3314b26a7bd1258 /net/ipv4/tcp_output.c
parent19cdead3e2ef8ed765c5d1ce48057ca9d97b5094 (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.c19
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 */
1270static void __pskb_trim_head(struct sk_buff *skb, int len) 1270static 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. */
1309int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) 1310int 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)