From 726a4b413d426f2209264501fe0f56c88588988f Mon Sep 17 00:00:00 2001 From: Andreas Wellving Date: Wed, 10 Jul 2019 11:13:59 +0200 Subject: tcp: CVE-2019-11477 tcp: limit payload size of sacked skbs References: https://nvd.nist.gov/vuln/detail/CVE-2019-11477 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=linux-4.9.y&id=cc1b58ccb78e0de51bcec1f2914d9296260668bd Change-Id: Ic95aaf292571c662f0772467277450c59dc8f8b3 Signed-off-by: Andreas Wellving --- patches/cve/4.9.x.scc | 3 + ...477-tcp-limit-payload-size-of-sacked-skbs.patch | 187 +++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 patches/cve/CVE-2019-11477-tcp-limit-payload-size-of-sacked-skbs.patch diff --git a/patches/cve/4.9.x.scc b/patches/cve/4.9.x.scc index 76d3db0..e3a9067 100644 --- a/patches/cve/4.9.x.scc +++ b/patches/cve/4.9.x.scc @@ -43,3 +43,6 @@ patch CVE-2019-11815-net-rds-force-to-destroy-connection-if-t_sock-is-NUL.patch #CVEs fixed in 4.9.175: patch CVE-2018-20836-scsi-libsas-fix-a-race-condition-when-smp-task-timeo.patch + +#CVEs fixed in 4.9.182: +patch CVE-2019-11477-tcp-limit-payload-size-of-sacked-skbs.patch diff --git a/patches/cve/CVE-2019-11477-tcp-limit-payload-size-of-sacked-skbs.patch b/patches/cve/CVE-2019-11477-tcp-limit-payload-size-of-sacked-skbs.patch new file mode 100644 index 0000000..cff87d7 --- /dev/null +++ b/patches/cve/CVE-2019-11477-tcp-limit-payload-size-of-sacked-skbs.patch @@ -0,0 +1,187 @@ +From cc1b58ccb78e0de51bcec1f2914d9296260668bd Mon Sep 17 00:00:00 2001 +From: Eric Dumazet +Date: Sat, 15 Jun 2019 17:31:03 -0700 +Subject: [PATCH] tcp: limit payload size of sacked skbs + +commit 3b4929f65b0d8249f19a50245cd88ed1a2f78cff upstream. + +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 + +Backport notes, provided by Joao Martins + +v4.15 or since commit 737ff314563 ("tcp: use sequence distance to +detect reordering") had switched from the packet-based FACK tracking and +switched to sequence-based. + +v4.14 and older still have the old logic and hence on +tcp_skb_shift_data() needs to retain its original logic and have +@fack_count in sync. In other words, we keep the increment of pcount with +tcp_skb_pcount(skb) to later used that to update fack_count. To make it +more explicit we track the new skb that gets incremented to pcount in +@next_pcount, and we get to avoid the constant invocation of +tcp_skb_pcount(skb) all together. + +CVE: CVE-2019-11477 +Upstream-Status: Backport [https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=linux-4.9.y&id=cc1b58ccb78e0de51bcec1f2914d9296260668bd] + +Fixes: 832d11c5cd07 ("tcp: Try to restore large SKBs while SACK processing") +Signed-off-by: Eric Dumazet +Reported-by: Jonathan Looney +Acked-by: Neal Cardwell +Reviewed-by: Tyler Hicks +Cc: Yuchung Cheng +Cc: Bruce Curtis +Cc: Jonathan Lemon +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Andreas Wellving +--- + include/linux/tcp.h | 3 +++ + include/net/tcp.h | 2 ++ + net/ipv4/tcp.c | 1 + + net/ipv4/tcp_input.c | 28 ++++++++++++++++++++++------ + net/ipv4/tcp_output.c | 4 ++-- + 5 files changed, 30 insertions(+), 8 deletions(-) + +diff --git a/include/linux/tcp.h b/include/linux/tcp.h +index d0c3615f9050..7f517458c64f 100644 +--- a/include/linux/tcp.h ++++ b/include/linux/tcp.h +@@ -433,4 +433,7 @@ static inline void tcp_saved_syn_free(struct tcp_sock *tp) + tp->saved_syn = NULL; + } + ++int tcp_skb_shift(struct sk_buff *to, struct sk_buff *from, int pcount, ++ int shiftlen); ++ + #endif /* _LINUX_TCP_H */ +diff --git a/include/net/tcp.h b/include/net/tcp.h +index fed2a78fb8cb..d7047de952f0 100644 +--- a/include/net/tcp.h ++++ b/include/net/tcp.h +@@ -53,6 +53,8 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); + + #define MAX_TCP_HEADER (128 + MAX_HEADER) + #define MAX_TCP_OPTION_SPACE 40 ++#define TCP_MIN_SND_MSS 48 ++#define TCP_MIN_GSO_SIZE (TCP_MIN_SND_MSS - MAX_TCP_OPTION_SPACE) + + /* + * Never offer a window over 32767 without using window scaling. Some +diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c +index 2ededb32b754..ee2822a411f9 100644 +--- a/net/ipv4/tcp.c ++++ b/net/ipv4/tcp.c +@@ -3307,6 +3307,7 @@ void __init tcp_init(void) + unsigned long limit; + unsigned int i; + ++ BUILD_BUG_ON(TCP_MIN_SND_MSS <= MAX_TCP_OPTION_SPACE); + BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > + FIELD_SIZEOF(struct sk_buff, cb)); + +diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c +index 80a0cdbabd40..e2e58bc42ba4 100644 +--- a/net/ipv4/tcp_input.c ++++ b/net/ipv4/tcp_input.c +@@ -1320,7 +1320,7 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, + TCP_SKB_CB(skb)->seq += shifted; + + tcp_skb_pcount_add(prev, pcount); +- BUG_ON(tcp_skb_pcount(skb) < pcount); ++ WARN_ON_ONCE(tcp_skb_pcount(skb) < pcount); + tcp_skb_pcount_add(skb, -pcount); + + /* When we're adding to gso_segs == 1, gso_size will be zero, +@@ -1387,6 +1387,21 @@ static int skb_can_shift(const struct sk_buff *skb) + return !skb_headlen(skb) && skb_is_nonlinear(skb); + } + ++int tcp_skb_shift(struct sk_buff *to, struct sk_buff *from, ++ int pcount, int shiftlen) ++{ ++ /* TCP min gso_size is 8 bytes (TCP_MIN_GSO_SIZE) ++ * Since TCP_SKB_CB(skb)->tcp_gso_segs is 16 bits, we need ++ * to make sure not storing more than 65535 * 8 bytes per skb, ++ * even if current MSS is bigger. ++ */ ++ if (unlikely(to->len + shiftlen >= 65535 * TCP_MIN_GSO_SIZE)) ++ return 0; ++ if (unlikely(tcp_skb_pcount(to) + pcount > 65535)) ++ return 0; ++ return skb_shift(to, from, shiftlen); ++} ++ + /* Try collapsing SACK blocks spanning across multiple skbs to a single + * skb. + */ +@@ -1398,6 +1413,7 @@ static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb, + struct tcp_sock *tp = tcp_sk(sk); + struct sk_buff *prev; + int mss; ++ int next_pcount; + int pcount = 0; + int len; + int in_sack; +@@ -1495,7 +1511,7 @@ static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb, + if (!after(TCP_SKB_CB(skb)->seq + len, tp->snd_una)) + goto fallback; + +- if (!skb_shift(prev, skb, len)) ++ if (!tcp_skb_shift(prev, skb, pcount, len)) + goto fallback; + if (!tcp_shifted_skb(sk, skb, state, pcount, len, mss, dup_sack)) + goto out; +@@ -1514,11 +1530,11 @@ static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb, + goto out; + + len = skb->len; +- if (skb_shift(prev, skb, len)) { +- pcount += tcp_skb_pcount(skb); +- tcp_shifted_skb(sk, skb, state, tcp_skb_pcount(skb), len, mss, 0); ++ next_pcount = tcp_skb_pcount(skb); ++ if (tcp_skb_shift(prev, skb, next_pcount, len)) { ++ pcount += next_pcount; ++ tcp_shifted_skb(sk, skb, state, next_pcount, len, mss, 0); + } +- + out: + state->fack_count += pcount; + return prev; +diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c +index 6f35cdd5f2f0..2f166662682e 100644 +--- a/net/ipv4/tcp_output.c ++++ b/net/ipv4/tcp_output.c +@@ -1355,8 +1355,8 @@ static inline int __tcp_mtu_to_mss(struct sock *sk, int pmtu) + mss_now -= icsk->icsk_ext_hdr_len; + + /* Then reserve room for full set of TCP options and 8 bytes of data */ +- if (mss_now < 48) +- mss_now = 48; ++ if (mss_now < TCP_MIN_SND_MSS) ++ mss_now = TCP_MIN_SND_MSS; + return mss_now; + } + +-- +2.20.1 + -- cgit v1.2.3-54-g00ecf