Skip to content

Commit 8b8a321

Browse files
yuchungchengdavem330
authored andcommitted
tcp: fix zero cwnd in tcp_cwnd_reduction
Patch 3759824 ("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: 3759824 ("tcp: PRR uses CRB mode by default and SS mode conditionally") Reported-by: Oleksandr Natalenko <[email protected]> Signed-off-by: Yuchung Cheng <[email protected]> Signed-off-by: Neal Cardwell <[email protected]> Signed-off-by: Eric Dumazet <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 58caf63 commit 8b8a321

File tree

1 file changed

+3
-0
lines changed

1 file changed

+3
-0
lines changed

net/ipv4/tcp_input.c

+3
Original file line numberDiff line numberDiff line change
@@ -2478,6 +2478,9 @@ static void tcp_cwnd_reduction(struct sock *sk, const int prior_unsacked,
24782478
int newly_acked_sacked = prior_unsacked -
24792479
(tp->packets_out - tp->sacked_out);
24802480

2481+
if (newly_acked_sacked <= 0 || WARN_ON_ONCE(!tp->prior_cwnd))
2482+
return;
2483+
24812484
tp->prr_delivered += newly_acked_sacked;
24822485
if (delta < 0) {
24832486
u64 dividend = (u64)tp->snd_ssthresh * tp->prr_delivered +

0 commit comments

Comments
 (0)