@@ -158,7 +158,9 @@ impl Controller for Cubic {
158158 let cubic_inc =
159159 ( w_cubic - cubic_cwnd as f64 ) / cubic_cwnd as f64 * self . current_mtu as f64 ;
160160
161- cubic_cwnd += cubic_inc as u64 ;
161+ // w_cubic grows cubically with the time since the last congestion
162+ // event and can exceed `u64::MAX` after a long lossless period.
163+ cubic_cwnd = cubic_cwnd. saturating_add ( cubic_inc as u64 ) ;
162164 }
163165
164166 // Update the increment and increase cwnd by MSS.
@@ -356,4 +358,24 @@ mod tests {
356358 assert_eq ! ( cubic. state. window, window + BASE_DATAGRAM_SIZE ) ;
357359 assert_eq ! ( cubic. state. cwnd_inc, BASE_DATAGRAM_SIZE + 1 ) ;
358360 }
361+
362+ #[ test]
363+ fn congestion_avoidance_does_not_overflow_after_long_lossless_period ( ) {
364+ let now = Instant :: now ( ) ;
365+ let rtt = RttEstimator :: new ( Duration :: from_millis ( 100 ) ) ;
366+ let config = Arc :: new ( CubicConfig :: default ( ) ) ;
367+ let mut cubic = Cubic :: new ( config, now, BASE_DATAGRAM_SIZE as u16 ) ;
368+
369+ // Put CUBIC directly into congestion avoidance.
370+ cubic. state . ssthresh = cubic. state . window ;
371+ cubic. state . recovery_start_time = Some ( now) ;
372+ let window = cubic. state . window ;
373+
374+ // After ten days without a congestion event, w_cubic exceeds u64::MAX.
375+ // Before this fix, computing the window increment overflowed.
376+ let later = now + Duration :: from_secs ( 10 * 24 * 60 * 60 ) ;
377+ cubic. on_ack ( later, later, BASE_DATAGRAM_SIZE , false , & rtt) ;
378+
379+ assert_eq ! ( cubic. state. window, window + BASE_DATAGRAM_SIZE ) ;
380+ }
359381}
0 commit comments