Skip to content

Commit 3b4929f

Browse files
edumazetdavem330
authored andcommitted
tcp: limit payload size of sacked skbs
Jonathan Looney reported that TCP can trigger the following crash in tcp_shifted_skb() : BUG_ON(tcp_skb_pcount(skb) < pcount); This can happen if the remote peer has advertized the smallest MSS that linux TCP accepts : 48 An skb can hold 17 fragments, and each fragment can hold 32KB on x86, or 64KB on PowerPC. This means that the 16bit witdh of TCP_SKB_CB(skb)->tcp_gso_segs can overflow. Note that tcp_sendmsg() builds skbs with less than 64KB of payload, so this problem needs SACK to be enabled. SACK blocks allow TCP to coalesce multiple skbs in the retransmit queue, thus filling the 17 fragments to maximal capacity. CVE-2019-11477 -- u16 overflow of TCP_SKB_CB(skb)->tcp_gso_segs Fixes: 832d11c ("tcp: Try to restore large SKBs while SACK processing") Signed-off-by: Eric Dumazet <[email protected]> Reported-by: Jonathan Looney <[email protected]> Acked-by: Neal Cardwell <[email protected]> Reviewed-by: Tyler Hicks <[email protected]> Cc: Yuchung Cheng <[email protected]> Cc: Bruce Curtis <[email protected]> Cc: Jonathan Lemon <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 1eb4169 commit 3b4929f

File tree

5 files changed

+30
-9
lines changed

5 files changed

+30
-9
lines changed

include/linux/tcp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,4 +484,8 @@ static inline u16 tcp_mss_clamp(const struct tcp_sock *tp, u16 mss)
484484

485485
return (user_mss && user_mss < mss) ? user_mss : mss;
486486
}
487+
488+
int tcp_skb_shift(struct sk_buff *to, struct sk_buff *from, int pcount,
489+
int shiftlen);
490+
487491
#endif /* _LINUX_TCP_H */

include/net/tcp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ void tcp_time_wait(struct sock *sk, int state, int timeo);
5151

5252
#define MAX_TCP_HEADER (128 + MAX_HEADER)
5353
#define MAX_TCP_OPTION_SPACE 40
54+
#define TCP_MIN_SND_MSS 48
55+
#define TCP_MIN_GSO_SIZE (TCP_MIN_SND_MSS - MAX_TCP_OPTION_SPACE)
5456

5557
/*
5658
* Never offer a window over 32767 without using window scaling. Some

net/ipv4/tcp.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3868,6 +3868,7 @@ void __init tcp_init(void)
38683868
unsigned long limit;
38693869
unsigned int i;
38703870

3871+
BUILD_BUG_ON(TCP_MIN_SND_MSS <= MAX_TCP_OPTION_SPACE);
38713872
BUILD_BUG_ON(sizeof(struct tcp_skb_cb) >
38723873
FIELD_SIZEOF(struct sk_buff, cb));
38733874

net/ipv4/tcp_input.c

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,7 +1302,7 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *prev,
13021302
TCP_SKB_CB(skb)->seq += shifted;
13031303

13041304
tcp_skb_pcount_add(prev, pcount);
1305-
BUG_ON(tcp_skb_pcount(skb) < pcount);
1305+
WARN_ON_ONCE(tcp_skb_pcount(skb) < pcount);
13061306
tcp_skb_pcount_add(skb, -pcount);
13071307

13081308
/* When we're adding to gso_segs == 1, gso_size will be zero,
@@ -1368,6 +1368,21 @@ static int skb_can_shift(const struct sk_buff *skb)
13681368
return !skb_headlen(skb) && skb_is_nonlinear(skb);
13691369
}
13701370

1371+
int tcp_skb_shift(struct sk_buff *to, struct sk_buff *from,
1372+
int pcount, int shiftlen)
1373+
{
1374+
/* TCP min gso_size is 8 bytes (TCP_MIN_GSO_SIZE)
1375+
* Since TCP_SKB_CB(skb)->tcp_gso_segs is 16 bits, we need
1376+
* to make sure not storing more than 65535 * 8 bytes per skb,
1377+
* even if current MSS is bigger.
1378+
*/
1379+
if (unlikely(to->len + shiftlen >= 65535 * TCP_MIN_GSO_SIZE))
1380+
return 0;
1381+
if (unlikely(tcp_skb_pcount(to) + pcount > 65535))
1382+
return 0;
1383+
return skb_shift(to, from, shiftlen);
1384+
}
1385+
13711386
/* Try collapsing SACK blocks spanning across multiple skbs to a single
13721387
* skb.
13731388
*/
@@ -1473,7 +1488,7 @@ static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb,
14731488
if (!after(TCP_SKB_CB(skb)->seq + len, tp->snd_una))
14741489
goto fallback;
14751490

1476-
if (!skb_shift(prev, skb, len))
1491+
if (!tcp_skb_shift(prev, skb, pcount, len))
14771492
goto fallback;
14781493
if (!tcp_shifted_skb(sk, prev, skb, state, pcount, len, mss, dup_sack))
14791494
goto out;
@@ -1491,11 +1506,10 @@ static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb,
14911506
goto out;
14921507

14931508
len = skb->len;
1494-
if (skb_shift(prev, skb, len)) {
1495-
pcount += tcp_skb_pcount(skb);
1496-
tcp_shifted_skb(sk, prev, skb, state, tcp_skb_pcount(skb),
1509+
pcount = tcp_skb_pcount(skb);
1510+
if (tcp_skb_shift(prev, skb, pcount, len))
1511+
tcp_shifted_skb(sk, prev, skb, state, pcount,
14971512
len, mss, 0);
1498-
}
14991513

15001514
out:
15011515
return prev;

net/ipv4/tcp_output.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,8 +1454,8 @@ static inline int __tcp_mtu_to_mss(struct sock *sk, int pmtu)
14541454
mss_now -= icsk->icsk_ext_hdr_len;
14551455

14561456
/* Then reserve room for full set of TCP options and 8 bytes of data */
1457-
if (mss_now < 48)
1458-
mss_now = 48;
1457+
if (mss_now < TCP_MIN_SND_MSS)
1458+
mss_now = TCP_MIN_SND_MSS;
14591459
return mss_now;
14601460
}
14611461

@@ -2747,7 +2747,7 @@ static bool tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb)
27472747
if (next_skb_size <= skb_availroom(skb))
27482748
skb_copy_bits(next_skb, 0, skb_put(skb, next_skb_size),
27492749
next_skb_size);
2750-
else if (!skb_shift(skb, next_skb, next_skb_size))
2750+
else if (!tcp_skb_shift(skb, next_skb, 1, next_skb_size))
27512751
return false;
27522752
}
27532753
tcp_highest_sack_replace(sk, next_skb, skb);

0 commit comments

Comments
 (0)