@@ -343,12 +343,23 @@ const fn shr_in_place_wide<const L: usize, const H: usize>(
343343
344344/// Calculate the maximum number of iterations required according to
345345/// safegcd-bounds: <https://github.com/sipa/safegcd-bounds>
346+ ///
347+ /// Computed in `u64` because `45907 * bits + 30179` exceeds `u32::MAX` once `bits >= 93558`.
348+ /// The count itself (about `2.3 * bits`) only exceeds `u32::MAX` for `bits` above `1864517463`,
349+ /// which panics rather than wrapping.
346350// NOTE: the division is non-constant-time, but this is used to compute the number of iterations we
347351// perform which is leaked in timing information
348352#[ inline]
349353#[ allow( clippy:: integer_division_remainder_used, reason = "public parameter" ) ]
354+ #[ allow( clippy:: cast_lossless, reason = "`const fn`" ) ]
355+ #[ allow( clippy:: cast_possible_truncation, reason = "checked by the assertion" ) ]
350356const fn iterations ( bits : u32 ) -> u32 {
351- ( 45907 * bits + 30179 ) / 19929
357+ let iterations = ( 45907 * bits as u64 + 30179 ) / 19929 ;
358+ assert ! (
359+ iterations <= u32 :: MAX as u64 ,
360+ "precision too large for safegcd"
361+ ) ;
362+ iterations as u32
352363}
353364
354365/// A `Uint` which carries a separate sign in order to maintain the same range.
@@ -526,7 +537,7 @@ impl<const LIMBS: usize> PartialEq for SignedInt<LIMBS> {
526537
527538#[ cfg( test) ]
528539mod tests {
529- use super :: SafeGcdInverter ;
540+ use super :: { SafeGcdInverter , iterations } ;
530541 use crate :: { U128 , U256 , modular:: safegcd:: shr_in_place_wide} ;
531542
532543 #[ test]
@@ -564,4 +575,23 @@ mod tests {
564575 ) ;
565576 assert_eq ! ( b_hi, U128 :: from_u128( 0x111111112222222 ) ) ;
566577 }
578+
579+ #[ test]
580+ fn iterations_do_not_overflow ( ) {
581+ // Values from the `safegcd-bounds` formula evaluated in arbitrary precision.
582+ assert_eq ! ( iterations( 256 ) , 591 ) ;
583+ assert_eq ! ( iterations( 93_504 ) , 215_390 ) ;
584+ // `45907 * bits + 30179` first exceeds `u32::MAX` here.
585+ assert_eq ! ( iterations( 93_558 ) , 215_514 ) ;
586+ assert_eq ! ( iterations( 93_568 ) , 215_537 ) ;
587+ assert_eq ! ( iterations( 98_304 ) , 226_447 ) ;
588+ // The largest precision whose count still fits in `u32`.
589+ assert_eq ! ( iterations( 1_864_517_463 ) , 4_294_967_294 ) ;
590+ }
591+
592+ #[ test]
593+ #[ should_panic( expected = "precision too large for safegcd" ) ]
594+ fn iterations_panics_beyond_u32 ( ) {
595+ iterations ( 1_864_517_464 ) ;
596+ }
567597}
0 commit comments