Skip to content

Commit 02d706a

Browse files
authored
unchecked_mul and unchecked_shr proofs (#7)
* Added harnesses for unchecked multiplication (`unchecked_mul`) and shift right (`unchecked_shr`) * Added a macro and input limits for multiplication proofs * Reduced duplicity in code by using macros to generate proof harnesses
1 parent 8ee5682 commit 02d706a

File tree

3 files changed

+75
-1
lines changed

3 files changed

+75
-1
lines changed

library/core/src/num/int_macros.rs

+2
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,7 @@ macro_rules! int_impl {
816816
without modifying the original"]
817817
#[inline(always)]
818818
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
819+
#[requires(!self.overflowing_mul(rhs).1)]
819820
pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self {
820821
assert_unsafe_precondition!(
821822
check_language_ub,
@@ -1424,6 +1425,7 @@ macro_rules! int_impl {
14241425
#[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")]
14251426
#[inline(always)]
14261427
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
1428+
#[requires(rhs < <$ActualT>::BITS)] // i.e. requires the right hand side of the shift (rhs) to be less than the number of bits in the type. This prevents undefined behavior.
14271429
pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self {
14281430
assert_unsafe_precondition!(
14291431
check_language_ub,

library/core/src/num/mod.rs

+71-1
Original file line numberDiff line numberDiff line change
@@ -1605,19 +1605,39 @@ mod verify {
16051605
}
16061606
}
16071607

1608+
macro_rules! generate_unchecked_mul_harness {
1609+
($type:ty, $method:ident, $harness_name:ident, $min:expr, $max:expr) => {
1610+
#[kani::proof_for_contract($type::$method)]
1611+
pub fn $harness_name() {
1612+
let num1: $type = kani::any();
1613+
let num2: $type = kani::any();
1614+
1615+
// Limit the values of num1 and num2 to the specified range for multiplication
1616+
kani::assume(num1 >= $min && num1 <= $max);
1617+
kani::assume(num2 >= $min && num2 <= $max);
1618+
1619+
unsafe {
1620+
num1.$method(num2);
1621+
}
1622+
}
1623+
}
1624+
}
1625+
1626+
16081627
macro_rules! generate_unchecked_shift_harness {
16091628
($type:ty, $method:ident, $harness_name:ident) => {
16101629
#[kani::proof_for_contract($type::$method)]
16111630
pub fn $harness_name() {
16121631
let num1: $type = kani::any::<$type>();
16131632
let num2: u32 = kani::any::<u32>();
1614-
1633+
16151634
unsafe {
16161635
num1.$method(num2);
16171636
}
16181637
}
16191638
}
16201639
}
1640+
16211641

16221642
macro_rules! generate_unchecked_neg_harness {
16231643
($type:ty, $method:ident, $harness_name:ident) => {
@@ -1654,4 +1674,54 @@ mod verify {
16541674
generate_unchecked_math_harness!(u64, unchecked_add, checked_unchecked_add_u64);
16551675
generate_unchecked_math_harness!(u128, unchecked_add, checked_unchecked_add_u128);
16561676
generate_unchecked_math_harness!(usize, unchecked_add, checked_unchecked_add_usize);
1677+
1678+
// unchecked_mul proofs
1679+
//
1680+
// Target types:
1681+
// i{8,16,32,64,128, size} and u{8,16,32,64,128, size} -- 12 types in total
1682+
//
1683+
// Target contracts:
1684+
// #[requires(!self.overflowing_mul(rhs).1)]
1685+
//
1686+
// Target function:
1687+
// pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self
1688+
// exponential state spaces for 32,64 and 128, hence provided limited range for verification.
1689+
generate_unchecked_math_harness!(i8, unchecked_mul, checked_unchecked_mul_i8);
1690+
generate_unchecked_math_harness!(i16, unchecked_mul, checked_unchecked_mul_i16);
1691+
generate_unchecked_mul_harness!(i32, unchecked_mul, checked_unchecked_mul_i32, -10_000i32, 10_000i32);
1692+
generate_unchecked_mul_harness!(i64, unchecked_mul, checked_unchecked_mul_i64, -1_000i64, 1_000i64);
1693+
generate_unchecked_mul_harness!(i128, unchecked_mul, checked_unchecked_mul_i128, -1_000_000_000_000_000i128, 1_000_000_000_000_000i128);
1694+
generate_unchecked_mul_harness!(isize, unchecked_mul, checked_unchecked_mul_isize, -100_000isize, 100_000isize);
1695+
generate_unchecked_math_harness!(u8, unchecked_mul, checked_unchecked_mul_u8);
1696+
generate_unchecked_math_harness!(u16, unchecked_mul, checked_unchecked_mul_u16);
1697+
generate_unchecked_mul_harness!(u32, unchecked_mul, checked_unchecked_mul_u32, 0u32, 20_000u32);
1698+
generate_unchecked_mul_harness!(u64, unchecked_mul, checked_unchecked_mul_u64, 0u64, 2_000u64);
1699+
generate_unchecked_mul_harness!(u128, unchecked_mul, checked_unchecked_mul_u128, 0u128, 1_000_000_000_000_000u128);
1700+
generate_unchecked_mul_harness!(usize, unchecked_mul, checked_unchecked_mul_usize, 0usize, 100_000usize);
1701+
1702+
1703+
// unchecked_shr proofs
1704+
//
1705+
// Target types:
1706+
// i{8,16,32,64,128,size} and u{8,16,32,64,128,size} -- 12 types in total
1707+
//
1708+
// Target contracts:
1709+
// #[requires(rhs < <$ActualT>::BITS)]
1710+
//
1711+
// Target function:
1712+
// pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self
1713+
generate_unchecked_shift_harness!(i8, unchecked_shr, checked_unchecked_shr_i8);
1714+
generate_unchecked_shift_harness!(i16, unchecked_shr, checked_unchecked_shr_i16);
1715+
generate_unchecked_shift_harness!(i32, unchecked_shr, checked_unchecked_shr_i32);
1716+
generate_unchecked_shift_harness!(i64, unchecked_shr, checked_unchecked_shr_i64);
1717+
generate_unchecked_shift_harness!(i128, unchecked_shr, checked_unchecked_shr_i128);
1718+
generate_unchecked_shift_harness!(isize, unchecked_shr, checked_unchecked_shr_isize);
1719+
generate_unchecked_shift_harness!(u8, unchecked_shr, checked_unchecked_shr_u8);
1720+
generate_unchecked_shift_harness!(u16, unchecked_shr, checked_unchecked_shr_u16);
1721+
generate_unchecked_shift_harness!(u32, unchecked_shr, checked_unchecked_shr_u32);
1722+
generate_unchecked_shift_harness!(u64, unchecked_shr, checked_unchecked_shr_u64);
1723+
generate_unchecked_shift_harness!(u128, unchecked_shr, checked_unchecked_shr_u128);
1724+
generate_unchecked_shift_harness!(usize, unchecked_shr, checked_unchecked_shr_usize);
1725+
}
1726+
}
16571727
}

library/core/src/num/uint_macros.rs

+2
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,7 @@ macro_rules! uint_impl {
908908
without modifying the original"]
909909
#[inline(always)]
910910
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
911+
#[requires(!self.overflowing_mul(rhs).1)]
911912
pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self {
912913
assert_unsafe_precondition!(
913914
check_language_ub,
@@ -1613,6 +1614,7 @@ macro_rules! uint_impl {
16131614
#[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")]
16141615
#[inline(always)]
16151616
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
1617+
#[requires(rhs < <$ActualT>::BITS)]// i.e. requires the right hand side of the shift (rhs) to be less than the number of bits in the type. This prevents undefined behavior.
16161618
pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self {
16171619
assert_unsafe_precondition!(
16181620
check_language_ub,

0 commit comments

Comments
 (0)