@@ -146,7 +146,9 @@ impl Controller for Cubic {
146146 let cubic_inc =
147147 ( w_cubic - cubic_cwnd as f64 ) / cubic_cwnd as f64 * self . current_mtu as f64 ;
148148
149- cubic_cwnd += cubic_inc as u64 ;
149+ // w_cubic grows cubically with the time since the last congestion
150+ // event and can exceed `u64::MAX` after a long lossless period.
151+ cubic_cwnd = cubic_cwnd. saturating_add ( cubic_inc as u64 ) ;
150152 }
151153
152154 // Update the increment and increase cwnd by MSS.
@@ -296,4 +298,24 @@ mod tests {
296298 assert_eq ! ( cubic. ssthresh, ( window as f64 * BETA_CUBIC ) as u64 ) ;
297299 assert_eq ! ( cubic. window, cubic. ssthresh) ;
298300 }
301+
302+ #[ test]
303+ fn congestion_avoidance_does_not_overflow_after_long_lossless_period ( ) {
304+ let now = Instant :: now ( ) ;
305+ let rtt = RttEstimator :: new ( Duration :: from_millis ( 100 ) ) ;
306+ let config = Arc :: new ( CubicConfig :: default ( ) ) ;
307+ let mut cubic = Cubic :: new ( config, now, BASE_DATAGRAM_SIZE as u16 ) ;
308+
309+ // Put CUBIC directly into congestion avoidance.
310+ cubic. ssthresh = cubic. window ;
311+ cubic. recovery_start_time = Some ( now) ;
312+ let window = cubic. window ;
313+
314+ // After ten days without a congestion event, w_cubic exceeds u64::MAX.
315+ // Before this fix, computing the window increment overflowed.
316+ let later = now + Duration :: from_secs ( 10 * 24 * 60 * 60 ) ;
317+ cubic. on_ack ( later, later, BASE_DATAGRAM_SIZE , false , & rtt) ;
318+
319+ assert_eq ! ( cubic. window, window + BASE_DATAGRAM_SIZE ) ;
320+ }
299321}
0 commit comments