@@ -1884,6 +1884,39 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
1884
1884
///
1885
1885
/// Any questions go to @nagisa.
1886
1886
#[ 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
+ } ) ]
1887
1920
pub ( crate ) const unsafe fn align_offset < T : Sized > ( p : * const T , a : usize ) -> usize {
1888
1921
// FIXME(#75598): Direct use of these intrinsics improves codegen significantly at opt-level <=
1889
1922
// 1, where the method versions of these operations are not inlined.
@@ -2380,6 +2413,9 @@ mod verify {
2380
2413
use crate :: fmt:: Debug ;
2381
2414
use super :: * ;
2382
2415
use crate :: kani;
2416
+ use intrinsics:: {
2417
+ mul_with_overflow, unchecked_sub, wrapping_mul, wrapping_sub
2418
+ } ;
2383
2419
2384
2420
#[ kani:: proof_for_contract( read_volatile) ]
2385
2421
pub fn check_read_u128 ( ) {
@@ -2388,4 +2424,120 @@ mod verify {
2388
2424
let copy = unsafe { read_volatile ( ptr) } ;
2389
2425
assert_eq ! ( val, copy) ;
2390
2426
}
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
+ }
2391
2543
}
0 commit comments