From cdfc3b2f3067d742a3cdc0e18498c2fb20bcb19f Mon Sep 17 00:00:00 2001 From: Salvatore Bonaccorso Date: Tue, 26 Jan 2016 08:58:09 +0100 Subject: [PATCH] tcp: fix zero cwnd in tcp_cwnd_reduction (CVE-2016-2070) --- debian/changelog | 3 + ...-fix-zero-cwnd-in-tcp_cwnd_reduction.patch | 63 +++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 67 insertions(+) create mode 100644 debian/patches/bugfix/all/tcp-fix-zero-cwnd-in-tcp_cwnd_reduction.patch diff --git a/debian/changelog b/debian/changelog index c7bce8575..0fa86ab36 100644 --- a/debian/changelog +++ b/debian/changelog @@ -54,6 +54,9 @@ linux (4.3.4-1) UNRELEASED; urgency=medium [ Ben Hutchings ] * fuse: break infinite loop in fuse_fill_write_pages() (CVE-2015-8785) + [ Salvatore Bonaccorso ] + tcp: fix zero cwnd in tcp_cwnd_reduction (CVE-2016-2070) + -- Ben Hutchings Sat, 23 Jan 2016 11:51:46 +0000 linux (4.3.3-7) unstable; urgency=medium diff --git a/debian/patches/bugfix/all/tcp-fix-zero-cwnd-in-tcp_cwnd_reduction.patch b/debian/patches/bugfix/all/tcp-fix-zero-cwnd-in-tcp_cwnd_reduction.patch new file mode 100644 index 000000000..bd192a17c --- /dev/null +++ b/debian/patches/bugfix/all/tcp-fix-zero-cwnd-in-tcp_cwnd_reduction.patch @@ -0,0 +1,63 @@ +From: Yuchung Cheng +Date: Wed, 6 Jan 2016 12:42:38 -0800 +Subject: tcp: fix zero cwnd in tcp_cwnd_reduction +Origin: https://git.kernel.org/linus/8b8a321ff72c785ed5e8b4cf6eda20b35d427390 + +Patch 3759824da87b ("tcp: PRR uses CRB mode by default and SS mode +conditionally") introduced a bug that cwnd may become 0 when both +inflight and sndcnt are 0 (cwnd = inflight + sndcnt). This may lead +to a div-by-zero if the connection starts another cwnd reduction +phase by setting tp->prior_cwnd to the current cwnd (0) in +tcp_init_cwnd_reduction(). + +To prevent this we skip PRR operation when nothing is acked or +sacked. Then cwnd must be positive in all cases as long as ssthresh +is positive: + +1) The proportional reduction mode + inflight > ssthresh > 0 + +2) The reduction bound mode + a) inflight == ssthresh > 0 + + b) inflight < ssthresh + sndcnt > 0 since newly_acked_sacked > 0 and inflight < ssthresh + +Therefore in all cases inflight and sndcnt can not both be 0. +We check invalid tp->prior_cwnd to avoid potential div0 bugs. + +In reality this bug is triggered only with a sequence of less common +events. For example, the connection is terminating an ECN-triggered +cwnd reduction with an inflight 0, then it receives reordered/old +ACKs or DSACKs from prior transmission (which acks nothing). Or the +connection is in fast recovery stage that marks everything lost, +but fails to retransmit due to local issues, then receives data +packets from other end which acks nothing. + +Fixes: 3759824da87b ("tcp: PRR uses CRB mode by default and SS mode conditionally") +Reported-by: Oleksandr Natalenko +Signed-off-by: Yuchung Cheng +Signed-off-by: Neal Cardwell +Signed-off-by: Eric Dumazet +Signed-off-by: David S. Miller +--- + net/ipv4/tcp_input.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c +index 2d656ee..d4c5115 100644 +--- a/net/ipv4/tcp_input.c ++++ b/net/ipv4/tcp_input.c +@@ -2478,6 +2478,9 @@ static void tcp_cwnd_reduction(struct sock *sk, const int prior_unsacked, + int newly_acked_sacked = prior_unsacked - + (tp->packets_out - tp->sacked_out); + ++ if (newly_acked_sacked <= 0 || WARN_ON_ONCE(!tp->prior_cwnd)) ++ return; ++ + tp->prr_delivered += newly_acked_sacked; + if (delta < 0) { + u64 dividend = (u64)tp->snd_ssthresh * tp->prr_delivered + +-- +2.1.4 + diff --git a/debian/patches/series b/debian/patches/series index 94af25d11..ff4957ee7 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -141,3 +141,4 @@ bugfix/all/bcache-allows-use-of-register-in-udev-to-avoid-devic.patch bugfix/all/bcache-prevent-crash-on-changing-writeback_running.patch bugfix/all/bcache-change-refill_dirty-to-always-scan-entire-dis.patch bugfix/all/fuse-break-infinite-loop-in-fuse_fill_write_pages.patch +bugfix/all/tcp-fix-zero-cwnd-in-tcp_cwnd_reduction.patch