@@ -1884,6 +1884,39 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
18841884///
18851885/// Any questions go to @nagisa.
18861886#[ lang = "align_offset" ]
1887+ #[ safety:: requires( a. is_power_of_two( ) ) ]
1888+ #[ safety:: ensures( |result| {
1889+ let stride = mem:: size_of:: <T >( ) ;
1890+ // ZSTs
1891+ if stride == 0 {
1892+ if p. addr( ) % a == 0 {
1893+ return * result == 0 ;
1894+ } else {
1895+ return * result == usize :: MAX ;
1896+ }
1897+ }
1898+
1899+ // In this case, the pointer cannot be aligned
1900+ if ( a % stride == 0 ) && ( p. addr( ) % stride != 0 ) {
1901+ return * result == usize :: MAX ;
1902+ }
1903+
1904+ // Checking if the answer should indeed be usize::MAX when a % stride != 0
1905+ // requires computing gcd(a, stride), which is too expensive without
1906+ // quantifiers (https://model-checking.github.io/kani/rfc/rfcs/0010-quantifiers.html).
1907+ // This should be updated once quantifiers are available.
1908+ if ( a % stride != 0 && * result == usize :: MAX ) {
1909+ return true ;
1910+ }
1911+
1912+ // If we reach this case, either:
1913+ // - a % stride == 0 and p.addr() % stride == 0, so it is definitely possible to align the pointer
1914+ // - a % stride != 0 and result != usize::MAX, so align_offset is claiming that it's possible to align the pointer
1915+ // Check that applying the returned result does indeed produce an aligned address
1916+ let product = usize :: wrapping_mul( * result, stride) ;
1917+ let new_addr = usize :: wrapping_add( product, p. addr( ) ) ;
1918+ * result != usize :: MAX && new_addr % a == 0
1919+ } ) ]
18871920pub ( crate ) const unsafe fn align_offset < T : Sized > ( p : * const T , a : usize ) -> usize {
18881921 // FIXME(#75598): Direct use of these intrinsics improves codegen significantly at opt-level <=
18891922 // 1, where the method versions of these operations are not inlined.
@@ -2380,6 +2413,9 @@ mod verify {
23802413 use crate :: fmt:: Debug ;
23812414 use super :: * ;
23822415 use crate :: kani;
2416+ use intrinsics:: {
2417+ mul_with_overflow, unchecked_sub, wrapping_mul, wrapping_sub
2418+ } ;
23832419
23842420 #[ kani:: proof_for_contract( read_volatile) ]
23852421 pub fn check_read_u128 ( ) {
@@ -2388,4 +2424,120 @@ mod verify {
23882424 let copy = unsafe { read_volatile ( ptr) } ;
23892425 assert_eq ! ( val, copy) ;
23902426 }
2427+
2428+ fn check_align_offset < T > ( p : * const T ) {
2429+ let a = kani:: any :: < usize > ( ) ;
2430+ unsafe { align_offset ( p, a) } ;
2431+ }
2432+
2433+ #[ kani:: proof_for_contract( align_offset) ]
2434+ fn check_align_offset_zst ( ) {
2435+ let p = kani:: any :: < usize > ( ) as * const ( ) ;
2436+ check_align_offset ( p) ;
2437+ }
2438+
2439+ #[ kani:: proof_for_contract( align_offset) ]
2440+ fn check_align_offset_u8 ( ) {
2441+ let p = kani:: any :: < usize > ( ) as * const u8 ;
2442+ check_align_offset ( p) ;
2443+ }
2444+
2445+ #[ kani:: proof_for_contract( align_offset) ]
2446+ fn check_align_offset_u16 ( ) {
2447+ let p = kani:: any :: < usize > ( ) as * const u16 ;
2448+ check_align_offset ( p) ;
2449+ }
2450+
2451+ #[ kani:: proof_for_contract( align_offset) ]
2452+ fn check_align_offset_u32 ( ) {
2453+ let p = kani:: any :: < usize > ( ) as * const u32 ;
2454+ check_align_offset ( p) ;
2455+ }
2456+
2457+ #[ kani:: proof_for_contract( align_offset) ]
2458+ fn check_align_offset_u64 ( ) {
2459+ let p = kani:: any :: < usize > ( ) as * const u64 ;
2460+ check_align_offset ( p) ;
2461+ }
2462+
2463+ #[ kani:: proof_for_contract( align_offset) ]
2464+ fn check_align_offset_u128 ( ) {
2465+ let p = kani:: any :: < usize > ( ) as * const u128 ;
2466+ check_align_offset ( p) ;
2467+ }
2468+
2469+ #[ kani:: proof_for_contract( align_offset) ]
2470+ fn check_align_offset_4096 ( ) {
2471+ let p = kani:: any :: < usize > ( ) as * const [ u128 ; 64 ] ;
2472+ check_align_offset ( p) ;
2473+ }
2474+
2475+ #[ kani:: proof_for_contract( align_offset) ]
2476+ fn check_align_offset_17 ( ) {
2477+ let p = kani:: any :: < usize > ( ) as * const [ char ; 17 ] ;
2478+ check_align_offset ( p) ;
2479+ }
2480+
2481+ // This function lives inside align_offset, so it is not publicly accessible (hence this copy).
2482+ #[ safety:: requires( m. is_power_of_two( ) ) ]
2483+ #[ safety:: requires( x < m) ]
2484+ // TODO: add ensures contract to check that the answer is indeed correct
2485+ // This will require quantifiers (https://model-checking.github.io/kani/rfc/rfcs/0010-quantifiers.html)
2486+ // so that we can add a precondition that gcd(x, m) = 1 like so:
2487+ // ∀d, d > 0 ∧ x % d = 0 ∧ m % d = 0 → d = 1
2488+ // With this precondition, we can then write this postcondition to check the correctness of the answer:
2489+ // #[safety::ensures(|result| wrapping_mul(*result, x) % m == 1)]
2490+ const unsafe fn mod_inv_copy ( x : usize , m : usize ) -> usize {
2491+ /// Multiplicative modular inverse table modulo 2⁴ = 16.
2492+ ///
2493+ /// Note, that this table does not contain values where inverse does not exist (i.e., for
2494+ /// `0⁻¹ mod 16`, `2⁻¹ mod 16`, etc.)
2495+ const INV_TABLE_MOD_16 : [ u8 ; 8 ] = [ 1 , 11 , 13 , 7 , 9 , 3 , 5 , 15 ] ;
2496+ /// Modulo for which the `INV_TABLE_MOD_16` is intended.
2497+ const INV_TABLE_MOD : usize = 16 ;
2498+
2499+ // SAFETY: `m` is required to be a power-of-two, hence non-zero.
2500+ let m_minus_one = unsafe { unchecked_sub ( m, 1 ) } ;
2501+ let mut inverse = INV_TABLE_MOD_16 [ ( x & ( INV_TABLE_MOD - 1 ) ) >> 1 ] as usize ;
2502+ let mut mod_gate = INV_TABLE_MOD ;
2503+ // We iterate "up" using the following formula:
2504+ //
2505+ // $$ xy ≡ 1 (mod 2ⁿ) → xy (2 - xy) ≡ 1 (mod 2²ⁿ) $$
2506+ //
2507+ // This application needs to be applied at least until `2²ⁿ ≥ m`, at which point we can
2508+ // finally reduce the computation to our desired `m` by taking `inverse mod m`.
2509+ //
2510+ // This computation is `O(log log m)`, which is to say, that on 64-bit machines this loop
2511+ // will always finish in at most 4 iterations.
2512+ loop {
2513+ // y = y * (2 - xy) mod n
2514+ //
2515+ // Note, that we use wrapping operations here intentionally – the original formula
2516+ // uses e.g., subtraction `mod n`. It is entirely fine to do them `mod
2517+ // usize::MAX` instead, because we take the result `mod n` at the end
2518+ // anyway.
2519+ if mod_gate >= m {
2520+ break ;
2521+ }
2522+ inverse = wrapping_mul ( inverse, wrapping_sub ( 2usize , wrapping_mul ( x, inverse) ) ) ;
2523+ let ( new_gate, overflow) = mul_with_overflow ( mod_gate, mod_gate) ;
2524+ if overflow {
2525+ break ;
2526+ }
2527+ mod_gate = new_gate;
2528+ }
2529+ inverse & m_minus_one
2530+ }
2531+
2532+ // The specification for mod_inv states that it cannot ever panic.
2533+ // Verify that is the case, given that the function's safety preconditions are met.
2534+
2535+ // TODO: Once https://github.com/model-checking/kani/issues/3467 is fixed,
2536+ // move this harness inside `align_offset` and delete `mod_inv_copy`
2537+ #[ kani:: proof_for_contract( mod_inv_copy) ]
2538+ fn check_mod_inv ( ) {
2539+ let x = kani:: any :: < usize > ( ) ;
2540+ let m = kani:: any :: < usize > ( ) ;
2541+ unsafe { mod_inv_copy ( x, m) } ;
2542+ }
23912543}
0 commit comments