aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2017-04-26 20:15:40 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-05-14 08:00:20 -0400
commitf3235cbd5be15aa084d5561c2eb8492ed68cd7e5 (patch)
treec661126b93152af4b9fe70d2c04e1f143acbcdd3
parent3b0129d4111e53927c2bc3c6b78a2b12ad71268b (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.c19
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 */
1249static void __pskb_trim_head(struct sk_buff *skb, int len) 1249static 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. */
1288int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) 1289int 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)