From 1ae4c6d4958e43bda11562e832d9a28b930ba7a6 Mon Sep 17 00:00:00 2001 From: yew005 Date: Tue, 10 Sep 2024 17:11:15 -0700 Subject: [PATCH 01/67] Add Kani proofs of unchecked_add for all integer types --- .../core/tests/num/kani-proofs/i128-proofs.rs | 23 +++++++++++++++++++ .../core/tests/num/kani-proofs/i16-proofs.rs | 23 +++++++++++++++++++ .../core/tests/num/kani-proofs/i32-proofs.rs | 23 +++++++++++++++++++ .../core/tests/num/kani-proofs/i64-proofs.rs | 23 +++++++++++++++++++ .../core/tests/num/kani-proofs/i8-proofs.rs | 23 +++++++++++++++++++ .../core/tests/num/kani-proofs/u128-proofs.rs | 21 +++++++++++++++++ .../core/tests/num/kani-proofs/u16-proofs.rs | 21 +++++++++++++++++ .../core/tests/num/kani-proofs/u32-proofs.rs | 21 +++++++++++++++++ .../core/tests/num/kani-proofs/u64-proofs.rs | 21 +++++++++++++++++ .../core/tests/num/kani-proofs/u8-proofs.rs | 21 +++++++++++++++++ 10 files changed, 220 insertions(+) create mode 100644 library/core/tests/num/kani-proofs/i128-proofs.rs create mode 100644 library/core/tests/num/kani-proofs/i16-proofs.rs create mode 100644 library/core/tests/num/kani-proofs/i32-proofs.rs create mode 100644 library/core/tests/num/kani-proofs/i64-proofs.rs create mode 100644 library/core/tests/num/kani-proofs/i8-proofs.rs create mode 100644 library/core/tests/num/kani-proofs/u128-proofs.rs create mode 100644 library/core/tests/num/kani-proofs/u16-proofs.rs create mode 100644 library/core/tests/num/kani-proofs/u32-proofs.rs create mode 100644 library/core/tests/num/kani-proofs/u64-proofs.rs create mode 100644 library/core/tests/num/kani-proofs/u8-proofs.rs diff --git a/library/core/tests/num/kani-proofs/i128-proofs.rs b/library/core/tests/num/kani-proofs/i128-proofs.rs new file mode 100644 index 0000000000000..279d8f26ff947 --- /dev/null +++ b/library/core/tests/num/kani-proofs/i128-proofs.rs @@ -0,0 +1,23 @@ +#[cfg(kani)] +mod verification { + // use super::*; + + #[kani::proof] + fn verify_i128_unchecked_add() { + let num1: i128 = kani::any::(); + let num2: i128 = kani::any::(); + + // Safety preconditions: + // - Positive number addition won't overflow + // - Negative number addition won't underflow + // Addition of two integers with different signs never overflows + // Undefined behavior occurs when overflow or underflow happens + kani::assume((num1 > 0 && num2 > 0 && num1 < i128::MAX - num2) + || (num1 < 0 && num2 < 0 && num1 > i128::MIN - num2)); + + unsafe { + let result = num1.unchecked_add(num2); + assert_eq!(Some(result), num1.checked_add(num2)); + } + } +} \ No newline at end of file diff --git a/library/core/tests/num/kani-proofs/i16-proofs.rs b/library/core/tests/num/kani-proofs/i16-proofs.rs new file mode 100644 index 0000000000000..d946148d78976 --- /dev/null +++ b/library/core/tests/num/kani-proofs/i16-proofs.rs @@ -0,0 +1,23 @@ +#[cfg(kani)] +mod verification { + // use super::*; + + #[kani::proof] + fn verify_i16_unchecked_add() { + let num1: i16 = kani::any::(); + let num2: i16 = kani::any::(); + + // Safety preconditions: + // - Positive number addition won't overflow + // - Negative number addition won't underflow + // Addition of two integers with different signs never overflows + // Undefined behavior occurs when overflow or underflow happens + kani::assume((num1 > 0 && num2 > 0 && num1 < i16::MAX - num2) + || (num1 < 0 && num2 < 0 && num1 > i16::MIN - num2)); + + unsafe { + let result = num1.unchecked_add(num2); + assert_eq!(Some(result), num1.checked_add(num2)); + } + } +} \ No newline at end of file diff --git a/library/core/tests/num/kani-proofs/i32-proofs.rs b/library/core/tests/num/kani-proofs/i32-proofs.rs new file mode 100644 index 0000000000000..bb2d542dbc38f --- /dev/null +++ b/library/core/tests/num/kani-proofs/i32-proofs.rs @@ -0,0 +1,23 @@ +#[cfg(kani)] +mod verification { + // use super::*; + + #[kani::proof] + fn verify_i32_unchecked_add() { + let num1: i32 = kani::any::(); + let num2: i32 = kani::any::(); + + // Safety preconditions: + // - Positive number addition won't overflow + // - Negative number addition won't underflow + // Addition of two integers with different signs never overflows + // Undefined behavior occurs when overflow or underflow happens + kani::assume((num1 > 0 && num2 > 0 && num1 < i32::MAX - num2) + || (num1 < 0 && num2 < 0 && num1 > i32::MIN - num2)); + + unsafe { + let result = num1.unchecked_add(num2); + assert_eq!(Some(result), num1.checked_add(num2)); + } + } +} \ No newline at end of file diff --git a/library/core/tests/num/kani-proofs/i64-proofs.rs b/library/core/tests/num/kani-proofs/i64-proofs.rs new file mode 100644 index 0000000000000..822bac06eb733 --- /dev/null +++ b/library/core/tests/num/kani-proofs/i64-proofs.rs @@ -0,0 +1,23 @@ +#[cfg(kani)] +mod verification { + // use super::*; + + #[kani::proof] + fn verify_i64_unchecked_add() { + let num1: i64 = kani::any::(); + let num2: i64 = kani::any::(); + + // Safety preconditions: + // - Positive number addition won't overflow + // - Negative number addition won't underflow + // Addition of two integers with different signs never overflows + // Undefined behavior occurs when overflow or underflow happens + kani::assume((num1 > 0 && num2 > 0 && num1 < i64::MAX - num2) + || (num1 < 0 && num2 < 0 && num1 > i64::MIN - num2)); + + unsafe { + let result = num1.unchecked_add(num2); + assert_eq!(Some(result), num1.checked_add(num2)); + } + } +} \ No newline at end of file diff --git a/library/core/tests/num/kani-proofs/i8-proofs.rs b/library/core/tests/num/kani-proofs/i8-proofs.rs new file mode 100644 index 0000000000000..edc3522f42be3 --- /dev/null +++ b/library/core/tests/num/kani-proofs/i8-proofs.rs @@ -0,0 +1,23 @@ +#[cfg(kani)] +mod verification { + // use super::*; + + #[kani::proof] + fn verify_i8_unchecked_add() { + let num1: i8 = kani::any::(); + let num2: i8 = kani::any::(); + + // Safety preconditions: + // - Positive number addition won't overflow + // - Negative number addition won't underflow + // Addition of two integers with different signs never overflows + // Undefined behavior occurs when overflow or underflow happens + kani::assume((num1 > 0 && num2 > 0 && num1 < i8::MAX - num2) + || (num1 < 0 && num2 < 0 && num1 > i8::MIN - num2)); + + unsafe { + let result = num1.unchecked_add(num2); + assert_eq!(Some(result), num1.checked_add(num2)); + } + } +} \ No newline at end of file diff --git a/library/core/tests/num/kani-proofs/u128-proofs.rs b/library/core/tests/num/kani-proofs/u128-proofs.rs new file mode 100644 index 0000000000000..98d781ec024b5 --- /dev/null +++ b/library/core/tests/num/kani-proofs/u128-proofs.rs @@ -0,0 +1,21 @@ +#[cfg(kani)] +mod verification { + // use super::*; + + #[kani::proof] + fn verify_u128_unchecked_add() { + let num1: u128 = kani::any::(); + let num2: u128 = kani::any::(); + + // Safety preconditions: + // - Addition won't overflow + // Unsigned integers are always positive, so underflow won't happen + // Undefined behavior occurs when overflow happens + kani::assume(num1 < u128::MAX - num2); + + unsafe { + let result = num1.unchecked_add(num2); + assert_eq!(Some(result), num1.checked_add(num2)); + } + } +} \ No newline at end of file diff --git a/library/core/tests/num/kani-proofs/u16-proofs.rs b/library/core/tests/num/kani-proofs/u16-proofs.rs new file mode 100644 index 0000000000000..9dfff81d1abbb --- /dev/null +++ b/library/core/tests/num/kani-proofs/u16-proofs.rs @@ -0,0 +1,21 @@ +#[cfg(kani)] +mod verification { + // use super::*; + + #[kani::proof] + fn verify_u16_unchecked_add() { + let num1: u16 = kani::any::(); + let num2: u16 = kani::any::(); + + // Safety preconditions: + // - Addition won't overflow + // Unsigned integers are always positive, so underflow won't happen + // Undefined behavior occurs when overflow happens + kani::assume(num1 < u16::MAX - num2); + + unsafe { + let result = num1.unchecked_add(num2); + assert_eq!(Some(result), num1.checked_add(num2)); + } + } +} \ No newline at end of file diff --git a/library/core/tests/num/kani-proofs/u32-proofs.rs b/library/core/tests/num/kani-proofs/u32-proofs.rs new file mode 100644 index 0000000000000..0d13bdcd2dce0 --- /dev/null +++ b/library/core/tests/num/kani-proofs/u32-proofs.rs @@ -0,0 +1,21 @@ +#[cfg(kani)] +mod verification { + // use super::*; + + #[kani::proof] + fn verify_u32_unchecked_add() { + let num1: u32 = kani::any::(); + let num2: u32 = kani::any::(); + + // Safety preconditions: + // - Addition won't overflow + // Unsigned integers are always positive, so underflow won't happen + // Undefined behavior occurs when overflow happens + kani::assume(num1 < u32::MAX - num2); + + unsafe { + let result = num1.unchecked_add(num2); + assert_eq!(Some(result), num1.checked_add(num2)); + } + } +} \ No newline at end of file diff --git a/library/core/tests/num/kani-proofs/u64-proofs.rs b/library/core/tests/num/kani-proofs/u64-proofs.rs new file mode 100644 index 0000000000000..405468c96e0ba --- /dev/null +++ b/library/core/tests/num/kani-proofs/u64-proofs.rs @@ -0,0 +1,21 @@ +#[cfg(kani)] +mod verification { + // use super::*; + + #[kani::proof] + fn verify_u64_unchecked_add() { + let num1: u64 = kani::any::(); + let num2: u64 = kani::any::(); + + // Safety preconditions: + // - Addition won't overflow + // Unsigned integers are always positive, so underflow won't happen + // Undefined behavior occurs when overflow happens + kani::assume(num1 < u64::MAX - num2); + + unsafe { + let result = num1.unchecked_add(num2); + assert_eq!(Some(result), num1.checked_add(num2)); + } + } +} \ No newline at end of file diff --git a/library/core/tests/num/kani-proofs/u8-proofs.rs b/library/core/tests/num/kani-proofs/u8-proofs.rs new file mode 100644 index 0000000000000..89826fc292c4b --- /dev/null +++ b/library/core/tests/num/kani-proofs/u8-proofs.rs @@ -0,0 +1,21 @@ +#[cfg(kani)] +mod verification { + // use super::*; + + #[kani::proof] + fn verify_u8_unchecked_add() { + let num1: u8 = kani::any::(); + let num2: u8 = kani::any::(); + + // Safety preconditions: + // - Addition won't overflow + // Unsigned integers are always positive, so underflow won't happen + // Undefined behavior occurs when overflow happens + kani::assume(num1 < u8::MAX - num2); + + unsafe { + let result = num1.unchecked_add(num2); + assert_eq!(Some(result), num1.checked_add(num2)); + } + } +} \ No newline at end of file From b553678c11aec836a9cc0aba1db0cc473a70fc03 Mon Sep 17 00:00:00 2001 From: yew005 Date: Tue, 10 Sep 2024 21:07:15 -0700 Subject: [PATCH 02/67] Format proof files & Add verification function templates --- .../core/tests/num/kani-proofs/i128-proofs.rs | 55 ++++++++++++++----- .../core/tests/num/kani-proofs/i16-proofs.rs | 55 ++++++++++++++----- .../core/tests/num/kani-proofs/i32-proofs.rs | 55 ++++++++++++++----- .../core/tests/num/kani-proofs/i64-proofs.rs | 55 ++++++++++++++----- .../core/tests/num/kani-proofs/i8-proofs.rs | 55 ++++++++++++++----- .../core/tests/num/kani-proofs/u128-proofs.rs | 49 +++++++++++++---- .../core/tests/num/kani-proofs/u16-proofs.rs | 49 +++++++++++++---- .../core/tests/num/kani-proofs/u32-proofs.rs | 49 +++++++++++++---- .../core/tests/num/kani-proofs/u64-proofs.rs | 49 +++++++++++++---- .../core/tests/num/kani-proofs/u8-proofs.rs | 49 +++++++++++++---- 10 files changed, 390 insertions(+), 130 deletions(-) diff --git a/library/core/tests/num/kani-proofs/i128-proofs.rs b/library/core/tests/num/kani-proofs/i128-proofs.rs index 279d8f26ff947..4ac03ec8714fe 100644 --- a/library/core/tests/num/kani-proofs/i128-proofs.rs +++ b/library/core/tests/num/kani-proofs/i128-proofs.rs @@ -1,23 +1,50 @@ #[cfg(kani)] mod verification { - // use super::*; + // use super::*; - #[kani::proof] - fn verify_i128_unchecked_add() { - let num1: i128 = kani::any::(); - let num2: i128 = kani::any::(); + #[kani::proof] + fn verify_i128_unchecked_add() { + let num1: i128 = kani::any::(); + let num2: i128 = kani::any::(); - // Safety preconditions: - // - Positive number addition won't overflow - // - Negative number addition won't underflow - // Addition of two integers with different signs never overflows - // Undefined behavior occurs when overflow or underflow happens - kani::assume((num1 > 0 && num2 > 0 && num1 < i128::MAX - num2) - || (num1 < 0 && num2 < 0 && num1 > i128::MIN - num2)); + // Safety preconditions: + // - Positive number addition won't overflow + // - Negative number addition won't underflow + // Addition of two integers with different signs never overflows + // Undefined behavior occurs when overflow or underflow happens + kani::assume( + (num1 > 0 && num2 > 0 && num1 < i128::MAX - num2) + || (num1 < 0 && num2 < 0 && num1 > i128::MIN - num2), + ); unsafe { let result = num1.unchecked_add(num2); assert_eq!(Some(result), num1.checked_add(num2)); } - } -} \ No newline at end of file + } + + #[kani::proof] + fn verify_i128_unchecked_sub() { + // TODO + } + + #[kani::proof] + fn verify_i128_unchecked_mul() { + // TODO + } + + #[kani::proof] + fn verify_i128_unchecked_shl() { + // TODO + } + + #[kani::proof] + fn verify_i128_unchecked_shr() { + // TODO + } + + #[kani::proof] + fn verify_i128_unchecked_neg() { + // TODO + } +} diff --git a/library/core/tests/num/kani-proofs/i16-proofs.rs b/library/core/tests/num/kani-proofs/i16-proofs.rs index d946148d78976..f57591f6b8e07 100644 --- a/library/core/tests/num/kani-proofs/i16-proofs.rs +++ b/library/core/tests/num/kani-proofs/i16-proofs.rs @@ -1,23 +1,50 @@ #[cfg(kani)] mod verification { - // use super::*; + // use super::*; - #[kani::proof] - fn verify_i16_unchecked_add() { - let num1: i16 = kani::any::(); - let num2: i16 = kani::any::(); + #[kani::proof] + fn verify_i16_unchecked_add() { + let num1: i16 = kani::any::(); + let num2: i16 = kani::any::(); - // Safety preconditions: - // - Positive number addition won't overflow - // - Negative number addition won't underflow - // Addition of two integers with different signs never overflows - // Undefined behavior occurs when overflow or underflow happens - kani::assume((num1 > 0 && num2 > 0 && num1 < i16::MAX - num2) - || (num1 < 0 && num2 < 0 && num1 > i16::MIN - num2)); + // Safety preconditions: + // - Positive number addition won't overflow + // - Negative number addition won't underflow + // Addition of two integers with different signs never overflows + // Undefined behavior occurs when overflow or underflow happens + kani::assume( + (num1 > 0 && num2 > 0 && num1 < i16::MAX - num2) + || (num1 < 0 && num2 < 0 && num1 > i16::MIN - num2), + ); unsafe { let result = num1.unchecked_add(num2); assert_eq!(Some(result), num1.checked_add(num2)); } - } -} \ No newline at end of file + } + + #[kani::proof] + fn verify_i16_unchecked_sub() { + // TODO + } + + #[kani::proof] + fn verify_i16_unchecked_mul() { + // TODO + } + + #[kani::proof] + fn verify_i16_unchecked_shl() { + // TODO + } + + #[kani::proof] + fn verify_i16_unchecked_shr() { + // TODO + } + + #[kani::proof] + fn verify_i16_unchecked_neg() { + // TODO + } +} diff --git a/library/core/tests/num/kani-proofs/i32-proofs.rs b/library/core/tests/num/kani-proofs/i32-proofs.rs index bb2d542dbc38f..0c8cca6a8d9c0 100644 --- a/library/core/tests/num/kani-proofs/i32-proofs.rs +++ b/library/core/tests/num/kani-proofs/i32-proofs.rs @@ -1,23 +1,50 @@ #[cfg(kani)] mod verification { - // use super::*; + // use super::*; - #[kani::proof] - fn verify_i32_unchecked_add() { - let num1: i32 = kani::any::(); - let num2: i32 = kani::any::(); + #[kani::proof] + fn verify_i32_unchecked_add() { + let num1: i32 = kani::any::(); + let num2: i32 = kani::any::(); - // Safety preconditions: - // - Positive number addition won't overflow - // - Negative number addition won't underflow - // Addition of two integers with different signs never overflows - // Undefined behavior occurs when overflow or underflow happens - kani::assume((num1 > 0 && num2 > 0 && num1 < i32::MAX - num2) - || (num1 < 0 && num2 < 0 && num1 > i32::MIN - num2)); + // Safety preconditions: + // - Positive number addition won't overflow + // - Negative number addition won't underflow + // Addition of two integers with different signs never overflows + // Undefined behavior occurs when overflow or underflow happens + kani::assume( + (num1 > 0 && num2 > 0 && num1 < i32::MAX - num2) + || (num1 < 0 && num2 < 0 && num1 > i32::MIN - num2), + ); unsafe { let result = num1.unchecked_add(num2); assert_eq!(Some(result), num1.checked_add(num2)); } - } -} \ No newline at end of file + } + + #[kani::proof] + fn verify_i32_unchecked_sub() { + // TODO + } + + #[kani::proof] + fn verify_i32_unchecked_mul() { + // TODO + } + + #[kani::proof] + fn verify_i32_unchecked_shl() { + // TODO + } + + #[kani::proof] + fn verify_i32_unchecked_shr() { + // TODO + } + + #[kani::proof] + fn verify_i32_unchecked_neg() { + // TODO + } +} diff --git a/library/core/tests/num/kani-proofs/i64-proofs.rs b/library/core/tests/num/kani-proofs/i64-proofs.rs index 822bac06eb733..658956c110dd8 100644 --- a/library/core/tests/num/kani-proofs/i64-proofs.rs +++ b/library/core/tests/num/kani-proofs/i64-proofs.rs @@ -1,23 +1,50 @@ #[cfg(kani)] mod verification { - // use super::*; + // use super::*; - #[kani::proof] - fn verify_i64_unchecked_add() { - let num1: i64 = kani::any::(); - let num2: i64 = kani::any::(); + #[kani::proof] + fn verify_i64_unchecked_add() { + let num1: i64 = kani::any::(); + let num2: i64 = kani::any::(); - // Safety preconditions: - // - Positive number addition won't overflow - // - Negative number addition won't underflow - // Addition of two integers with different signs never overflows - // Undefined behavior occurs when overflow or underflow happens - kani::assume((num1 > 0 && num2 > 0 && num1 < i64::MAX - num2) - || (num1 < 0 && num2 < 0 && num1 > i64::MIN - num2)); + // Safety preconditions: + // - Positive number addition won't overflow + // - Negative number addition won't underflow + // Addition of two integers with different signs never overflows + // Undefined behavior occurs when overflow or underflow happens + kani::assume( + (num1 > 0 && num2 > 0 && num1 < i64::MAX - num2) + || (num1 < 0 && num2 < 0 && num1 > i64::MIN - num2), + ); unsafe { let result = num1.unchecked_add(num2); assert_eq!(Some(result), num1.checked_add(num2)); } - } -} \ No newline at end of file + } + + #[kani::proof] + fn verify_i64_unchecked_sub() { + // TODO + } + + #[kani::proof] + fn verify_i64_unchecked_mul() { + // TODO + } + + #[kani::proof] + fn verify_i64_unchecked_shl() { + // TODO + } + + #[kani::proof] + fn verify_i64_unchecked_shr() { + // TODO + } + + #[kani::proof] + fn verify_i64_unchecked_neg() { + // TODO + } +} diff --git a/library/core/tests/num/kani-proofs/i8-proofs.rs b/library/core/tests/num/kani-proofs/i8-proofs.rs index edc3522f42be3..932407510ffda 100644 --- a/library/core/tests/num/kani-proofs/i8-proofs.rs +++ b/library/core/tests/num/kani-proofs/i8-proofs.rs @@ -1,23 +1,50 @@ #[cfg(kani)] mod verification { - // use super::*; + // use super::*; - #[kani::proof] - fn verify_i8_unchecked_add() { - let num1: i8 = kani::any::(); - let num2: i8 = kani::any::(); + #[kani::proof] + fn verify_i8_unchecked_add() { + let num1: i8 = kani::any::(); + let num2: i8 = kani::any::(); - // Safety preconditions: - // - Positive number addition won't overflow - // - Negative number addition won't underflow - // Addition of two integers with different signs never overflows - // Undefined behavior occurs when overflow or underflow happens - kani::assume((num1 > 0 && num2 > 0 && num1 < i8::MAX - num2) - || (num1 < 0 && num2 < 0 && num1 > i8::MIN - num2)); + // Safety preconditions: + // - Positive number addition won't overflow + // - Negative number addition won't underflow + // Addition of two integers with different signs never overflows + // Undefined behavior occurs when overflow or underflow happens + kani::assume( + (num1 > 0 && num2 > 0 && num1 < i8::MAX - num2) + || (num1 < 0 && num2 < 0 && num1 > i8::MIN - num2), + ); unsafe { let result = num1.unchecked_add(num2); assert_eq!(Some(result), num1.checked_add(num2)); } - } -} \ No newline at end of file + } + + #[kani::proof] + fn verify_i8_unchecked_sub() { + // TODO + } + + #[kani::proof] + fn verify_i8_unchecked_mul() { + // TODO + } + + #[kani::proof] + fn verify_i8_unchecked_shl() { + // TODO + } + + #[kani::proof] + fn verify_i8_unchecked_shr() { + // TODO + } + + #[kani::proof] + fn verify_i8_unchecked_neg() { + // TODO + } +} diff --git a/library/core/tests/num/kani-proofs/u128-proofs.rs b/library/core/tests/num/kani-proofs/u128-proofs.rs index 98d781ec024b5..4746ba6f6149d 100644 --- a/library/core/tests/num/kani-proofs/u128-proofs.rs +++ b/library/core/tests/num/kani-proofs/u128-proofs.rs @@ -1,21 +1,46 @@ #[cfg(kani)] mod verification { - // use super::*; + // use super::*; - #[kani::proof] - fn verify_u128_unchecked_add() { - let num1: u128 = kani::any::(); - let num2: u128 = kani::any::(); + #[kani::proof] + fn verify_u128_unchecked_add() { + let num1: u128 = kani::any::(); + let num2: u128 = kani::any::(); - // Safety preconditions: - // - Addition won't overflow - // Unsigned integers are always positive, so underflow won't happen - // Undefined behavior occurs when overflow happens - kani::assume(num1 < u128::MAX - num2); + // Safety preconditions: + // - Addition won't overflow + // Unsigned integers are always positive, so underflow won't happen + // Undefined behavior occurs when overflow happens + kani::assume(num1 < u128::MAX - num2); unsafe { let result = num1.unchecked_add(num2); assert_eq!(Some(result), num1.checked_add(num2)); } - } -} \ No newline at end of file + } + + #[kani::proof] + fn verify_u128_unchecked_sub() { + // TODO + } + + #[kani::proof] + fn verify_u128_unchecked_mul() { + // TODO + } + + #[kani::proof] + fn verify_u128_unchecked_shl() { + // TODO + } + + #[kani::proof] + fn verify_u128_unchecked_shr() { + // TODO + } + + #[kani::proof] + fn verify_u128_unchecked_neg() { + // TODO + } +} diff --git a/library/core/tests/num/kani-proofs/u16-proofs.rs b/library/core/tests/num/kani-proofs/u16-proofs.rs index 9dfff81d1abbb..c398cc0ee474b 100644 --- a/library/core/tests/num/kani-proofs/u16-proofs.rs +++ b/library/core/tests/num/kani-proofs/u16-proofs.rs @@ -1,21 +1,46 @@ #[cfg(kani)] mod verification { - // use super::*; + // use super::*; - #[kani::proof] - fn verify_u16_unchecked_add() { - let num1: u16 = kani::any::(); - let num2: u16 = kani::any::(); + #[kani::proof] + fn verify_u16_unchecked_add() { + let num1: u16 = kani::any::(); + let num2: u16 = kani::any::(); - // Safety preconditions: - // - Addition won't overflow - // Unsigned integers are always positive, so underflow won't happen - // Undefined behavior occurs when overflow happens - kani::assume(num1 < u16::MAX - num2); + // Safety preconditions: + // - Addition won't overflow + // Unsigned integers are always positive, so underflow won't happen + // Undefined behavior occurs when overflow happens + kani::assume(num1 < u16::MAX - num2); unsafe { let result = num1.unchecked_add(num2); assert_eq!(Some(result), num1.checked_add(num2)); } - } -} \ No newline at end of file + } + + #[kani::proof] + fn verify_u16_unchecked_sub() { + // TODO + } + + #[kani::proof] + fn verify_u16_unchecked_mul() { + // TODO + } + + #[kani::proof] + fn verify_u16_unchecked_shl() { + // TODO + } + + #[kani::proof] + fn verify_u16_unchecked_shr() { + // TODO + } + + #[kani::proof] + fn verify_u16_unchecked_neg() { + // TODO + } +} diff --git a/library/core/tests/num/kani-proofs/u32-proofs.rs b/library/core/tests/num/kani-proofs/u32-proofs.rs index 0d13bdcd2dce0..8f4dfb17949a5 100644 --- a/library/core/tests/num/kani-proofs/u32-proofs.rs +++ b/library/core/tests/num/kani-proofs/u32-proofs.rs @@ -1,21 +1,46 @@ #[cfg(kani)] mod verification { - // use super::*; + // use super::*; - #[kani::proof] - fn verify_u32_unchecked_add() { - let num1: u32 = kani::any::(); - let num2: u32 = kani::any::(); + #[kani::proof] + fn verify_u32_unchecked_add() { + let num1: u32 = kani::any::(); + let num2: u32 = kani::any::(); - // Safety preconditions: - // - Addition won't overflow - // Unsigned integers are always positive, so underflow won't happen - // Undefined behavior occurs when overflow happens - kani::assume(num1 < u32::MAX - num2); + // Safety preconditions: + // - Addition won't overflow + // Unsigned integers are always positive, so underflow won't happen + // Undefined behavior occurs when overflow happens + kani::assume(num1 < u32::MAX - num2); unsafe { let result = num1.unchecked_add(num2); assert_eq!(Some(result), num1.checked_add(num2)); } - } -} \ No newline at end of file + } + + #[kani::proof] + fn verify_u32_unchecked_sub() { + // TODO + } + + #[kani::proof] + fn verify_u32_unchecked_mul() { + // TODO + } + + #[kani::proof] + fn verify_u32_unchecked_shl() { + // TODO + } + + #[kani::proof] + fn verify_u32_unchecked_shr() { + // TODO + } + + #[kani::proof] + fn verify_u32_unchecked_neg() { + // TODO + } +} diff --git a/library/core/tests/num/kani-proofs/u64-proofs.rs b/library/core/tests/num/kani-proofs/u64-proofs.rs index 405468c96e0ba..d65c6f9178857 100644 --- a/library/core/tests/num/kani-proofs/u64-proofs.rs +++ b/library/core/tests/num/kani-proofs/u64-proofs.rs @@ -1,21 +1,46 @@ #[cfg(kani)] mod verification { - // use super::*; + // use super::*; - #[kani::proof] - fn verify_u64_unchecked_add() { - let num1: u64 = kani::any::(); - let num2: u64 = kani::any::(); + #[kani::proof] + fn verify_u64_unchecked_add() { + let num1: u64 = kani::any::(); + let num2: u64 = kani::any::(); - // Safety preconditions: - // - Addition won't overflow - // Unsigned integers are always positive, so underflow won't happen - // Undefined behavior occurs when overflow happens - kani::assume(num1 < u64::MAX - num2); + // Safety preconditions: + // - Addition won't overflow + // Unsigned integers are always positive, so underflow won't happen + // Undefined behavior occurs when overflow happens + kani::assume(num1 < u64::MAX - num2); unsafe { let result = num1.unchecked_add(num2); assert_eq!(Some(result), num1.checked_add(num2)); } - } -} \ No newline at end of file + } + + #[kani::proof] + fn verify_u64_unchecked_sub() { + // TODO + } + + #[kani::proof] + fn verify_u64_unchecked_mul() { + // TODO + } + + #[kani::proof] + fn verify_u64_unchecked_shl() { + // TODO + } + + #[kani::proof] + fn verify_u64_unchecked_shr() { + // TODO + } + + #[kani::proof] + fn verify_u64_unchecked_neg() { + // TODO + } +} diff --git a/library/core/tests/num/kani-proofs/u8-proofs.rs b/library/core/tests/num/kani-proofs/u8-proofs.rs index 89826fc292c4b..ef6569ceaa3ba 100644 --- a/library/core/tests/num/kani-proofs/u8-proofs.rs +++ b/library/core/tests/num/kani-proofs/u8-proofs.rs @@ -1,21 +1,46 @@ #[cfg(kani)] mod verification { - // use super::*; + // use super::*; - #[kani::proof] - fn verify_u8_unchecked_add() { - let num1: u8 = kani::any::(); - let num2: u8 = kani::any::(); + #[kani::proof] + fn verify_u8_unchecked_add() { + let num1: u8 = kani::any::(); + let num2: u8 = kani::any::(); - // Safety preconditions: - // - Addition won't overflow - // Unsigned integers are always positive, so underflow won't happen - // Undefined behavior occurs when overflow happens - kani::assume(num1 < u8::MAX - num2); + // Safety preconditions: + // - Addition won't overflow + // Unsigned integers are always positive, so underflow won't happen + // Undefined behavior occurs when overflow happens + kani::assume(num1 < u8::MAX - num2); unsafe { let result = num1.unchecked_add(num2); assert_eq!(Some(result), num1.checked_add(num2)); } - } -} \ No newline at end of file + } + + #[kani::proof] + fn verify_u8_unchecked_sub() { + // TODO + } + + #[kani::proof] + fn verify_u8_unchecked_mul() { + // TODO + } + + #[kani::proof] + fn verify_u8_unchecked_shl() { + // TODO + } + + #[kani::proof] + fn verify_u8_unchecked_shr() { + // TODO + } + + #[kani::proof] + fn verify_u8_unchecked_neg() { + // TODO + } +} From 4db34e48f01294fcda6aa07bc21d0b268d3b951e Mon Sep 17 00:00:00 2001 From: MWDZ Date: Wed, 11 Sep 2024 11:19:15 -0700 Subject: [PATCH 03/67] feat: add i8 uncheck shl verification --- library/core/tests/num/kani-proofs/i8-proofs.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/library/core/tests/num/kani-proofs/i8-proofs.rs b/library/core/tests/num/kani-proofs/i8-proofs.rs index 932407510ffda..b609bfb2335c0 100644 --- a/library/core/tests/num/kani-proofs/i8-proofs.rs +++ b/library/core/tests/num/kani-proofs/i8-proofs.rs @@ -1,3 +1,4 @@ +#![feature(unchecked_shifts)] #[cfg(kani)] mod verification { // use super::*; @@ -35,7 +36,16 @@ mod verification { #[kani::proof] fn verify_i8_unchecked_shl() { - // TODO + let num: i8 = kani::any::(); // Any value in type i8 + let shift_amount: u32 = kani::any::(); // Any shift amount in type u32 + + // Assume the shift value is smaller than 8 because i8 only has 8 digits + kani::assume(shift_amount < 8); + + unsafe { + let result = num.unchecked_shl(shift_amount); + assert_eq!(Some(result), num.checked_shl(shift_amount)); + } } #[kani::proof] From 54f550769076c522040bada869c4de27157342f1 Mon Sep 17 00:00:00 2001 From: MWDZ Date: Wed, 11 Sep 2024 11:54:55 -0700 Subject: [PATCH 04/67] feat: add i16 and i32 uncheck shl verification --- library/core/tests/num/kani-proofs/i16-proofs.rs | 12 +++++++++++- library/core/tests/num/kani-proofs/i32-proofs.rs | 13 ++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/library/core/tests/num/kani-proofs/i16-proofs.rs b/library/core/tests/num/kani-proofs/i16-proofs.rs index f57591f6b8e07..1958805372bca 100644 --- a/library/core/tests/num/kani-proofs/i16-proofs.rs +++ b/library/core/tests/num/kani-proofs/i16-proofs.rs @@ -1,3 +1,4 @@ +#![feature(unchecked_shifts)] #[cfg(kani)] mod verification { // use super::*; @@ -35,7 +36,16 @@ mod verification { #[kani::proof] fn verify_i16_unchecked_shl() { - // TODO + let num: i16 = kani::any::(); // Any value in type i16 + let shift_amount: u32 = kani::any::(); // Any shift amount in type u32 + + // Assume the shift value is smaller than 16 because i16 only has 16 bits + kani::assume(shift_amount < 16); + + unsafe { + let result = num.unchecked_shl(shift_amount); + assert_eq!(Some(result), num.checked_shl(shift_amount)); + } } #[kani::proof] diff --git a/library/core/tests/num/kani-proofs/i32-proofs.rs b/library/core/tests/num/kani-proofs/i32-proofs.rs index 0c8cca6a8d9c0..d2bce7b845002 100644 --- a/library/core/tests/num/kani-proofs/i32-proofs.rs +++ b/library/core/tests/num/kani-proofs/i32-proofs.rs @@ -1,3 +1,5 @@ +#![feature(unchecked_shifts)] + #[cfg(kani)] mod verification { // use super::*; @@ -30,7 +32,16 @@ mod verification { #[kani::proof] fn verify_i32_unchecked_mul() { - // TODO + let num: i32 = kani::any::(); // Any value in type i32 + let shift_amount: u32 = kani::any::(); // Any shift amount in type u32 + + // Assume the shift value is smaller than 32 because i32 only has 32 bits + kani::assume(shift_amount < 32); + + unsafe { + let result = num.unchecked_shl(shift_amount); + assert_eq!(Some(result), num.checked_shl(shift_amount)); + } } #[kani::proof] From 472da0e1eaebdf55fb742414d03d3f1cdeb759a3 Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Wed, 11 Sep 2024 17:31:20 -0700 Subject: [PATCH 05/67] added unsafe integer i8 unchecked sub following the template --- library/core/tests/num/kani-proofs/i8-proofs.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/library/core/tests/num/kani-proofs/i8-proofs.rs b/library/core/tests/num/kani-proofs/i8-proofs.rs index 932407510ffda..daa488a88a960 100644 --- a/library/core/tests/num/kani-proofs/i8-proofs.rs +++ b/library/core/tests/num/kani-proofs/i8-proofs.rs @@ -25,7 +25,22 @@ mod verification { #[kani::proof] fn verify_i8_unchecked_sub() { - // TODO + let num1: i8 = kani::any::(); + let num2: i8 = kani::any::(); + + // Safety preconditions: + // - Subtraction won't underflow when num1 is positive and num2 is negative + // - Subtraction won't overflow when num1 is negative and num2 is positive + // Undefined behavior occurs when overflow or underflow happens + kani::assume( + (num1 >= 0 && num2 <= 0 && num1 <= i8::MAX + num2) + || (num1 <= 0 && num2 >= 0 && num1 >= i8::MIN + num2), + ); + + unsafe { + let result = num1.unchecked_sub(num2); + assert_eq!(Some(result), num1.checked_sub(num2)); + } } #[kani::proof] From daaaf7e1c61c9cfad8a44912ca7b66ca6432ae86 Mon Sep 17 00:00:00 2001 From: yew005 Date: Mon, 16 Sep 2024 16:53:39 -0700 Subject: [PATCH 06/67] Experiment with two verification approaches in mod.rs of core::num --- library/core/src/num/mod.rs | 60 +++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 309e1ba958aee..bb4a8ca6e59c2 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1580,3 +1580,63 @@ from_str_radix_size_impl! { i16 isize, u16 usize } from_str_radix_size_impl! { i32 isize, u32 usize } #[cfg(target_pointer_width = "64")] from_str_radix_size_impl! { i64 isize, u64 usize } + + +// #[feature(unchecked_math)] +#[cfg(kani)] +use crate::kani; + +#[unstable(feature = "kani", issue = "none")] +mod verify { + use super::*; + + // Signed unchecked_add Safety preconditions: + // - Positive number addition won't overflow + // - Negative number addition won't underflow + // Addition of two integers with different signs never overflows + // Undefined behavior occurs when overflow or underflow happens + #[kani::requires(!num1.overflowing_add(num2).1)] + #[kani::ensures(|ret| *ret >= i8::MIN && *ret <= i8::MAX)] + fn i8_unchecked_add_wrapper(num1: i8, num2: i8) -> i8 { + unsafe { num1.unchecked_add(num2) } + } + + // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self + #[kani::proof_for_contract(i8_unchecked_add_wrapper)] + pub fn check_unchecked_add_i8() { + let num1: i8 = kani::any::(); + let num2: i8 = kani::any::(); + + // kani::assume( + // (num1 > 0 && num2 > 0 && num1 < i8::MAX - num2) + // || (num1 < 0 && num2 < 0 && num1 > i8::MIN - num2), + // ); + + unsafe { + // Kani ensures the inputs satisfy preconditions + i8_unchecked_add_wrapper(num1, num2); + // Kani ensures the output satisfy postconditions + } + } + + // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self + #[kani::proof] + pub fn check_unchecked_add_i16() { + let num1: i16 = kani::any::(); + let num2: i16 = kani::any::(); + + // overflowing_add return (result, bool) where bool is if + // add will overflow. This check takes the boolean. + kani::assume(!num1.overflowing_add(num2).1); + + unsafe { + let result = num1.unchecked_add(num2); + + // Either check result + assert_eq!(Some(result), num1.checked_add(num2)); + + // Or check range of result + assert!(result >= i16::MIN && result <= i16::MAX); + } + } +} From 1c9dbde3758ae3889aa73ca491849c3303af0fc1 Mon Sep 17 00:00:00 2001 From: yew005 Date: Wed, 18 Sep 2024 14:27:27 -0700 Subject: [PATCH 07/67] Add contracts to unchecked_add && Add i8::unchecked_add proof --- library/core/src/num/int_macros.rs | 2 ++ library/core/src/num/mod.rs | 37 ++--------------------------- library/core/src/num/uint_macros.rs | 2 ++ 3 files changed, 6 insertions(+), 35 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 878a911dde50d..6e97b7999eef4 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -511,6 +511,8 @@ macro_rules! int_impl { without modifying the original"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[kani::requires(!self.overflowing_add(rhs).1)] + #[kani::ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index f5223023c8f2b..9cacb61a47030 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1596,48 +1596,15 @@ mod verify { // - Negative number addition won't underflow // Addition of two integers with different signs never overflows // Undefined behavior occurs when overflow or underflow happens - #[kani::requires(!num1.overflowing_add(num2).1)] - #[kani::ensures(|ret| *ret >= i8::MIN && *ret <= i8::MAX)] - fn i8_unchecked_add_wrapper(num1: i8, num2: i8) -> i8 { - unsafe { num1.unchecked_add(num2) } - } // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self - #[kani::proof_for_contract(i8_unchecked_add_wrapper)] + #[kani::proof_for_contract(i8::unchecked_add)] pub fn check_unchecked_add_i8() { let num1: i8 = kani::any::(); let num2: i8 = kani::any::(); - - // kani::assume( - // (num1 > 0 && num2 > 0 && num1 < i8::MAX - num2) - // || (num1 < 0 && num2 < 0 && num1 > i8::MIN - num2), - // ); - - unsafe { - // Kani ensures the inputs satisfy preconditions - i8_unchecked_add_wrapper(num1, num2); - // Kani ensures the output satisfy postconditions - } - } - - // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self - #[kani::proof] - pub fn check_unchecked_add_i16() { - let num1: i16 = kani::any::(); - let num2: i16 = kani::any::(); - - // overflowing_add return (result, bool) where bool is if - // add will overflow. This check takes the boolean. - kani::assume(!num1.overflowing_add(num2).1); unsafe { - let result = num1.unchecked_add(num2); - - // Either check result - assert_eq!(Some(result), num1.checked_add(num2)); - - // Or check range of result - assert!(result >= i16::MIN && result <= i16::MAX); + num1.unchecked_add(num2); } } } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index d9036abecc592..64d43c394e2b2 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -558,6 +558,8 @@ macro_rules! uint_impl { without modifying the original"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[kani::requires(!self.overflowing_add(rhs).1)] + #[kani::ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, From 894231fb15f248db0b76efab587e3c7cdb2ac1cb Mon Sep 17 00:00:00 2001 From: yew005 Date: Wed, 18 Sep 2024 14:31:58 -0700 Subject: [PATCH 08/67] Format core::num mod.rs --- library/core/src/num/mod.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 9cacb61a47030..1ebc09815717a 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1397,7 +1397,10 @@ const fn from_str_radix_panic_ct(_radix: u32) -> ! { #[track_caller] fn from_str_radix_panic_rt(radix: u32) -> ! { - panic!("from_str_radix_int: must lie in the range `[2, 36]` - found {}", radix); + panic!( + "from_str_radix_int: must lie in the range `[2, 36]` - found {}", + radix + ); } #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] @@ -1582,7 +1585,6 @@ from_str_radix_size_impl! { i32 isize, u32 usize } #[cfg(target_pointer_width = "64")] from_str_radix_size_impl! { i64 isize, u64 usize } - // #[feature(unchecked_math)] #[cfg(kani)] use crate::kani; @@ -1596,6 +1598,9 @@ mod verify { // - Negative number addition won't underflow // Addition of two integers with different signs never overflows // Undefined behavior occurs when overflow or underflow happens + // Target contracts: + // #[kani::requires(!self.overflowing_add(rhs).1)] + // #[kani::ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self #[kani::proof_for_contract(i8::unchecked_add)] From 2bc4faf2ff544da0611eaa67e4644535a629bf8b Mon Sep 17 00:00:00 2001 From: yew005 Date: Wed, 18 Sep 2024 14:41:15 -0700 Subject: [PATCH 09/67] Add comment for unchecked_add proofs --- library/core/src/num/mod.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 1ebc09815717a..02d686519b9a2 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1593,16 +1593,27 @@ use crate::kani; mod verify { use super::*; - // Signed unchecked_add Safety preconditions: - // - Positive number addition won't overflow - // - Negative number addition won't underflow - // Addition of two integers with different signs never overflows - // Undefined behavior occurs when overflow or underflow happens + // `unchecked_add` proofs + // // Target contracts: // #[kani::requires(!self.overflowing_add(rhs).1)] // #[kani::ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] - + // + // Target function: // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self + // + // Safety preconditions: + // 1. Signed integers (i8, i16, i32, i64, i128): + // - Positive number addition won't overflow + // - Negative number addition won't underflow + // Addition of two integers with different signs never overflows + // Undefined behavior occurs when overflow or underflow happens + // + // 2. Unsigned integers (u8, u16, u32, u64, u128): + // - Addition won't overflow + // Unsigned integers are always positive, so underflow won't happen + // Undefined behavior occurs when overflow happens + #[kani::proof_for_contract(i8::unchecked_add)] pub fn check_unchecked_add_i8() { let num1: i8 = kani::any::(); From d9d4b5f377b0f6a6ca7986d371961633698262e1 Mon Sep 17 00:00:00 2001 From: yew005 Date: Thu, 19 Sep 2024 15:11:12 -0700 Subject: [PATCH 10/67] Add harnesses for i16,i32,i64,i128 unchecked_add --- library/core/src/num/mod.rs | 47 +++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 02d686519b9a2..ae9762bd72d62 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1585,7 +1585,6 @@ from_str_radix_size_impl! { i32 isize, u32 usize } #[cfg(target_pointer_width = "64")] from_str_radix_size_impl! { i64 isize, u64 usize } -// #[feature(unchecked_math)] #[cfg(kani)] use crate::kani; @@ -1598,17 +1597,17 @@ mod verify { // Target contracts: // #[kani::requires(!self.overflowing_add(rhs).1)] // #[kani::ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] - // + // // Target function: // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self - // + // // Safety preconditions: // 1. Signed integers (i8, i16, i32, i64, i128): // - Positive number addition won't overflow // - Negative number addition won't underflow // Addition of two integers with different signs never overflows // Undefined behavior occurs when overflow or underflow happens - // + // // 2. Unsigned integers (u8, u16, u32, u64, u128): // - Addition won't overflow // Unsigned integers are always positive, so underflow won't happen @@ -1623,4 +1622,44 @@ mod verify { num1.unchecked_add(num2); } } + + #[kani::proof_for_contract(i16::unchecked_add)] + pub fn check_unchecked_add_i16() { + let num1: i16 = kani::any::(); + let num2: i16 = kani::any::(); + + unsafe { + num1.unchecked_add(num2); + } + } + + #[kani::proof_for_contract(i32::unchecked_add)] + pub fn check_unchecked_add_i32() { + let num1: i32 = kani::any::(); + let num2: i32 = kani::any::(); + + unsafe { + num1.unchecked_add(num2); + } + } + + #[kani::proof_for_contract(i64::unchecked_add)] + pub fn check_unchecked_add_i64() { + let num1: i64 = kani::any::(); + let num2: i64 = kani::any::(); + + unsafe { + num1.unchecked_add(num2); + } + } + + #[kani::proof_for_contract(i128::unchecked_add)] + pub fn check_unchecked_add_i128() { + let num1: i128 = kani::any::(); + let num2: i128 = kani::any::(); + + unsafe { + num1.unchecked_add(num2); + } + } } From 07c8b4a5dc06a826feee8fef416955765d197d0d Mon Sep 17 00:00:00 2001 From: yew005 Date: Thu, 19 Sep 2024 15:18:50 -0700 Subject: [PATCH 11/67] Add harnesses for u8,u16,u32,u64,u128 unchecked_add --- library/core/src/num/mod.rs | 50 +++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index ae9762bd72d62..95984fccebc1f 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1662,4 +1662,54 @@ mod verify { num1.unchecked_add(num2); } } + + #[kani::proof_for_contract(u8::unchecked_add)] + pub fn check_unchecked_add_u8() { + let num1: u8 = kani::any::(); + let num2: u8 = kani::any::(); + + unsafe { + num1.unchecked_add(num2); + } + } + + #[kani::proof_for_contract(u16::unchecked_add)] + pub fn check_unchecked_add_u16() { + let num1: u16 = kani::any::(); + let num2: u16 = kani::any::(); + + unsafe { + num1.unchecked_add(num2); + } + } + + #[kani::proof_for_contract(u32::unchecked_add)] + pub fn check_unchecked_add_u32() { + let num1: u32 = kani::any::(); + let num2: u32 = kani::any::(); + + unsafe { + num1.unchecked_add(num2); + } + } + + #[kani::proof_for_contract(u64::unchecked_add)] + pub fn check_unchecked_add_u64() { + let num1: u64 = kani::any::(); + let num2: u64 = kani::any::(); + + unsafe { + num1.unchecked_add(num2); + } + } + + #[kani::proof_for_contract(u128::unchecked_add)] + pub fn check_unchecked_add_u128() { + let num1: u128 = kani::any::(); + let num2: u128 = kani::any::(); + + unsafe { + num1.unchecked_add(num2); + } + } } From 1fd4c6ae936496966b9f305906065d10b1c1b80a Mon Sep 17 00:00:00 2001 From: yew005 Date: Thu, 19 Sep 2024 15:22:12 -0700 Subject: [PATCH 12/67] Cleanup misplaced proofs --- .../core/tests/num/kani-proofs/i128-proofs.rs | 50 ------------------- .../core/tests/num/kani-proofs/i16-proofs.rs | 50 ------------------- .../core/tests/num/kani-proofs/i32-proofs.rs | 50 ------------------- .../core/tests/num/kani-proofs/i64-proofs.rs | 50 ------------------- .../core/tests/num/kani-proofs/i8-proofs.rs | 50 ------------------- .../core/tests/num/kani-proofs/u128-proofs.rs | 46 ----------------- .../core/tests/num/kani-proofs/u16-proofs.rs | 46 ----------------- .../core/tests/num/kani-proofs/u32-proofs.rs | 46 ----------------- .../core/tests/num/kani-proofs/u64-proofs.rs | 46 ----------------- .../core/tests/num/kani-proofs/u8-proofs.rs | 46 ----------------- 10 files changed, 480 deletions(-) delete mode 100644 library/core/tests/num/kani-proofs/i128-proofs.rs delete mode 100644 library/core/tests/num/kani-proofs/i16-proofs.rs delete mode 100644 library/core/tests/num/kani-proofs/i32-proofs.rs delete mode 100644 library/core/tests/num/kani-proofs/i64-proofs.rs delete mode 100644 library/core/tests/num/kani-proofs/i8-proofs.rs delete mode 100644 library/core/tests/num/kani-proofs/u128-proofs.rs delete mode 100644 library/core/tests/num/kani-proofs/u16-proofs.rs delete mode 100644 library/core/tests/num/kani-proofs/u32-proofs.rs delete mode 100644 library/core/tests/num/kani-proofs/u64-proofs.rs delete mode 100644 library/core/tests/num/kani-proofs/u8-proofs.rs diff --git a/library/core/tests/num/kani-proofs/i128-proofs.rs b/library/core/tests/num/kani-proofs/i128-proofs.rs deleted file mode 100644 index 4ac03ec8714fe..0000000000000 --- a/library/core/tests/num/kani-proofs/i128-proofs.rs +++ /dev/null @@ -1,50 +0,0 @@ -#[cfg(kani)] -mod verification { - // use super::*; - - #[kani::proof] - fn verify_i128_unchecked_add() { - let num1: i128 = kani::any::(); - let num2: i128 = kani::any::(); - - // Safety preconditions: - // - Positive number addition won't overflow - // - Negative number addition won't underflow - // Addition of two integers with different signs never overflows - // Undefined behavior occurs when overflow or underflow happens - kani::assume( - (num1 > 0 && num2 > 0 && num1 < i128::MAX - num2) - || (num1 < 0 && num2 < 0 && num1 > i128::MIN - num2), - ); - - unsafe { - let result = num1.unchecked_add(num2); - assert_eq!(Some(result), num1.checked_add(num2)); - } - } - - #[kani::proof] - fn verify_i128_unchecked_sub() { - // TODO - } - - #[kani::proof] - fn verify_i128_unchecked_mul() { - // TODO - } - - #[kani::proof] - fn verify_i128_unchecked_shl() { - // TODO - } - - #[kani::proof] - fn verify_i128_unchecked_shr() { - // TODO - } - - #[kani::proof] - fn verify_i128_unchecked_neg() { - // TODO - } -} diff --git a/library/core/tests/num/kani-proofs/i16-proofs.rs b/library/core/tests/num/kani-proofs/i16-proofs.rs deleted file mode 100644 index f57591f6b8e07..0000000000000 --- a/library/core/tests/num/kani-proofs/i16-proofs.rs +++ /dev/null @@ -1,50 +0,0 @@ -#[cfg(kani)] -mod verification { - // use super::*; - - #[kani::proof] - fn verify_i16_unchecked_add() { - let num1: i16 = kani::any::(); - let num2: i16 = kani::any::(); - - // Safety preconditions: - // - Positive number addition won't overflow - // - Negative number addition won't underflow - // Addition of two integers with different signs never overflows - // Undefined behavior occurs when overflow or underflow happens - kani::assume( - (num1 > 0 && num2 > 0 && num1 < i16::MAX - num2) - || (num1 < 0 && num2 < 0 && num1 > i16::MIN - num2), - ); - - unsafe { - let result = num1.unchecked_add(num2); - assert_eq!(Some(result), num1.checked_add(num2)); - } - } - - #[kani::proof] - fn verify_i16_unchecked_sub() { - // TODO - } - - #[kani::proof] - fn verify_i16_unchecked_mul() { - // TODO - } - - #[kani::proof] - fn verify_i16_unchecked_shl() { - // TODO - } - - #[kani::proof] - fn verify_i16_unchecked_shr() { - // TODO - } - - #[kani::proof] - fn verify_i16_unchecked_neg() { - // TODO - } -} diff --git a/library/core/tests/num/kani-proofs/i32-proofs.rs b/library/core/tests/num/kani-proofs/i32-proofs.rs deleted file mode 100644 index 0c8cca6a8d9c0..0000000000000 --- a/library/core/tests/num/kani-proofs/i32-proofs.rs +++ /dev/null @@ -1,50 +0,0 @@ -#[cfg(kani)] -mod verification { - // use super::*; - - #[kani::proof] - fn verify_i32_unchecked_add() { - let num1: i32 = kani::any::(); - let num2: i32 = kani::any::(); - - // Safety preconditions: - // - Positive number addition won't overflow - // - Negative number addition won't underflow - // Addition of two integers with different signs never overflows - // Undefined behavior occurs when overflow or underflow happens - kani::assume( - (num1 > 0 && num2 > 0 && num1 < i32::MAX - num2) - || (num1 < 0 && num2 < 0 && num1 > i32::MIN - num2), - ); - - unsafe { - let result = num1.unchecked_add(num2); - assert_eq!(Some(result), num1.checked_add(num2)); - } - } - - #[kani::proof] - fn verify_i32_unchecked_sub() { - // TODO - } - - #[kani::proof] - fn verify_i32_unchecked_mul() { - // TODO - } - - #[kani::proof] - fn verify_i32_unchecked_shl() { - // TODO - } - - #[kani::proof] - fn verify_i32_unchecked_shr() { - // TODO - } - - #[kani::proof] - fn verify_i32_unchecked_neg() { - // TODO - } -} diff --git a/library/core/tests/num/kani-proofs/i64-proofs.rs b/library/core/tests/num/kani-proofs/i64-proofs.rs deleted file mode 100644 index 658956c110dd8..0000000000000 --- a/library/core/tests/num/kani-proofs/i64-proofs.rs +++ /dev/null @@ -1,50 +0,0 @@ -#[cfg(kani)] -mod verification { - // use super::*; - - #[kani::proof] - fn verify_i64_unchecked_add() { - let num1: i64 = kani::any::(); - let num2: i64 = kani::any::(); - - // Safety preconditions: - // - Positive number addition won't overflow - // - Negative number addition won't underflow - // Addition of two integers with different signs never overflows - // Undefined behavior occurs when overflow or underflow happens - kani::assume( - (num1 > 0 && num2 > 0 && num1 < i64::MAX - num2) - || (num1 < 0 && num2 < 0 && num1 > i64::MIN - num2), - ); - - unsafe { - let result = num1.unchecked_add(num2); - assert_eq!(Some(result), num1.checked_add(num2)); - } - } - - #[kani::proof] - fn verify_i64_unchecked_sub() { - // TODO - } - - #[kani::proof] - fn verify_i64_unchecked_mul() { - // TODO - } - - #[kani::proof] - fn verify_i64_unchecked_shl() { - // TODO - } - - #[kani::proof] - fn verify_i64_unchecked_shr() { - // TODO - } - - #[kani::proof] - fn verify_i64_unchecked_neg() { - // TODO - } -} diff --git a/library/core/tests/num/kani-proofs/i8-proofs.rs b/library/core/tests/num/kani-proofs/i8-proofs.rs deleted file mode 100644 index 932407510ffda..0000000000000 --- a/library/core/tests/num/kani-proofs/i8-proofs.rs +++ /dev/null @@ -1,50 +0,0 @@ -#[cfg(kani)] -mod verification { - // use super::*; - - #[kani::proof] - fn verify_i8_unchecked_add() { - let num1: i8 = kani::any::(); - let num2: i8 = kani::any::(); - - // Safety preconditions: - // - Positive number addition won't overflow - // - Negative number addition won't underflow - // Addition of two integers with different signs never overflows - // Undefined behavior occurs when overflow or underflow happens - kani::assume( - (num1 > 0 && num2 > 0 && num1 < i8::MAX - num2) - || (num1 < 0 && num2 < 0 && num1 > i8::MIN - num2), - ); - - unsafe { - let result = num1.unchecked_add(num2); - assert_eq!(Some(result), num1.checked_add(num2)); - } - } - - #[kani::proof] - fn verify_i8_unchecked_sub() { - // TODO - } - - #[kani::proof] - fn verify_i8_unchecked_mul() { - // TODO - } - - #[kani::proof] - fn verify_i8_unchecked_shl() { - // TODO - } - - #[kani::proof] - fn verify_i8_unchecked_shr() { - // TODO - } - - #[kani::proof] - fn verify_i8_unchecked_neg() { - // TODO - } -} diff --git a/library/core/tests/num/kani-proofs/u128-proofs.rs b/library/core/tests/num/kani-proofs/u128-proofs.rs deleted file mode 100644 index 4746ba6f6149d..0000000000000 --- a/library/core/tests/num/kani-proofs/u128-proofs.rs +++ /dev/null @@ -1,46 +0,0 @@ -#[cfg(kani)] -mod verification { - // use super::*; - - #[kani::proof] - fn verify_u128_unchecked_add() { - let num1: u128 = kani::any::(); - let num2: u128 = kani::any::(); - - // Safety preconditions: - // - Addition won't overflow - // Unsigned integers are always positive, so underflow won't happen - // Undefined behavior occurs when overflow happens - kani::assume(num1 < u128::MAX - num2); - - unsafe { - let result = num1.unchecked_add(num2); - assert_eq!(Some(result), num1.checked_add(num2)); - } - } - - #[kani::proof] - fn verify_u128_unchecked_sub() { - // TODO - } - - #[kani::proof] - fn verify_u128_unchecked_mul() { - // TODO - } - - #[kani::proof] - fn verify_u128_unchecked_shl() { - // TODO - } - - #[kani::proof] - fn verify_u128_unchecked_shr() { - // TODO - } - - #[kani::proof] - fn verify_u128_unchecked_neg() { - // TODO - } -} diff --git a/library/core/tests/num/kani-proofs/u16-proofs.rs b/library/core/tests/num/kani-proofs/u16-proofs.rs deleted file mode 100644 index c398cc0ee474b..0000000000000 --- a/library/core/tests/num/kani-proofs/u16-proofs.rs +++ /dev/null @@ -1,46 +0,0 @@ -#[cfg(kani)] -mod verification { - // use super::*; - - #[kani::proof] - fn verify_u16_unchecked_add() { - let num1: u16 = kani::any::(); - let num2: u16 = kani::any::(); - - // Safety preconditions: - // - Addition won't overflow - // Unsigned integers are always positive, so underflow won't happen - // Undefined behavior occurs when overflow happens - kani::assume(num1 < u16::MAX - num2); - - unsafe { - let result = num1.unchecked_add(num2); - assert_eq!(Some(result), num1.checked_add(num2)); - } - } - - #[kani::proof] - fn verify_u16_unchecked_sub() { - // TODO - } - - #[kani::proof] - fn verify_u16_unchecked_mul() { - // TODO - } - - #[kani::proof] - fn verify_u16_unchecked_shl() { - // TODO - } - - #[kani::proof] - fn verify_u16_unchecked_shr() { - // TODO - } - - #[kani::proof] - fn verify_u16_unchecked_neg() { - // TODO - } -} diff --git a/library/core/tests/num/kani-proofs/u32-proofs.rs b/library/core/tests/num/kani-proofs/u32-proofs.rs deleted file mode 100644 index 8f4dfb17949a5..0000000000000 --- a/library/core/tests/num/kani-proofs/u32-proofs.rs +++ /dev/null @@ -1,46 +0,0 @@ -#[cfg(kani)] -mod verification { - // use super::*; - - #[kani::proof] - fn verify_u32_unchecked_add() { - let num1: u32 = kani::any::(); - let num2: u32 = kani::any::(); - - // Safety preconditions: - // - Addition won't overflow - // Unsigned integers are always positive, so underflow won't happen - // Undefined behavior occurs when overflow happens - kani::assume(num1 < u32::MAX - num2); - - unsafe { - let result = num1.unchecked_add(num2); - assert_eq!(Some(result), num1.checked_add(num2)); - } - } - - #[kani::proof] - fn verify_u32_unchecked_sub() { - // TODO - } - - #[kani::proof] - fn verify_u32_unchecked_mul() { - // TODO - } - - #[kani::proof] - fn verify_u32_unchecked_shl() { - // TODO - } - - #[kani::proof] - fn verify_u32_unchecked_shr() { - // TODO - } - - #[kani::proof] - fn verify_u32_unchecked_neg() { - // TODO - } -} diff --git a/library/core/tests/num/kani-proofs/u64-proofs.rs b/library/core/tests/num/kani-proofs/u64-proofs.rs deleted file mode 100644 index d65c6f9178857..0000000000000 --- a/library/core/tests/num/kani-proofs/u64-proofs.rs +++ /dev/null @@ -1,46 +0,0 @@ -#[cfg(kani)] -mod verification { - // use super::*; - - #[kani::proof] - fn verify_u64_unchecked_add() { - let num1: u64 = kani::any::(); - let num2: u64 = kani::any::(); - - // Safety preconditions: - // - Addition won't overflow - // Unsigned integers are always positive, so underflow won't happen - // Undefined behavior occurs when overflow happens - kani::assume(num1 < u64::MAX - num2); - - unsafe { - let result = num1.unchecked_add(num2); - assert_eq!(Some(result), num1.checked_add(num2)); - } - } - - #[kani::proof] - fn verify_u64_unchecked_sub() { - // TODO - } - - #[kani::proof] - fn verify_u64_unchecked_mul() { - // TODO - } - - #[kani::proof] - fn verify_u64_unchecked_shl() { - // TODO - } - - #[kani::proof] - fn verify_u64_unchecked_shr() { - // TODO - } - - #[kani::proof] - fn verify_u64_unchecked_neg() { - // TODO - } -} diff --git a/library/core/tests/num/kani-proofs/u8-proofs.rs b/library/core/tests/num/kani-proofs/u8-proofs.rs deleted file mode 100644 index ef6569ceaa3ba..0000000000000 --- a/library/core/tests/num/kani-proofs/u8-proofs.rs +++ /dev/null @@ -1,46 +0,0 @@ -#[cfg(kani)] -mod verification { - // use super::*; - - #[kani::proof] - fn verify_u8_unchecked_add() { - let num1: u8 = kani::any::(); - let num2: u8 = kani::any::(); - - // Safety preconditions: - // - Addition won't overflow - // Unsigned integers are always positive, so underflow won't happen - // Undefined behavior occurs when overflow happens - kani::assume(num1 < u8::MAX - num2); - - unsafe { - let result = num1.unchecked_add(num2); - assert_eq!(Some(result), num1.checked_add(num2)); - } - } - - #[kani::proof] - fn verify_u8_unchecked_sub() { - // TODO - } - - #[kani::proof] - fn verify_u8_unchecked_mul() { - // TODO - } - - #[kani::proof] - fn verify_u8_unchecked_shl() { - // TODO - } - - #[kani::proof] - fn verify_u8_unchecked_shr() { - // TODO - } - - #[kani::proof] - fn verify_u8_unchecked_neg() { - // TODO - } -} From b360311c955df808f38ec2c4c5ddaa57f4f810e3 Mon Sep 17 00:00:00 2001 From: yew005 Date: Fri, 20 Sep 2024 12:57:35 -0700 Subject: [PATCH 13/67] Clean up comment --- library/core/src/num/mod.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 95984fccebc1f..95fc1f394f63b 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1601,17 +1601,8 @@ mod verify { // Target function: // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self // - // Safety preconditions: - // 1. Signed integers (i8, i16, i32, i64, i128): - // - Positive number addition won't overflow - // - Negative number addition won't underflow - // Addition of two integers with different signs never overflows - // Undefined behavior occurs when overflow or underflow happens - // - // 2. Unsigned integers (u8, u16, u32, u64, u128): - // - Addition won't overflow - // Unsigned integers are always positive, so underflow won't happen - // Undefined behavior occurs when overflow happens + // Target types: + // i{8,16,32,64,128} and u{8,16,32,64,128} -- 10 types in total #[kani::proof_for_contract(i8::unchecked_add)] pub fn check_unchecked_add_i8() { From 8cbca87171dc3efa4264e09df094866b499ecf07 Mon Sep 17 00:00:00 2001 From: yew005 Date: Fri, 20 Sep 2024 12:58:16 -0700 Subject: [PATCH 14/67] Format comment --- library/core/src/num/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 95fc1f394f63b..766b52f227ffa 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1594,16 +1594,15 @@ mod verify { // `unchecked_add` proofs // + // Target types: + // i{8,16,32,64,128} and u{8,16,32,64,128} -- 10 types in total + // // Target contracts: // #[kani::requires(!self.overflowing_add(rhs).1)] // #[kani::ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] // // Target function: // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self - // - // Target types: - // i{8,16,32,64,128} and u{8,16,32,64,128} -- 10 types in total - #[kani::proof_for_contract(i8::unchecked_add)] pub fn check_unchecked_add_i8() { let num1: i8 = kani::any::(); From 0eef8581caa0f581689307aa6c3611bde6aec4b2 Mon Sep 17 00:00:00 2001 From: yew005 Date: Fri, 20 Sep 2024 13:15:21 -0700 Subject: [PATCH 15/67] Remove before contracts. Fix import in --- library/core/src/num/int_macros.rs | 4 ++-- library/core/src/num/mod.rs | 7 ++++--- library/core/src/num/uint_macros.rs | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 6e97b7999eef4..fa0c9c98dde74 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -511,8 +511,8 @@ macro_rules! int_impl { without modifying the original"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[kani::requires(!self.overflowing_add(rhs).1)] - #[kani::ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] + #[requires(!self.overflowing_add(rhs).1)] + #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 766b52f227ffa..28aeb1b897149 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -5,6 +5,10 @@ use crate::str::FromStr; use crate::ub_checks::assert_unsafe_precondition; use crate::{ascii, intrinsics, mem}; +use safety::{ensures, requires}; + +#[cfg(kani)] +use crate::kani; // Used because the `?` operator is not allowed in a const context. macro_rules! try_opt { @@ -1585,9 +1589,6 @@ from_str_radix_size_impl! { i32 isize, u32 usize } #[cfg(target_pointer_width = "64")] from_str_radix_size_impl! { i64 isize, u64 usize } -#[cfg(kani)] -use crate::kani; - #[unstable(feature = "kani", issue = "none")] mod verify { use super::*; diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 64d43c394e2b2..48f0e2d4dc24b 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -558,8 +558,8 @@ macro_rules! uint_impl { without modifying the original"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[kani::requires(!self.overflowing_add(rhs).1)] - #[kani::ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] + #[requires(!self.overflowing_add(rhs).1)] + #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, From 4858a531de50d0a2654ab6cbf66461b78ef210a9 Mon Sep 17 00:00:00 2001 From: yew005 Date: Fri, 20 Sep 2024 13:43:34 -0700 Subject: [PATCH 16/67] Fix comment --- library/core/src/num/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 28aeb1b897149..4dbce78394d9e 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1599,8 +1599,8 @@ mod verify { // i{8,16,32,64,128} and u{8,16,32,64,128} -- 10 types in total // // Target contracts: - // #[kani::requires(!self.overflowing_add(rhs).1)] - // #[kani::ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] + // #[requires(!self.overflowing_add(rhs).1)] + // #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] // // Target function: // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self From bbd75f7189ba5bae71febf02a94db85a6f037928 Mon Sep 17 00:00:00 2001 From: Rajath Date: Mon, 23 Sep 2024 13:01:33 -0700 Subject: [PATCH 17/67] unchecked multiplication and shift right --- library/core/src/num/int_macros.rs | 6 +- library/core/src/num/mod.rs | 228 ++++++++++++++++++++++++++++ library/core/src/num/uint_macros.rs | 4 + 3 files changed, 237 insertions(+), 1 deletion(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index fa0c9c98dde74..1f82f24c6bf27 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -817,6 +817,8 @@ macro_rules! int_impl { without modifying the original"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[requires(!self.overflowing_mul(rhs).1)] + #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -1423,8 +1425,10 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")] - #[inline(always)] + #[inline(always)]0 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[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. + #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] // ensures result is within range after bit shift pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 4dbce78394d9e..661c3f11cd3b6 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1703,4 +1703,232 @@ mod verify { num1.unchecked_add(num2); } } + + // `unchecked_mul` proofs + // + // Target types: + // i{8,16,32,64,128} and u{8,16,32,64,128} -- 10 types in total + // + // Target Files : src/num/int_macros.rs , src/num/uint_macros.rs + // + // Target contracts : + // #[requires(!self.overflowing_mul(rhs).1)] + // #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] + // + // Target function definition: + // pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self + + #[kani::proof_for_contract(i8::unchecked_mul)] + pub fn check_unchecked_mul_i8() { + let num1: i8 = kani::any::(); + let num2: i8 = kani::any::(); + + unsafe { + num1.unchecked_mul(num2); + } + } + + #[kani::proof_for_contract(i16::unchecked_mul)] + pub fn check_unchecked_mul_i16() { + let num1: i16 = kani::any::(); + let num2: i16 = kani::any::(); + + unsafe { + num1.unchecked_mul(num2); + } + } + + #[kani::proof_for_contract(i32::unchecked_mul)] + pub fn check_unchecked_mul_i32() { + let num1: i32 = kani::any::(); + let num2: i32 = kani::any::(); + + unsafe { + num1.unchecked_mul(num2); + } + } + + #[kani::proof_for_contract(i64::unchecked_mul)] + pub fn check_unchecked_mul_i64() { + let num1: i64 = kani::any::(); + let num2: i64 = kani::any::(); + + unsafe { + num1.unchecked_mul(num2); + } + } + + #[kani::proof_for_contract(i128::unchecked_mul)] + pub fn check_unchecked_mul_i128() { + let num1: i128 = kani::any::(); + let num2: i128 = kani::any::(); + + unsafe { + num1.unchecked_mul(num2); + } + } + + #[kani::proof_for_contract(u8::unchecked_mul)] + pub fn check_unchecked_mul_u8() { + let num1: u8 = kani::any::(); + let num2: u8 = kani::any::(); + + unsafe { + num1.unchecked_mul(num2); + } + } + + #[kani::proof_for_contract(u16::unchecked_mul)] + pub fn check_unchecked_mul_u16() { + let num1: u16 = kani::any::(); + let num2: u16 = kani::any::(); + + unsafe { + num1.unchecked_mul(num2); + } + } + + #[kani::proof_for_contract(u32::unchecked_mul)] + pub fn check_unchecked_mul_u32() { + let num1: u32 = kani::any::(); + let num2: u32 = kani::any::(); + + unsafe { + num1.unchecked_mul(num2); + } + } + + #[kani::proof_for_contract(u64::unchecked_mul)] + pub fn check_unchecked_mul_u64() { + let num1: u64 = kani::any::(); + let num2: u64 = kani::any::(); + + unsafe { + num1.unchecked_mul(num2); + } + } + + #[kani::proof_for_contract(u128::unchecked_mul)] + pub fn check_unchecked_mul_u128() { + let num1: u128 = kani::any::(); + let num2: u128 = kani::any::(); + + unsafe { + num1.unchecked_mul(num2); + } + } + + // `unchecked_shr` proofs + // + // Target types: + // i{8,16,32,64,128} and u{8,16,32,64,128} -- 10 types in total + // + // Target Files : src/num/int_macros.rs , src/num/uint_macros.rs + // + // Target contracts: + // #[requires(rhs < <$ActualT>::BITS)] + // #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] + // + // Target function: + // pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self + + #[kani::proof_for_contract(i8::unchecked_shr)] + pub fn check_unchecked_shr_i8() { + let num: i8 = kani::any::(); + let shift: u32 = kani::any::() % i8::BITS; + + unsafe { + num.unchecked_shr(shift); + } + } + + #[kani::proof_for_contract(i16::unchecked_shr)] + pub fn check_unchecked_shr_i16() { + let num: i16 = kani::any::(); + let shift: u32 = kani::any::() % i16::BITS; + + unsafe { + num.unchecked_shr(shift); + } + } + + #[kani::proof_for_contract(i32::unchecked_shr)] + pub fn check_unchecked_shr_i32() { + let num: i32 = kani::any::(); + let shift: u32 = kani::any::() % i32::BITS; + + unsafe { + num.unchecked_shr(shift); + } + } + + #[kani::proof_for_contract(i64::unchecked_shr)] + pub fn check_unchecked_shr_i64() { + let num: i64 = kani::any::(); + let shift: u32 = kani::any::() % i64::BITS; + + unsafe { + num.unchecked_shr(shift); + } + } + + #[kani::proof_for_contract(i128::unchecked_shr)] + pub fn check_unchecked_shr_i128() { + let num: i128 = kani::any::(); + let shift: u32 = kani::any::() % i128::BITS; + + unsafe { + num.unchecked_shr(shift); + } + } + + #[kani::proof_for_contract(u8::unchecked_shr)] + pub fn check_unchecked_shr_u8() { + let num: u8 = kani::any::(); + let shift: u32 = kani::any::() % u8::BITS; + + unsafe { + num.unchecked_shr(shift); + } + } + + #[kani::proof_for_contract(u16::unchecked_shr)] + pub fn check_unchecked_shr_u16() { + let num: u16 = kani::any::(); + let shift: u32 = kani::any::() % u16::BITS; + + unsafe { + num.unchecked_shr(shift); + } + } + + #[kani::proof_for_contract(u32::unchecked_shr)] + pub fn check_unchecked_shr_u32() { + let num: u32 = kani::any::(); + let shift: u32 = kani::any::() % u32::BITS; + + unsafe { + num.unchecked_shr(shift); + } + } + + #[kani::proof_for_contract(u64::unchecked_shr)] + pub fn check_unchecked_shr_u64() { + let num: u64 = kani::any::(); + let shift: u32 = kani::any::() % u64::BITS; + + unsafe { + num.unchecked_shr(shift); + } + } + + #[kani::proof_for_contract(u128::unchecked_shr)] + pub fn check_unchecked_shr_u128() { + let num: u128 = kani::any::(); + let shift: u32 = kani::any::() % u128::BITS; + + unsafe { + num.unchecked_shr(shift); + } + } } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 48f0e2d4dc24b..4ca4e2bcb08b7 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -909,6 +909,8 @@ macro_rules! uint_impl { without modifying the original"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[requires(!self.overflowing_mul(rhs).1)] + #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -1614,6 +1616,8 @@ macro_rules! uint_impl { #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[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. + #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] // ensures result is within range after bit shift pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, From a7d3cc24514a38a538bd0c935401379354994313 Mon Sep 17 00:00:00 2001 From: MWDZ Date: Mon, 23 Sep 2024 20:12:20 -0700 Subject: [PATCH 18/67] feat: add contract and test case for unchecked_shl in type int and unit --- library/core/src/num/int_macros.rs | 2 + library/core/src/num/mod.rs | 113 ++++++++++++++++++++++++++++ library/core/src/num/uint_macros.rs | 2 + 3 files changed, 117 insertions(+) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index fa0c9c98dde74..486f9493b7148 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1299,6 +1299,8 @@ macro_rules! int_impl { #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[requires(rhs < Self::BITS)] + #[ensures(|ret| *ret >= Self::MIN && *ret <= Self::MAX)] pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 4dbce78394d9e..3d65b60b155f7 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1703,4 +1703,117 @@ mod verify { num1.unchecked_add(num2); } } + + // `unchecked_shl` proofs + // + // Target types: + // i{8,16,32,64,128} and u{8,16,32,64,128} -- 10 types in total + // + // Target contracts: + // #[requires(shift < Self::BITS)] + // #[ensures(|ret| *ret == self << shift)] + // + // Target function: + // pub const unsafe fn unchecked_shl(self, shift: u32) -> Self + // + // This function performs an unchecked bitwise left shift operation. + #[kani::proof_for_contract(i8::unchecked_shl)] + pub fn check_unchecked_shl_i8() { + let num1: i8 = kani::any::(); + let shift: u32 = kani::any::(); + + unsafe { + num1.unchecked_shl(shift); + } + } + + #[kani::proof_for_contract(i16::unchecked_shl)] + pub fn check_unchecked_shl_i16() { + let num1: i16 = kani::any::(); + let shift: u32 = kani::any::(); + + unsafe { + num1.unchecked_shl(shift); + } + } + + #[kani::proof_for_contract(i32::unchecked_shl)] + pub fn check_unchecked_shl_i32() { + let num1: i32 = kani::any::(); + let shift: u32 = kani::any::(); + + unsafe { + num1.unchecked_shl(shift); + } + } + + #[kani::proof_for_contract(i64::unchecked_shl)] + pub fn check_unchecked_shl_i64() { + let num1: i64 = kani::any::(); + let shift: u32 = kani::any::(); + + unsafe { + num1.unchecked_shl(shift); + } + } + + #[kani::proof_for_contract(i128::unchecked_shl)] + pub fn check_unchecked_shl_i128() { + let num1: i128 = kani::any::(); + let shift: u32 = kani::any::(); + + unsafe { + num1.unchecked_shl(shift); + } + } + + #[kani::proof_for_contract(u8::unchecked_shl)] + pub fn check_unchecked_shl_u8() { + let num1: u8 = kani::any::(); + let shift: u32 = kani::any::(); + + unsafe { + num1.unchecked_shl(shift); + } + } + + #[kani::proof_for_contract(u16::unchecked_shl)] + pub fn check_unchecked_shl_u16() { + let num1: u16 = kani::any::(); + let shift: u32 = kani::any::(); + + unsafe { + num1.unchecked_shl(shift); + } + } + + #[kani::proof_for_contract(u32::unchecked_shl)] + pub fn check_unchecked_shl_u32() { + let num1: u32 = kani::any::(); + let shift: u32 = kani::any::(); + + unsafe { + num1.unchecked_shl(shift); + } + } + + #[kani::proof_for_contract(u64::unchecked_shl)] + pub fn check_unchecked_shl_u64() { + let num1: u64 = kani::any::(); + let shift: u32 = kani::any::(); + + unsafe { + num1.unchecked_shl(shift); + } + } + + #[kani::proof_for_contract(u128::unchecked_shl)] + pub fn check_unchecked_shl_u128() { + let num1: u128 = kani::any::(); + let shift: u32 = kani::any::(); + + unsafe { + num1.unchecked_shl(shift); + } + } } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 48f0e2d4dc24b..617db39cbc220 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1488,6 +1488,8 @@ macro_rules! uint_impl { #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[requires(rhs < Self::BITS)] + #[ensures(|ret| *ret >= Self::MIN && *ret <= Self::MAX)] pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, From 271afaddbee6eef41ec4d304a477adc62d1a7991 Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Tue, 24 Sep 2024 00:54:07 -0700 Subject: [PATCH 19/67] added preconditions and postconditions --- library/core/src/num/int_macros.rs | 2 ++ library/core/src/num/uint_macros.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index fa0c9c98dde74..8b9004ef0ee51 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -665,6 +665,8 @@ macro_rules! int_impl { without modifying the original"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[requires(!self.overflowing_sub(rhs).1)] // Preconditions: No overflow should occur + #[ensures(|ret| *ret >= Self::MIN && *ret <= Self::MAX)] // Postconditions: Result must be within valid bounds pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 48f0e2d4dc24b..a69d393d5d389 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -752,6 +752,8 @@ macro_rules! uint_impl { without modifying the original"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[requires(!self.overflowing_sub(rhs).1)] // Preconditions: No overflow should occur + #[ensures(|ret| *ret >= Self::MIN && *ret <= Self::MAX)] // Postconditions: Result must be within valid bounds pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, From c7762c269195d4290731e83fce878aa29b9ca2f6 Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Tue, 24 Sep 2024 01:06:28 -0700 Subject: [PATCH 20/67] add proof functions for unchecked_sub --- library/core/src/num/mod.rs | 107 ++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 4dbce78394d9e..98df564f5c946 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1703,4 +1703,111 @@ mod verify { num1.unchecked_add(num2); } } + + + // `unchecked_sub` proofs + // + // Target types: + // i{8,16,32,64,128} and u{8,16,32,64,128} -- 10 types in total + // + // Target contracts: + //#[requires(!self.overflowing_sub(rhs).1)] // Preconditions: No overflow should occur + //#[ensures(|ret| *ret >= Self::MIN && *ret <= Self::MAX)] // Postconditions: Result must be within valid bounds + // + // Target function: + // pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self + + #[kani::proof_for_contract(i8::unchecked_sub)] + pub fn check_unchecked_sub_i8() { + let num1: i8 = kani::any::(); + let num2: i8 = kani::any::(); + unsafe { + num1.unchecked_sub(num2); + } + } + + #[kani::proof_for_contract(i16::unchecked_sub)] + pub fn check_unchecked_sub_i16() { + let num1: i16 = kani::any::(); + let num2: i16 = kani::any::(); + unsafe { + num1.unchecked_sub(num2); + } + } + + #[kani::proof_for_contract(i32::unchecked_sub)] + pub fn check_unchecked_sub_i32() { + let num1: i32 = kani::any::(); + let num2: i32 = kani::any::(); + unsafe { + num1.unchecked_sub(num2); + } + } + + #[kani::proof_for_contract(i64::unchecked_sub)] + pub fn check_unchecked_sub_i64() { + let num1: i64 = kani::any::(); + let num2: i64 = kani::any::(); + unsafe { + num1.unchecked_sub(num2); + } + } + + #[kani::proof_for_contract(i128::unchecked_sub)] + pub fn check_unchecked_sub_i128() { + let num1: i128 = kani::any::(); + let num2: i128 = kani::any::(); + unsafe { + num1.unchecked_sub(num2); + } + } + + #[kani::proof_for_contract(u8::unchecked_sub)] + pub fn check_unchecked_sub_u8() { + let num1: u8 = kani::any::(); + let num2: u8 = kani::any::(); + unsafe { + num1.unchecked_sub(num2); + } + } + + #[kani::proof_for_contract(u16::unchecked_sub)] + pub fn check_unchecked_sub_u16() { + let num1: u16 = kani::any::(); + let num2: u16 = kani::any::(); + unsafe { + num1.unchecked_sub(num2); + } + } + + #[kani::proof_for_contract(u32::unchecked_sub)] + pub fn check_unchecked_sub_u32() { + let num1: u32 = kani::any::(); + let num2: u32 = kani::any::(); + unsafe { + num1.unchecked_sub(num2); + } + } + + #[kani::proof_for_contract(u64::unchecked_sub)] + pub fn check_unchecked_sub_u64() { + let num1: u64 = kani::any::(); + let num2: u64 = kani::any::(); + unsafe { + num1.unchecked_sub(num2); + } + } + + #[kani::proof_for_contract(u128::unchecked_sub)] + pub fn check_unchecked_sub_u128() { + let num1: u128 = kani::any::(); + let num2: u128 = kani::any::(); + unsafe { + num1.unchecked_sub(num2); + } + } + + + + } From d99844df3a9f0bb24d973e250122eedd3542924e Mon Sep 17 00:00:00 2001 From: yew005 Date: Tue, 24 Sep 2024 14:16:46 -0700 Subject: [PATCH 21/67] Remove ensures contracts && Undo formatting on existing code --- library/core/src/num/int_macros.rs | 1 - library/core/src/num/mod.rs | 5 +---- library/core/src/num/uint_macros.rs | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index fa0c9c98dde74..fa3e4cef9bcb4 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -512,7 +512,6 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires(!self.overflowing_add(rhs).1)] - #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 4dbce78394d9e..526b8895e89cb 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1401,10 +1401,7 @@ const fn from_str_radix_panic_ct(_radix: u32) -> ! { #[track_caller] fn from_str_radix_panic_rt(radix: u32) -> ! { - panic!( - "from_str_radix_int: must lie in the range `[2, 36]` - found {}", - radix - ); + panic!("from_str_radix_int: must lie in the range `[2, 36]` - found {}", radix); } #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 48f0e2d4dc24b..b12d82bdc720c 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -559,7 +559,6 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires(!self.overflowing_add(rhs).1)] - #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, From fbcf49e631daa53e111bf750c1d3c916921a016a Mon Sep 17 00:00:00 2001 From: yew005 Date: Tue, 24 Sep 2024 14:28:59 -0700 Subject: [PATCH 22/67] Add {isize, usize}::unchecked_add harnesses --- library/core/src/num/mod.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 526b8895e89cb..159062099dbc0 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1593,11 +1593,10 @@ mod verify { // `unchecked_add` proofs // // Target types: - // i{8,16,32,64,128} and u{8,16,32,64,128} -- 10 types in total + // i{8,16,32,64,128, size} and u{8,16,32,64,128, size} -- 12 types in total // // Target contracts: // #[requires(!self.overflowing_add(rhs).1)] - // #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] // // Target function: // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self @@ -1651,6 +1650,16 @@ mod verify { } } + #[kani::proof_for_contract(isize::unchecked_add)] + pub fn check_unchecked_add_isize() { + let num1: isize = kani::any::(); + let num2: isize = kani::any::(); + + unsafe { + num1.unchecked_add(num2); + } + } + #[kani::proof_for_contract(u8::unchecked_add)] pub fn check_unchecked_add_u8() { let num1: u8 = kani::any::(); @@ -1700,4 +1709,14 @@ mod verify { num1.unchecked_add(num2); } } + + #[kani::proof_for_contract(usize::unchecked_add)] + pub fn check_unchecked_add_usize() { + let num1: usize = kani::any::(); + let num2: usize = kani::any::(); + + unsafe { + num1.unchecked_add(num2); + } + } } From 864afa684013af8608c43740099e75c655d83060 Mon Sep 17 00:00:00 2001 From: Rajath Kotyal <53811196+rajathkotyal@users.noreply.github.com> Date: Wed, 25 Sep 2024 03:04:12 -0700 Subject: [PATCH 23/67] Small fix typo --- library/core/src/num/int_macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 1f82f24c6bf27..07715ddaa3b70 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1425,7 +1425,7 @@ macro_rules! int_impl { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")] - #[inline(always)]0 + #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[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. #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] // ensures result is within range after bit shift From 54a03ef0c9c301d7d3aa06a82d75e305c3ee76bf Mon Sep 17 00:00:00 2001 From: yew005 Date: Fri, 27 Sep 2024 15:09:57 -0700 Subject: [PATCH 24/67] Add harness generation macros for unchecked methods && Update unchecked_add harnesses --- library/core/src/num/mod.rs | 169 +++++++++++------------------------- 1 file changed, 52 insertions(+), 117 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 159062099dbc0..8e31a0f434981 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1586,137 +1586,72 @@ from_str_radix_size_impl! { i32 isize, u32 usize } #[cfg(target_pointer_width = "64")] from_str_radix_size_impl! { i64 isize, u64 usize } +#[cfg(kani)] #[unstable(feature = "kani", issue = "none")] mod verify { use super::*; - // `unchecked_add` proofs - // - // Target types: - // i{8,16,32,64,128, size} and u{8,16,32,64,128, size} -- 12 types in total - // - // Target contracts: - // #[requires(!self.overflowing_add(rhs).1)] - // - // Target function: - // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self - #[kani::proof_for_contract(i8::unchecked_add)] - pub fn check_unchecked_add_i8() { - let num1: i8 = kani::any::(); - let num2: i8 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(i16::unchecked_add)] - pub fn check_unchecked_add_i16() { - let num1: i16 = kani::any::(); - let num2: i16 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(i32::unchecked_add)] - pub fn check_unchecked_add_i32() { - let num1: i32 = kani::any::(); - let num2: i32 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(i64::unchecked_add)] - pub fn check_unchecked_add_i64() { - let num1: i64 = kani::any::(); - let num2: i64 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(i128::unchecked_add)] - pub fn check_unchecked_add_i128() { - let num1: i128 = kani::any::(); - let num2: i128 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(isize::unchecked_add)] - pub fn check_unchecked_add_isize() { - let num1: isize = kani::any::(); - let num2: isize = kani::any::(); + macro_rules! generate_unchecked_math_harness { + ($type:ty, $method:ident, $harness_name:ident) => { + #[kani::proof_for_contract($type::$method)] + pub fn $harness_name() { + let num1: $type = kani::any::<$type>(); + let num2: $type = kani::any::<$type>(); - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(u8::unchecked_add)] - pub fn check_unchecked_add_u8() { - let num1: u8 = kani::any::(); - let num2: u8 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(u16::unchecked_add)] - pub fn check_unchecked_add_u16() { - let num1: u16 = kani::any::(); - let num2: u16 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(u32::unchecked_add)] - pub fn check_unchecked_add_u32() { - let num1: u32 = kani::any::(); - let num2: u32 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); + unsafe { + num1.$method(num2); + } + } } } - #[kani::proof_for_contract(u64::unchecked_add)] - pub fn check_unchecked_add_u64() { - let num1: u64 = kani::any::(); - let num2: u64 = kani::any::(); + macro_rules! generate_unchecked_shift_harness { + ($type:ty, $method:ident, $harness_name:ident) => { + #[kani::proof_for_contract($type::$method)] + pub fn $harness_name() { + let num1: $type = kani::any::<$type>(); + let num2: u32 = kani::any::(); - unsafe { - num1.unchecked_add(num2); + unsafe { + num1.$method(num2); + } + } } } - #[kani::proof_for_contract(u128::unchecked_add)] - pub fn check_unchecked_add_u128() { - let num1: u128 = kani::any::(); - let num2: u128 = kani::any::(); + macro_rules! generate_unchecked_neg_harness { + ($type:ty, $method:ident, $harness_name:ident) => { + #[kani::proof_for_contract($type::$method)] + pub fn $harness_name() { + let num1: $type = kani::any::<$type>(); - unsafe { - num1.unchecked_add(num2); + unsafe { + num1.$method(); + } + } } } - #[kani::proof_for_contract(usize::unchecked_add)] - pub fn check_unchecked_add_usize() { - let num1: usize = kani::any::(); - let num2: usize = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } + // `unchecked_add` proofs + // + // Target types: + // i{8,16,32,64,128, size} and u{8,16,32,64,128, size} -- 12 types in total + // + // Target contracts: + // #[requires(!self.overflowing_add(rhs).1)] + // + // Target function: + // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self + generate_unchecked_math_harness!(i8, unchecked_add, checked_unchecked_add_i8); + generate_unchecked_math_harness!(i16, unchecked_add, checked_unchecked_add_i16); + generate_unchecked_math_harness!(i32, unchecked_add, checked_unchecked_add_i32); + generate_unchecked_math_harness!(i64, unchecked_add, checked_unchecked_add_i64); + generate_unchecked_math_harness!(i128, unchecked_add, checked_unchecked_add_i128); + generate_unchecked_math_harness!(isize, unchecked_add, checked_unchecked_add_isize); + generate_unchecked_math_harness!(u8, unchecked_add, checked_unchecked_add_u8); + generate_unchecked_math_harness!(u16, unchecked_add, checked_unchecked_add_u16); + generate_unchecked_math_harness!(u32, unchecked_add, checked_unchecked_add_u32); + generate_unchecked_math_harness!(u64, unchecked_add, checked_unchecked_add_u64); + generate_unchecked_math_harness!(u128, unchecked_add, checked_unchecked_add_u128); + generate_unchecked_math_harness!(usize, unchecked_add, checked_unchecked_add_usize); } From ae678de8bda63673540fd1c5a2bbb5fb776bff94 Mon Sep 17 00:00:00 2001 From: rajathkotyal Date: Sun, 29 Sep 2024 04:32:21 -0700 Subject: [PATCH 25/67] added limits to multiplication proofs, reduced duplicacy in code by using macros to generate proof harnesses --- library/core/src/num/int_macros.rs | 3 - library/core/src/num/mod.rs | 412 +++++++--------------------- library/core/src/num/uint_macros.rs | 3 - 3 files changed, 103 insertions(+), 315 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 07715ddaa3b70..a46e653de2bd0 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -512,7 +512,6 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires(!self.overflowing_add(rhs).1)] - #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -818,7 +817,6 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires(!self.overflowing_mul(rhs).1)] - #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -1428,7 +1426,6 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[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. - #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] // ensures result is within range after bit shift pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 661c3f11cd3b6..186d05eda5a98 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1401,10 +1401,7 @@ const fn from_str_radix_panic_ct(_radix: u32) -> ! { #[track_caller] fn from_str_radix_panic_rt(radix: u32) -> ! { - panic!( - "from_str_radix_int: must lie in the range `[2, 36]` - found {}", - radix - ); + panic!("from_str_radix_int: must lie in the range `[2, 36]` - found {}", radix); } #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] @@ -1589,346 +1586,143 @@ from_str_radix_size_impl! { i32 isize, u32 usize } #[cfg(target_pointer_width = "64")] from_str_radix_size_impl! { i64 isize, u64 usize } +#[cfg(kani)] #[unstable(feature = "kani", issue = "none")] mod verify { use super::*; - // `unchecked_add` proofs - // - // Target types: - // i{8,16,32,64,128} and u{8,16,32,64,128} -- 10 types in total - // - // Target contracts: - // #[requires(!self.overflowing_add(rhs).1)] - // #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] - // - // Target function: - // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self - #[kani::proof_for_contract(i8::unchecked_add)] - pub fn check_unchecked_add_i8() { - let num1: i8 = kani::any::(); - let num2: i8 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(i16::unchecked_add)] - pub fn check_unchecked_add_i16() { - let num1: i16 = kani::any::(); - let num2: i16 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(i32::unchecked_add)] - pub fn check_unchecked_add_i32() { - let num1: i32 = kani::any::(); - let num2: i32 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(i64::unchecked_add)] - pub fn check_unchecked_add_i64() { - let num1: i64 = kani::any::(); - let num2: i64 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(i128::unchecked_add)] - pub fn check_unchecked_add_i128() { - let num1: i128 = kani::any::(); - let num2: i128 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(u8::unchecked_add)] - pub fn check_unchecked_add_u8() { - let num1: u8 = kani::any::(); - let num2: u8 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(u16::unchecked_add)] - pub fn check_unchecked_add_u16() { - let num1: u16 = kani::any::(); - let num2: u16 = kani::any::(); + macro_rules! generate_unchecked_math_harness { + ($type:ty, $method:ident, $harness_name:ident) => { + #[kani::proof_for_contract($type::$method)] + pub fn $harness_name() { + let num1: $type = kani::any::<$type>(); + let num2: $type = kani::any::<$type>(); - unsafe { - num1.unchecked_add(num2); + unsafe { + num1.$method(num2); + } + } } } - #[kani::proof_for_contract(u32::unchecked_add)] - pub fn check_unchecked_add_u32() { - let num1: u32 = kani::any::(); - let num2: u32 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); + macro_rules! generate_unchecked_mul_harness { + ($type:ty, $method:ident, $harness_name:ident, $min:expr, $max:expr) => { + #[kani::proof_for_contract($type::$method)] + pub fn $harness_name() { + let num1: $type = kani::any(); + let num2: $type = kani::any(); + + // Limit the values of num1 and num2 to the specified range for multiplication + kani::assume(num1 >= $min && num1 <= $max); + kani::assume(num2 >= $min && num2 <= $max); + + unsafe { + num1.$method(num2); + } + } } } - - #[kani::proof_for_contract(u64::unchecked_add)] - pub fn check_unchecked_add_u64() { - let num1: u64 = kani::any::(); - let num2: u64 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); + + + macro_rules! generate_unchecked_shift_harness { + ($type:ty, $method:ident, $harness_name:ident) => { + #[kani::proof_for_contract($type::$method)] + pub fn $harness_name() { + let num1: $type = kani::any::<$type>(); + let num2: u32 = kani::any::(); + + unsafe { + num1.$method(num2); + } + } } } + - #[kani::proof_for_contract(u128::unchecked_add)] - pub fn check_unchecked_add_u128() { - let num1: u128 = kani::any::(); - let num2: u128 = kani::any::(); + macro_rules! generate_unchecked_neg_harness { + ($type:ty, $method:ident, $harness_name:ident) => { + #[kani::proof_for_contract($type::$method)] + pub fn $harness_name() { + let num1: $type = kani::any::<$type>(); - unsafe { - num1.unchecked_add(num2); + unsafe { + num1.$method(); + } + } } } - // `unchecked_mul` proofs + // `unchecked_add` proofs // // Target types: - // i{8,16,32,64,128} and u{8,16,32,64,128} -- 10 types in total + // i{8,16,32,64,128, size} and u{8,16,32,64,128, size} -- 12 types in total // - // Target Files : src/num/int_macros.rs , src/num/uint_macros.rs + // Target contracts: + // #[requires(!self.overflowing_add(rhs).1)] // - // Target contracts : + // Target function: + // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self + generate_unchecked_math_harness!(i8, unchecked_add, checked_unchecked_add_i8); + generate_unchecked_math_harness!(i16, unchecked_add, checked_unchecked_add_i16); + generate_unchecked_math_harness!(i32, unchecked_add, checked_unchecked_add_i32); + generate_unchecked_math_harness!(i64, unchecked_add, checked_unchecked_add_i64); + generate_unchecked_math_harness!(i128, unchecked_add, checked_unchecked_add_i128); + generate_unchecked_math_harness!(isize, unchecked_add, checked_unchecked_add_isize); + generate_unchecked_math_harness!(u8, unchecked_add, checked_unchecked_add_u8); + generate_unchecked_math_harness!(u16, unchecked_add, checked_unchecked_add_u16); + generate_unchecked_math_harness!(u32, unchecked_add, checked_unchecked_add_u32); + generate_unchecked_math_harness!(u64, unchecked_add, checked_unchecked_add_u64); + generate_unchecked_math_harness!(u128, unchecked_add, checked_unchecked_add_u128); + generate_unchecked_math_harness!(usize, unchecked_add, checked_unchecked_add_usize); + + // unchecked_mul proofs + // + // Target types: + // i{8,16,32,64,128, size} and u{8,16,32,64,128, size} -- 12 types in total + // + // Target contracts: // #[requires(!self.overflowing_mul(rhs).1)] - // #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] // - // Target function definition: + // Target function: // pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self + // exponential state spaces for 32,64 and 128, hence provided limited range for verification. + generate_unchecked_math_harness!(i8, unchecked_mul, checked_unchecked_mul_i8); + generate_unchecked_math_harness!(i16, unchecked_mul, checked_unchecked_mul_i16); + generate_unchecked_mul_harness!(i32, unchecked_mul, checked_unchecked_mul_i32, -10_000i32, 10_000i32); + generate_unchecked_mul_harness!(i64, unchecked_mul, checked_unchecked_mul_i64, -1_000i64, 1_000i64); + generate_unchecked_mul_harness!(i128, unchecked_mul, checked_unchecked_mul_i128, -1_000_000_000_000_000i128, 1_000_000_000_000_000i128); + generate_unchecked_mul_harness!(isize, unchecked_mul, checked_unchecked_mul_isize, -100_000isize, 100_000isize); - #[kani::proof_for_contract(i8::unchecked_mul)] - pub fn check_unchecked_mul_i8() { - let num1: i8 = kani::any::(); - let num2: i8 = kani::any::(); - - unsafe { - num1.unchecked_mul(num2); - } - } - - #[kani::proof_for_contract(i16::unchecked_mul)] - pub fn check_unchecked_mul_i16() { - let num1: i16 = kani::any::(); - let num2: i16 = kani::any::(); - - unsafe { - num1.unchecked_mul(num2); - } - } - - #[kani::proof_for_contract(i32::unchecked_mul)] - pub fn check_unchecked_mul_i32() { - let num1: i32 = kani::any::(); - let num2: i32 = kani::any::(); - - unsafe { - num1.unchecked_mul(num2); - } - } - - #[kani::proof_for_contract(i64::unchecked_mul)] - pub fn check_unchecked_mul_i64() { - let num1: i64 = kani::any::(); - let num2: i64 = kani::any::(); - unsafe { - num1.unchecked_mul(num2); - } - } + generate_unchecked_math_harness!(u8, unchecked_mul, checked_unchecked_mul_u8); + generate_unchecked_math_harness!(u16, unchecked_mul, checked_unchecked_mul_u16); + generate_unchecked_mul_harness!(u32, unchecked_mul, checked_unchecked_mul_u32, 0u32, 20_000u32); + generate_unchecked_mul_harness!(u64, unchecked_mul, checked_unchecked_mul_u64, 0u64, 2_000u64); + generate_unchecked_mul_harness!(u128, unchecked_mul, checked_unchecked_mul_u128, 0u128, 1_000_000_000_000_000u128); + generate_unchecked_mul_harness!(usize, unchecked_mul, checked_unchecked_mul_usize, 0usize, 100_000usize); - #[kani::proof_for_contract(i128::unchecked_mul)] - pub fn check_unchecked_mul_i128() { - let num1: i128 = kani::any::(); - let num2: i128 = kani::any::(); - unsafe { - num1.unchecked_mul(num2); - } - } - - #[kani::proof_for_contract(u8::unchecked_mul)] - pub fn check_unchecked_mul_u8() { - let num1: u8 = kani::any::(); - let num2: u8 = kani::any::(); - - unsafe { - num1.unchecked_mul(num2); - } - } - - #[kani::proof_for_contract(u16::unchecked_mul)] - pub fn check_unchecked_mul_u16() { - let num1: u16 = kani::any::(); - let num2: u16 = kani::any::(); - - unsafe { - num1.unchecked_mul(num2); - } - } - - #[kani::proof_for_contract(u32::unchecked_mul)] - pub fn check_unchecked_mul_u32() { - let num1: u32 = kani::any::(); - let num2: u32 = kani::any::(); - - unsafe { - num1.unchecked_mul(num2); - } - } - - #[kani::proof_for_contract(u64::unchecked_mul)] - pub fn check_unchecked_mul_u64() { - let num1: u64 = kani::any::(); - let num2: u64 = kani::any::(); - - unsafe { - num1.unchecked_mul(num2); - } - } - - #[kani::proof_for_contract(u128::unchecked_mul)] - pub fn check_unchecked_mul_u128() { - let num1: u128 = kani::any::(); - let num2: u128 = kani::any::(); - - unsafe { - num1.unchecked_mul(num2); - } - } - - // `unchecked_shr` proofs + // unchecked_shr proofs // // Target types: - // i{8,16,32,64,128} and u{8,16,32,64,128} -- 10 types in total - // - // Target Files : src/num/int_macros.rs , src/num/uint_macros.rs + // i{8,16,32,64,128,size} and u{8,16,32,64,128,size} -- 12 types in total // // Target contracts: // #[requires(rhs < <$ActualT>::BITS)] - // #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] // // Target function: // pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self + generate_unchecked_shift_harness!(i8, unchecked_shr, checked_unchecked_shr_i8); + generate_unchecked_shift_harness!(i16, unchecked_shr, checked_unchecked_shr_i16); + generate_unchecked_shift_harness!(i32, unchecked_shr, checked_unchecked_shr_i32); + generate_unchecked_shift_harness!(i64, unchecked_shr, checked_unchecked_shr_i64); + generate_unchecked_shift_harness!(i128, unchecked_shr, checked_unchecked_shr_i128); + generate_unchecked_shift_harness!(isize, unchecked_shr, checked_unchecked_shr_isize); + generate_unchecked_shift_harness!(u8, unchecked_shr, checked_unchecked_shr_u8); + generate_unchecked_shift_harness!(u16, unchecked_shr, checked_unchecked_shr_u16); + generate_unchecked_shift_harness!(u32, unchecked_shr, checked_unchecked_shr_u32); + generate_unchecked_shift_harness!(u64, unchecked_shr, checked_unchecked_shr_u64); + generate_unchecked_shift_harness!(u128, unchecked_shr, checked_unchecked_shr_u128); + generate_unchecked_shift_harness!(usize, unchecked_shr, checked_unchecked_shr_usize); - #[kani::proof_for_contract(i8::unchecked_shr)] - pub fn check_unchecked_shr_i8() { - let num: i8 = kani::any::(); - let shift: u32 = kani::any::() % i8::BITS; - - unsafe { - num.unchecked_shr(shift); - } - } - - #[kani::proof_for_contract(i16::unchecked_shr)] - pub fn check_unchecked_shr_i16() { - let num: i16 = kani::any::(); - let shift: u32 = kani::any::() % i16::BITS; - - unsafe { - num.unchecked_shr(shift); - } - } - - #[kani::proof_for_contract(i32::unchecked_shr)] - pub fn check_unchecked_shr_i32() { - let num: i32 = kani::any::(); - let shift: u32 = kani::any::() % i32::BITS; - - unsafe { - num.unchecked_shr(shift); - } - } - - #[kani::proof_for_contract(i64::unchecked_shr)] - pub fn check_unchecked_shr_i64() { - let num: i64 = kani::any::(); - let shift: u32 = kani::any::() % i64::BITS; - - unsafe { - num.unchecked_shr(shift); - } - } - - #[kani::proof_for_contract(i128::unchecked_shr)] - pub fn check_unchecked_shr_i128() { - let num: i128 = kani::any::(); - let shift: u32 = kani::any::() % i128::BITS; - - unsafe { - num.unchecked_shr(shift); - } - } - - #[kani::proof_for_contract(u8::unchecked_shr)] - pub fn check_unchecked_shr_u8() { - let num: u8 = kani::any::(); - let shift: u32 = kani::any::() % u8::BITS; - - unsafe { - num.unchecked_shr(shift); - } - } - - #[kani::proof_for_contract(u16::unchecked_shr)] - pub fn check_unchecked_shr_u16() { - let num: u16 = kani::any::(); - let shift: u32 = kani::any::() % u16::BITS; - - unsafe { - num.unchecked_shr(shift); - } - } - - #[kani::proof_for_contract(u32::unchecked_shr)] - pub fn check_unchecked_shr_u32() { - let num: u32 = kani::any::(); - let shift: u32 = kani::any::() % u32::BITS; - - unsafe { - num.unchecked_shr(shift); - } - } - - #[kani::proof_for_contract(u64::unchecked_shr)] - pub fn check_unchecked_shr_u64() { - let num: u64 = kani::any::(); - let shift: u32 = kani::any::() % u64::BITS; - - unsafe { - num.unchecked_shr(shift); - } - } - - #[kani::proof_for_contract(u128::unchecked_shr)] - pub fn check_unchecked_shr_u128() { - let num: u128 = kani::any::(); - let shift: u32 = kani::any::() % u128::BITS; - - unsafe { - num.unchecked_shr(shift); - } - } } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 4ca4e2bcb08b7..8706ed8bd6d5f 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -559,7 +559,6 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires(!self.overflowing_add(rhs).1)] - #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -910,7 +909,6 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires(!self.overflowing_mul(rhs).1)] - #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -1617,7 +1615,6 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[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. - #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] // ensures result is within range after bit shift pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, From 8ee56826478518f03a622803455c52af2f85f6ba Mon Sep 17 00:00:00 2001 From: yew005 Date: Sun, 29 Sep 2024 19:47:07 -0700 Subject: [PATCH 26/67] Remove unused import safety::ensures --- library/core/src/num/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 8e31a0f434981..51bc33b7b47a4 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -5,7 +5,7 @@ use crate::str::FromStr; use crate::ub_checks::assert_unsafe_precondition; use crate::{ascii, intrinsics, mem}; -use safety::{ensures, requires}; +use safety::requires; #[cfg(kani)] use crate::kani; From 02d706a2f818cdacee696763b4185458a2fc90e5 Mon Sep 17 00:00:00 2001 From: Rajath Kotyal <53811196+rajathkotyal@users.noreply.github.com> Date: Sun, 29 Sep 2024 21:15:21 -0700 Subject: [PATCH 27/67] 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 --- library/core/src/num/int_macros.rs | 2 + library/core/src/num/mod.rs | 72 ++++++++++++++++++++++++++++- library/core/src/num/uint_macros.rs | 2 + 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index fa3e4cef9bcb4..a46e653de2bd0 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -816,6 +816,7 @@ macro_rules! int_impl { without modifying the original"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[requires(!self.overflowing_mul(rhs).1)] pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -1424,6 +1425,7 @@ macro_rules! int_impl { #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[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. pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 51bc33b7b47a4..54e90d1bff0ff 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1605,19 +1605,39 @@ mod verify { } } + macro_rules! generate_unchecked_mul_harness { + ($type:ty, $method:ident, $harness_name:ident, $min:expr, $max:expr) => { + #[kani::proof_for_contract($type::$method)] + pub fn $harness_name() { + let num1: $type = kani::any(); + let num2: $type = kani::any(); + + // Limit the values of num1 and num2 to the specified range for multiplication + kani::assume(num1 >= $min && num1 <= $max); + kani::assume(num2 >= $min && num2 <= $max); + + unsafe { + num1.$method(num2); + } + } + } + } + + macro_rules! generate_unchecked_shift_harness { ($type:ty, $method:ident, $harness_name:ident) => { #[kani::proof_for_contract($type::$method)] pub fn $harness_name() { let num1: $type = kani::any::<$type>(); let num2: u32 = kani::any::(); - + unsafe { num1.$method(num2); } } } } + macro_rules! generate_unchecked_neg_harness { ($type:ty, $method:ident, $harness_name:ident) => { @@ -1654,4 +1674,54 @@ mod verify { generate_unchecked_math_harness!(u64, unchecked_add, checked_unchecked_add_u64); generate_unchecked_math_harness!(u128, unchecked_add, checked_unchecked_add_u128); generate_unchecked_math_harness!(usize, unchecked_add, checked_unchecked_add_usize); + + // unchecked_mul proofs + // + // Target types: + // i{8,16,32,64,128, size} and u{8,16,32,64,128, size} -- 12 types in total + // + // Target contracts: + // #[requires(!self.overflowing_mul(rhs).1)] + // + // Target function: + // pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self + // exponential state spaces for 32,64 and 128, hence provided limited range for verification. + generate_unchecked_math_harness!(i8, unchecked_mul, checked_unchecked_mul_i8); + generate_unchecked_math_harness!(i16, unchecked_mul, checked_unchecked_mul_i16); + generate_unchecked_mul_harness!(i32, unchecked_mul, checked_unchecked_mul_i32, -10_000i32, 10_000i32); + generate_unchecked_mul_harness!(i64, unchecked_mul, checked_unchecked_mul_i64, -1_000i64, 1_000i64); + generate_unchecked_mul_harness!(i128, unchecked_mul, checked_unchecked_mul_i128, -1_000_000_000_000_000i128, 1_000_000_000_000_000i128); + generate_unchecked_mul_harness!(isize, unchecked_mul, checked_unchecked_mul_isize, -100_000isize, 100_000isize); + generate_unchecked_math_harness!(u8, unchecked_mul, checked_unchecked_mul_u8); + generate_unchecked_math_harness!(u16, unchecked_mul, checked_unchecked_mul_u16); + generate_unchecked_mul_harness!(u32, unchecked_mul, checked_unchecked_mul_u32, 0u32, 20_000u32); + generate_unchecked_mul_harness!(u64, unchecked_mul, checked_unchecked_mul_u64, 0u64, 2_000u64); + generate_unchecked_mul_harness!(u128, unchecked_mul, checked_unchecked_mul_u128, 0u128, 1_000_000_000_000_000u128); + generate_unchecked_mul_harness!(usize, unchecked_mul, checked_unchecked_mul_usize, 0usize, 100_000usize); + + + // unchecked_shr proofs + // + // Target types: + // i{8,16,32,64,128,size} and u{8,16,32,64,128,size} -- 12 types in total + // + // Target contracts: + // #[requires(rhs < <$ActualT>::BITS)] + // + // Target function: + // pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self + generate_unchecked_shift_harness!(i8, unchecked_shr, checked_unchecked_shr_i8); + generate_unchecked_shift_harness!(i16, unchecked_shr, checked_unchecked_shr_i16); + generate_unchecked_shift_harness!(i32, unchecked_shr, checked_unchecked_shr_i32); + generate_unchecked_shift_harness!(i64, unchecked_shr, checked_unchecked_shr_i64); + generate_unchecked_shift_harness!(i128, unchecked_shr, checked_unchecked_shr_i128); + generate_unchecked_shift_harness!(isize, unchecked_shr, checked_unchecked_shr_isize); + generate_unchecked_shift_harness!(u8, unchecked_shr, checked_unchecked_shr_u8); + generate_unchecked_shift_harness!(u16, unchecked_shr, checked_unchecked_shr_u16); + generate_unchecked_shift_harness!(u32, unchecked_shr, checked_unchecked_shr_u32); + generate_unchecked_shift_harness!(u64, unchecked_shr, checked_unchecked_shr_u64); + generate_unchecked_shift_harness!(u128, unchecked_shr, checked_unchecked_shr_u128); + generate_unchecked_shift_harness!(usize, unchecked_shr, checked_unchecked_shr_usize); + } + } } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index b12d82bdc720c..8706ed8bd6d5f 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -908,6 +908,7 @@ macro_rules! uint_impl { without modifying the original"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[requires(!self.overflowing_mul(rhs).1)] pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -1613,6 +1614,7 @@ macro_rules! uint_impl { #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[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. pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, From dce9e831fca8c3109a565ef1d8423ded11eac118 Mon Sep 17 00:00:00 2001 From: yew005 Date: Sun, 29 Sep 2024 21:21:41 -0700 Subject: [PATCH 28/67] Add comments. Fix spacing --- library/core/src/num/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 54e90d1bff0ff..3a83e75b74288 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1591,6 +1591,9 @@ from_str_radix_size_impl! { i64 isize, u64 usize } mod verify { use super::*; + // Verify `unchecked_{add, sub, mul}` + // However, `unchecked_mul` harnesses have bad performance, so + // recommend to use generate_unchecked_mul_harness! to set input limits macro_rules! generate_unchecked_math_harness { ($type:ty, $method:ident, $harness_name:ident) => { #[kani::proof_for_contract($type::$method)] @@ -1605,14 +1608,15 @@ mod verify { } } + // Improve unchecked_mul performance for {32, 64, 128}-bit integer types + // by adding upper and lower limits for inputs macro_rules! generate_unchecked_mul_harness { ($type:ty, $method:ident, $harness_name:ident, $min:expr, $max:expr) => { #[kani::proof_for_contract($type::$method)] pub fn $harness_name() { - let num1: $type = kani::any(); - let num2: $type = kani::any(); - - // Limit the values of num1 and num2 to the specified range for multiplication + let num1: $type = kani::any::<$type>(); + let num2: $type = kani::any::<$type>(); + kani::assume(num1 >= $min && num1 <= $max); kani::assume(num2 >= $min && num2 <= $max); @@ -1622,8 +1626,8 @@ mod verify { } } } - + // Verify `unchecked_{shl, shr}` macro_rules! generate_unchecked_shift_harness { ($type:ty, $method:ident, $harness_name:ident) => { #[kani::proof_for_contract($type::$method)] @@ -1637,7 +1641,6 @@ mod verify { } } } - macro_rules! generate_unchecked_neg_harness { ($type:ty, $method:ident, $harness_name:ident) => { @@ -1699,7 +1702,6 @@ mod verify { generate_unchecked_mul_harness!(u128, unchecked_mul, checked_unchecked_mul_u128, 0u128, 1_000_000_000_000_000u128); generate_unchecked_mul_harness!(usize, unchecked_mul, checked_unchecked_mul_usize, 0usize, 100_000usize); - // unchecked_shr proofs // // Target types: @@ -1722,6 +1724,4 @@ mod verify { generate_unchecked_shift_harness!(u64, unchecked_shr, checked_unchecked_shr_u64); generate_unchecked_shift_harness!(u128, unchecked_shr, checked_unchecked_shr_u128); generate_unchecked_shift_harness!(usize, unchecked_shr, checked_unchecked_shr_usize); - } - } } From 3880fc7b0f50b719b934eedc5497e7d59e1687e3 Mon Sep 17 00:00:00 2001 From: rajathmCMU Date: Wed, 2 Oct 2024 12:58:43 -0700 Subject: [PATCH 29/67] Revert "Add comments. Fix spacing" This reverts commit dce9e831fca8c3109a565ef1d8423ded11eac118. --- library/core/src/num/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 3a83e75b74288..54e90d1bff0ff 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1591,9 +1591,6 @@ from_str_radix_size_impl! { i64 isize, u64 usize } mod verify { use super::*; - // Verify `unchecked_{add, sub, mul}` - // However, `unchecked_mul` harnesses have bad performance, so - // recommend to use generate_unchecked_mul_harness! to set input limits macro_rules! generate_unchecked_math_harness { ($type:ty, $method:ident, $harness_name:ident) => { #[kani::proof_for_contract($type::$method)] @@ -1608,15 +1605,14 @@ mod verify { } } - // Improve unchecked_mul performance for {32, 64, 128}-bit integer types - // by adding upper and lower limits for inputs macro_rules! generate_unchecked_mul_harness { ($type:ty, $method:ident, $harness_name:ident, $min:expr, $max:expr) => { #[kani::proof_for_contract($type::$method)] pub fn $harness_name() { - let num1: $type = kani::any::<$type>(); - let num2: $type = kani::any::<$type>(); - + let num1: $type = kani::any(); + let num2: $type = kani::any(); + + // Limit the values of num1 and num2 to the specified range for multiplication kani::assume(num1 >= $min && num1 <= $max); kani::assume(num2 >= $min && num2 <= $max); @@ -1626,8 +1622,8 @@ mod verify { } } } + - // Verify `unchecked_{shl, shr}` macro_rules! generate_unchecked_shift_harness { ($type:ty, $method:ident, $harness_name:ident) => { #[kani::proof_for_contract($type::$method)] @@ -1641,6 +1637,7 @@ mod verify { } } } + macro_rules! generate_unchecked_neg_harness { ($type:ty, $method:ident, $harness_name:ident) => { @@ -1702,6 +1699,7 @@ mod verify { generate_unchecked_mul_harness!(u128, unchecked_mul, checked_unchecked_mul_u128, 0u128, 1_000_000_000_000_000u128); generate_unchecked_mul_harness!(usize, unchecked_mul, checked_unchecked_mul_usize, 0usize, 100_000usize); + // unchecked_shr proofs // // Target types: @@ -1724,4 +1722,6 @@ mod verify { generate_unchecked_shift_harness!(u64, unchecked_shr, checked_unchecked_shr_u64); generate_unchecked_shift_harness!(u128, unchecked_shr, checked_unchecked_shr_u128); generate_unchecked_shift_harness!(usize, unchecked_shr, checked_unchecked_shr_usize); + } + } } From 781cb87ed6976b255bc7fffa5227d2cf815ede6a Mon Sep 17 00:00:00 2001 From: rajathmCMU Date: Wed, 2 Oct 2024 12:58:47 -0700 Subject: [PATCH 30/67] Revert "unchecked_mul and unchecked_shr proofs (#7)" This reverts commit 02d706a2f818cdacee696763b4185458a2fc90e5. --- library/core/src/num/int_macros.rs | 2 - library/core/src/num/mod.rs | 72 +---------------------------- library/core/src/num/uint_macros.rs | 2 - 3 files changed, 1 insertion(+), 75 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index a46e653de2bd0..fa3e4cef9bcb4 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -816,7 +816,6 @@ macro_rules! int_impl { without modifying the original"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[requires(!self.overflowing_mul(rhs).1)] pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -1425,7 +1424,6 @@ macro_rules! int_impl { #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[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. pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 54e90d1bff0ff..51bc33b7b47a4 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1605,39 +1605,19 @@ mod verify { } } - macro_rules! generate_unchecked_mul_harness { - ($type:ty, $method:ident, $harness_name:ident, $min:expr, $max:expr) => { - #[kani::proof_for_contract($type::$method)] - pub fn $harness_name() { - let num1: $type = kani::any(); - let num2: $type = kani::any(); - - // Limit the values of num1 and num2 to the specified range for multiplication - kani::assume(num1 >= $min && num1 <= $max); - kani::assume(num2 >= $min && num2 <= $max); - - unsafe { - num1.$method(num2); - } - } - } - } - - macro_rules! generate_unchecked_shift_harness { ($type:ty, $method:ident, $harness_name:ident) => { #[kani::proof_for_contract($type::$method)] pub fn $harness_name() { let num1: $type = kani::any::<$type>(); let num2: u32 = kani::any::(); - + unsafe { num1.$method(num2); } } } } - macro_rules! generate_unchecked_neg_harness { ($type:ty, $method:ident, $harness_name:ident) => { @@ -1674,54 +1654,4 @@ mod verify { generate_unchecked_math_harness!(u64, unchecked_add, checked_unchecked_add_u64); generate_unchecked_math_harness!(u128, unchecked_add, checked_unchecked_add_u128); generate_unchecked_math_harness!(usize, unchecked_add, checked_unchecked_add_usize); - - // unchecked_mul proofs - // - // Target types: - // i{8,16,32,64,128, size} and u{8,16,32,64,128, size} -- 12 types in total - // - // Target contracts: - // #[requires(!self.overflowing_mul(rhs).1)] - // - // Target function: - // pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self - // exponential state spaces for 32,64 and 128, hence provided limited range for verification. - generate_unchecked_math_harness!(i8, unchecked_mul, checked_unchecked_mul_i8); - generate_unchecked_math_harness!(i16, unchecked_mul, checked_unchecked_mul_i16); - generate_unchecked_mul_harness!(i32, unchecked_mul, checked_unchecked_mul_i32, -10_000i32, 10_000i32); - generate_unchecked_mul_harness!(i64, unchecked_mul, checked_unchecked_mul_i64, -1_000i64, 1_000i64); - generate_unchecked_mul_harness!(i128, unchecked_mul, checked_unchecked_mul_i128, -1_000_000_000_000_000i128, 1_000_000_000_000_000i128); - generate_unchecked_mul_harness!(isize, unchecked_mul, checked_unchecked_mul_isize, -100_000isize, 100_000isize); - generate_unchecked_math_harness!(u8, unchecked_mul, checked_unchecked_mul_u8); - generate_unchecked_math_harness!(u16, unchecked_mul, checked_unchecked_mul_u16); - generate_unchecked_mul_harness!(u32, unchecked_mul, checked_unchecked_mul_u32, 0u32, 20_000u32); - generate_unchecked_mul_harness!(u64, unchecked_mul, checked_unchecked_mul_u64, 0u64, 2_000u64); - generate_unchecked_mul_harness!(u128, unchecked_mul, checked_unchecked_mul_u128, 0u128, 1_000_000_000_000_000u128); - generate_unchecked_mul_harness!(usize, unchecked_mul, checked_unchecked_mul_usize, 0usize, 100_000usize); - - - // unchecked_shr proofs - // - // Target types: - // i{8,16,32,64,128,size} and u{8,16,32,64,128,size} -- 12 types in total - // - // Target contracts: - // #[requires(rhs < <$ActualT>::BITS)] - // - // Target function: - // pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self - generate_unchecked_shift_harness!(i8, unchecked_shr, checked_unchecked_shr_i8); - generate_unchecked_shift_harness!(i16, unchecked_shr, checked_unchecked_shr_i16); - generate_unchecked_shift_harness!(i32, unchecked_shr, checked_unchecked_shr_i32); - generate_unchecked_shift_harness!(i64, unchecked_shr, checked_unchecked_shr_i64); - generate_unchecked_shift_harness!(i128, unchecked_shr, checked_unchecked_shr_i128); - generate_unchecked_shift_harness!(isize, unchecked_shr, checked_unchecked_shr_isize); - generate_unchecked_shift_harness!(u8, unchecked_shr, checked_unchecked_shr_u8); - generate_unchecked_shift_harness!(u16, unchecked_shr, checked_unchecked_shr_u16); - generate_unchecked_shift_harness!(u32, unchecked_shr, checked_unchecked_shr_u32); - generate_unchecked_shift_harness!(u64, unchecked_shr, checked_unchecked_shr_u64); - generate_unchecked_shift_harness!(u128, unchecked_shr, checked_unchecked_shr_u128); - generate_unchecked_shift_harness!(usize, unchecked_shr, checked_unchecked_shr_usize); - } - } } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 8706ed8bd6d5f..b12d82bdc720c 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -908,7 +908,6 @@ macro_rules! uint_impl { without modifying the original"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[requires(!self.overflowing_mul(rhs).1)] pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -1614,7 +1613,6 @@ macro_rules! uint_impl { #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[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. pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, From 500e8dba61c74a9b941f97f510aba0d9db993988 Mon Sep 17 00:00:00 2001 From: rajathmCMU Date: Wed, 2 Oct 2024 13:03:51 -0700 Subject: [PATCH 31/67] Add comments. Fix spacing --- library/core/src/num/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index b389468bac52e..afa9d4385fb3c 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1591,6 +1591,9 @@ from_str_radix_size_impl! { i64 isize, u64 usize } mod verify { use super::*; + // Verify `unchecked_{add, sub, mul}` + // However, `unchecked_mul` harnesses have bad performance, so + // recommend to use generate_unchecked_mul_harness! to set input limits macro_rules! generate_unchecked_math_harness { ($type:ty, $method:ident, $harness_name:ident) => { #[kani::proof_for_contract($type::$method)] @@ -1605,14 +1608,15 @@ mod verify { } } + // Improve unchecked_mul performance for {32, 64, 128}-bit integer types + // by adding upper and lower limits for inputs macro_rules! generate_unchecked_mul_harness { ($type:ty, $method:ident, $harness_name:ident, $min:expr, $max:expr) => { #[kani::proof_for_contract($type::$method)] pub fn $harness_name() { - let num1: $type = kani::any(); - let num2: $type = kani::any(); - - // Limit the values of num1 and num2 to the specified range for multiplication + let num1: $type = kani::any::<$type>(); + let num2: $type = kani::any::<$type>(); + kani::assume(num1 >= $min && num1 <= $max); kani::assume(num2 >= $min && num2 <= $max); @@ -1622,8 +1626,8 @@ mod verify { } } } - + // Verify `unchecked_{shl, shr}` macro_rules! generate_unchecked_shift_harness { ($type:ty, $method:ident, $harness_name:ident) => { #[kani::proof_for_contract($type::$method)] @@ -1637,7 +1641,6 @@ mod verify { } } } - macro_rules! generate_unchecked_neg_harness { ($type:ty, $method:ident, $harness_name:ident) => { @@ -1699,7 +1702,6 @@ mod verify { generate_unchecked_mul_harness!(u128, unchecked_mul, checked_unchecked_mul_u128, 0u128, 1_000_000_000_000_000u128); generate_unchecked_mul_harness!(usize, unchecked_mul, checked_unchecked_mul_usize, 0usize, 100_000usize); - // unchecked_shr proofs // // Target types: @@ -1722,6 +1724,4 @@ mod verify { generate_unchecked_shift_harness!(u64, unchecked_shr, checked_unchecked_shr_u64); generate_unchecked_shift_harness!(u128, unchecked_shr, checked_unchecked_shr_u128); generate_unchecked_shift_harness!(usize, unchecked_shr, checked_unchecked_shr_usize); - } - } } From 1c97825c7497118a4d0f27a265e0d1531cd9d3e4 Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Wed, 2 Oct 2024 15:43:53 -0700 Subject: [PATCH 32/67] cleaned up code: deleted the previous attempted proofs --- kani | 1 + .../core/tests/num/kani-proofs/i8-proofs.rs | 65 ------------------- 2 files changed, 1 insertion(+), 65 deletions(-) create mode 160000 kani delete mode 100644 library/core/tests/num/kani-proofs/i8-proofs.rs diff --git a/kani b/kani new file mode 160000 index 0000000000000..4f75199d476c1 --- /dev/null +++ b/kani @@ -0,0 +1 @@ +Subproject commit 4f75199d476c10316e43171b662bf97a35cfdb29 diff --git a/library/core/tests/num/kani-proofs/i8-proofs.rs b/library/core/tests/num/kani-proofs/i8-proofs.rs deleted file mode 100644 index daa488a88a960..0000000000000 --- a/library/core/tests/num/kani-proofs/i8-proofs.rs +++ /dev/null @@ -1,65 +0,0 @@ -#[cfg(kani)] -mod verification { - // use super::*; - - #[kani::proof] - fn verify_i8_unchecked_add() { - let num1: i8 = kani::any::(); - let num2: i8 = kani::any::(); - - // Safety preconditions: - // - Positive number addition won't overflow - // - Negative number addition won't underflow - // Addition of two integers with different signs never overflows - // Undefined behavior occurs when overflow or underflow happens - kani::assume( - (num1 > 0 && num2 > 0 && num1 < i8::MAX - num2) - || (num1 < 0 && num2 < 0 && num1 > i8::MIN - num2), - ); - - unsafe { - let result = num1.unchecked_add(num2); - assert_eq!(Some(result), num1.checked_add(num2)); - } - } - - #[kani::proof] - fn verify_i8_unchecked_sub() { - let num1: i8 = kani::any::(); - let num2: i8 = kani::any::(); - - // Safety preconditions: - // - Subtraction won't underflow when num1 is positive and num2 is negative - // - Subtraction won't overflow when num1 is negative and num2 is positive - // Undefined behavior occurs when overflow or underflow happens - kani::assume( - (num1 >= 0 && num2 <= 0 && num1 <= i8::MAX + num2) - || (num1 <= 0 && num2 >= 0 && num1 >= i8::MIN + num2), - ); - - unsafe { - let result = num1.unchecked_sub(num2); - assert_eq!(Some(result), num1.checked_sub(num2)); - } - } - - #[kani::proof] - fn verify_i8_unchecked_mul() { - // TODO - } - - #[kani::proof] - fn verify_i8_unchecked_shl() { - // TODO - } - - #[kani::proof] - fn verify_i8_unchecked_shr() { - // TODO - } - - #[kani::proof] - fn verify_i8_unchecked_neg() { - // TODO - } -} From e3eb36588bd38c1c0765d44dc159b9ff9edf7d70 Mon Sep 17 00:00:00 2001 From: MWDZ Date: Wed, 2 Oct 2024 22:56:38 -0700 Subject: [PATCH 33/67] feat: merge the latest version --- doc/src/tools/kani.md | 77 +++++++--- library/core/src/num/int_macros.rs | 3 +- library/core/src/num/mod.rs | 223 +++++++++++++++------------- library/core/src/num/uint_macros.rs | 3 +- library/core/src/ptr/mod.rs | 8 +- library/core/src/ptr/non_null.rs | 34 +++++ 6 files changed, 225 insertions(+), 123 deletions(-) diff --git a/doc/src/tools/kani.md b/doc/src/tools/kani.md index 985d507c4be33..9cfd198e9bc8e 100644 --- a/doc/src/tools/kani.md +++ b/doc/src/tools/kani.md @@ -5,7 +5,7 @@ Kani is designed to prove safety properties in your code as well as the absence of some forms of undefined behavior. It uses model checking under the hood to ensure that Rust programs adhere to user specified properties. -You can find more information about how to install in [this section of the Kani book](https://model-checking.github.io/kani/install-guide.html). +You can find more information about how to install in [the installation section of the Kani book](https://model-checking.github.io/kani/install-guide.html). ## Usage @@ -27,7 +27,8 @@ fn harness() { let a = kani::any::(); let b = kani::any::(); let result = abs_diff(a, b); - kani::assert(result >= 0, "Result should always be more than 0");} + kani::assert(result >= 0, "Result should always be more than 0"); +} ``` Running the command `cargo kani` in your cargo crate will give the result @@ -46,29 +47,28 @@ Verification failed for - harness Complete - 0 successfully verified harnesses, 1 failures, 1 total. ``` +For a more detailed tutorial, you can refer to the [tutorial section of the Kani book](https://model-checking.github.io/kani/kani-tutorial.html). ## Using Kani to verify the Rust Standard Library -To aid the Rust Standard Library verification effort, Kani provides a sub-command out of the box to help you get started. +Here is a short tutorial of how to use Kani to verify proofs for the standard library. -### Step 1 +### Step 1 - Add some proofs to your copy of the model-checking std -Modify your local copy of the Rust Standard Library by writing proofs for the functions/methods that you want to verify. +Create a local copy of the [model-checking fork](https://github.com/model-checking/verify-rust-std) of the Rust Standard Library. The fork comes with Kani configured, so all you'll need to do is to call Kani's building-block APIs (such as +`assert`, `assume`, `proof` and [function-contracts](https://github.com/model-checking/kani/blob/main/rfc/src/rfcs/0009-function-contracts.md) such as `modifies`, `requires` and `ensures`) directly. -For example, insert this short blob into your copy of the library. This blob imports the building-block APIs such as -`assert`, `assume`, `proof` and [function-contracts](https://github.com/model-checking/kani/blob/main/rfc/src/rfcs/0009-function-contracts.md) such as `proof_for_contract` and `fake_function`. -``` rust -#[cfg(kani)] -kani_core::kani_lib!(core); +For example, insert this module into an existing file in the core library, like `library/core/src/hint.rs` or `library/core/src/error.rs` in your copy of the library. This is just for the purpose of getting started, so you can insert in any existing file in the core library if you have other preferences. +``` rust #[cfg(kani)] #[unstable(feature = "kani", issue = "none")] pub mod verify { use crate::kani; #[kani::proof] - pub fn harness() { + pub fn harness_introduction() { kani::assert(true, "yay"); } @@ -84,21 +84,24 @@ pub mod verify { } ``` -### Step 2 +### Step 2 - Run the Kani verify-std subcommand -Run the following command in your local terminal: +To aid the Rust Standard Library verification effort, Kani provides a sub-command out of the box to help you get started. +Run the following command in your local terminal (Replace "/path/to/library" and "/path/to/target" with your local paths) from the verify repository root: -`kani verify-std -Z unstable-options "path/to/library" --target-dir "/path/to/target" -Z function-contracts -Z stubbing`. +``` +kani verify-std -Z unstable-options "/path/to/library" --target-dir "/path/to/target" -Z function-contracts -Z mem-predicates +``` The command `kani verify-std` is a sub-command of the `kani`. This specific sub-command is used to verify the Rust Standard Library with the following arguments. -- `"path/to/library"`: This argument specifies the path to the modified Rust Standard Library that was prepared earlier in the script. -- `--target-dir "path/to/target"`: This optional argument sets the target directory where Kani will store its output and intermediate files. +- `"path/to/library"`: This argument specifies the path to the modified Rust Standard Library that was prepared earlier in the script. For example, `./library` or `/home/ubuntu/verify-rust-std/library` +- `--target-dir "path/to/target"`: This optional argument sets the target directory where Kani will store its output and intermediate files. For example, `/tmp` or `/tmp/verify-std` -Apart from these, you can use your regular `kani-args` such as `-Z function-contracts` and `-Z stubbing` depending on your verification needs. +Apart from these, you can use your regular `kani-args` such as `-Z function-contracts`, `-Z stubbing` and `-Z mem-predicates` depending on your verification needs. If you run into a Kani error that says `Use of unstable feature`, add the corresponding feature with `-Z` to the command line. For more details on Kani's features, refer to [the features section in the Kani Book](https://model-checking.github.io/kani/reference/attributes.html) -### Step 3 +### Step 3 - Check verification result After running the command, you can expect an output that looks like this: @@ -112,6 +115,44 @@ Verification Time: 0.017101772s Complete - 2 successfully verified harnesses, 0 failures, 2 total. ``` +### Running on a specific harness + +You can specify a specific harness to be verified using the `--harness` flag. + +For example, in your local copy of the verify repo, run the following command. + +``` +kani verify-std --harness harness_introduction -Z unstable-options "./library" --target-dir "/tmp" -Z function-contracts -Z mem-predicates +``` + +This gives you the verification result for just `harness_introduction` from the aforementioned blob. + +``` +RESULTS: +Check 1: verify::harness_introduction.assertion.1 + - Status: SUCCESS + - Description: "yay" + - Location: library/core/src/lib.rs:479:9 in function verify::harness_introduction + + +SUMMARY: + ** 0 of 1 failed + +VERIFICATION:- SUCCESSFUL +Verification Time: 0.01885804s + +Complete - 1 successfully verified harnesses, 0 failures, 1 total. +``` + +Now you can write proof harnesses to verify specific functions in the library. +The current convention is to keep proofs in the same module file of the verification target. +To run Kani for an individual proof, use `--harness [harness_function_name]`. +Note that Kani will batch run all proofs in the library folder if you do not supply the `--harness` flag. +If Kani returns the error `no harnesses matched the harness filter`, you can give the full name of the harness. +For example, to run the proof harness named `check_new` in `library/core/src/ptr/unique.rs`, use +`--harness ptr::unique::verify::check_new`. To run all proofs in `unique.rs`, use `--harness ptr::unique::verify`. +To find the full name of a harness, check the Kani output and find the line starting with `Checking harness [harness full name]`. + ## More details You can find more information about how to install and how you can customize your use of Kani in the diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 486f9493b7148..29c9e048bd93a 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -512,7 +512,6 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires(!self.overflowing_add(rhs).1)] - #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -817,6 +816,7 @@ macro_rules! int_impl { without modifying the original"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[requires(!self.overflowing_mul(rhs).1)] pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -1427,6 +1427,7 @@ macro_rules! int_impl { #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[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. pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 3d65b60b155f7..6c96c769cc736 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1401,10 +1401,7 @@ const fn from_str_radix_panic_ct(_radix: u32) -> ! { #[track_caller] fn from_str_radix_panic_rt(radix: u32) -> ! { - panic!( - "from_str_radix_int: must lie in the range `[2, 36]` - found {}", - radix - ); + panic!("from_str_radix_int: must lie in the range `[2, 36]` - found {}", radix); } #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] @@ -1589,120 +1586,144 @@ from_str_radix_size_impl! { i32 isize, u32 usize } #[cfg(target_pointer_width = "64")] from_str_radix_size_impl! { i64 isize, u64 usize } +#[cfg(kani)] #[unstable(feature = "kani", issue = "none")] mod verify { use super::*; - // `unchecked_add` proofs - // - // Target types: - // i{8,16,32,64,128} and u{8,16,32,64,128} -- 10 types in total - // - // Target contracts: - // #[requires(!self.overflowing_add(rhs).1)] - // #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] - // - // Target function: - // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self - #[kani::proof_for_contract(i8::unchecked_add)] - pub fn check_unchecked_add_i8() { - let num1: i8 = kani::any::(); - let num2: i8 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(i16::unchecked_add)] - pub fn check_unchecked_add_i16() { - let num1: i16 = kani::any::(); - let num2: i16 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(i32::unchecked_add)] - pub fn check_unchecked_add_i32() { - let num1: i32 = kani::any::(); - let num2: i32 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(i64::unchecked_add)] - pub fn check_unchecked_add_i64() { - let num1: i64 = kani::any::(); - let num2: i64 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(i128::unchecked_add)] - pub fn check_unchecked_add_i128() { - let num1: i128 = kani::any::(); - let num2: i128 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } - - #[kani::proof_for_contract(u8::unchecked_add)] - pub fn check_unchecked_add_u8() { - let num1: u8 = kani::any::(); - let num2: u8 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); + // Verify `unchecked_{add, sub, mul}` + // However, `unchecked_mul` harnesses have bad performance, so + // recommend to use generate_unchecked_mul_harness! to set input limits + macro_rules! generate_unchecked_math_harness { + ($type:ty, $method:ident, $harness_name:ident) => { + #[kani::proof_for_contract($type::$method)] + pub fn $harness_name() { + let num1: $type = kani::any::<$type>(); + let num2: $type = kani::any::<$type>(); + + unsafe { + num1.$method(num2); + } + } } } - #[kani::proof_for_contract(u16::unchecked_add)] - pub fn check_unchecked_add_u16() { - let num1: u16 = kani::any::(); - let num2: u16 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); + // Improve unchecked_mul performance for {32, 64, 128}-bit integer types + // by adding upper and lower limits for inputs + macro_rules! generate_unchecked_mul_harness { + ($type:ty, $method:ident, $harness_name:ident, $min:expr, $max:expr) => { + #[kani::proof_for_contract($type::$method)] + pub fn $harness_name() { + let num1: $type = kani::any::<$type>(); + let num2: $type = kani::any::<$type>(); + + kani::assume(num1 >= $min && num1 <= $max); + kani::assume(num2 >= $min && num2 <= $max); + + unsafe { + num1.$method(num2); + } + } } } - #[kani::proof_for_contract(u32::unchecked_add)] - pub fn check_unchecked_add_u32() { - let num1: u32 = kani::any::(); - let num2: u32 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); + // Verify `unchecked_{shl, shr}` + macro_rules! generate_unchecked_shift_harness { + ($type:ty, $method:ident, $harness_name:ident) => { + #[kani::proof_for_contract($type::$method)] + pub fn $harness_name() { + let num1: $type = kani::any::<$type>(); + let num2: u32 = kani::any::(); + + unsafe { + num1.$method(num2); + } + } } } - #[kani::proof_for_contract(u64::unchecked_add)] - pub fn check_unchecked_add_u64() { - let num1: u64 = kani::any::(); - let num2: u64 = kani::any::(); + macro_rules! generate_unchecked_neg_harness { + ($type:ty, $method:ident, $harness_name:ident) => { + #[kani::proof_for_contract($type::$method)] + pub fn $harness_name() { + let num1: $type = kani::any::<$type>(); - unsafe { - num1.unchecked_add(num2); + unsafe { + num1.$method(); + } + } } } - #[kani::proof_for_contract(u128::unchecked_add)] - pub fn check_unchecked_add_u128() { - let num1: u128 = kani::any::(); - let num2: u128 = kani::any::(); - - unsafe { - num1.unchecked_add(num2); - } - } + // `unchecked_add` proofs + // + // Target types: + // i{8,16,32,64,128, size} and u{8,16,32,64,128, size} -- 12 types in total + // + // Target contracts: + // #[requires(!self.overflowing_add(rhs).1)] + // + // Target function: + // pub const unsafe fn unchecked_add(self, rhs: Self) -> Self + generate_unchecked_math_harness!(i8, unchecked_add, checked_unchecked_add_i8); + generate_unchecked_math_harness!(i16, unchecked_add, checked_unchecked_add_i16); + generate_unchecked_math_harness!(i32, unchecked_add, checked_unchecked_add_i32); + generate_unchecked_math_harness!(i64, unchecked_add, checked_unchecked_add_i64); + generate_unchecked_math_harness!(i128, unchecked_add, checked_unchecked_add_i128); + generate_unchecked_math_harness!(isize, unchecked_add, checked_unchecked_add_isize); + generate_unchecked_math_harness!(u8, unchecked_add, checked_unchecked_add_u8); + generate_unchecked_math_harness!(u16, unchecked_add, checked_unchecked_add_u16); + generate_unchecked_math_harness!(u32, unchecked_add, checked_unchecked_add_u32); + generate_unchecked_math_harness!(u64, unchecked_add, checked_unchecked_add_u64); + generate_unchecked_math_harness!(u128, unchecked_add, checked_unchecked_add_u128); + generate_unchecked_math_harness!(usize, unchecked_add, checked_unchecked_add_usize); + + // unchecked_mul proofs + // + // Target types: + // i{8,16,32,64,128, size} and u{8,16,32,64,128, size} -- 12 types in total + // + // Target contracts: + // #[requires(!self.overflowing_mul(rhs).1)] + // + // Target function: + // pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self + // exponential state spaces for 32,64 and 128, hence provided limited range for verification. + generate_unchecked_math_harness!(i8, unchecked_mul, checked_unchecked_mul_i8); + generate_unchecked_math_harness!(i16, unchecked_mul, checked_unchecked_mul_i16); + generate_unchecked_mul_harness!(i32, unchecked_mul, checked_unchecked_mul_i32, -10_000i32, 10_000i32); + generate_unchecked_mul_harness!(i64, unchecked_mul, checked_unchecked_mul_i64, -1_000i64, 1_000i64); + generate_unchecked_mul_harness!(i128, unchecked_mul, checked_unchecked_mul_i128, -1_000_000_000_000_000i128, 1_000_000_000_000_000i128); + generate_unchecked_mul_harness!(isize, unchecked_mul, checked_unchecked_mul_isize, -100_000isize, 100_000isize); + generate_unchecked_math_harness!(u8, unchecked_mul, checked_unchecked_mul_u8); + generate_unchecked_math_harness!(u16, unchecked_mul, checked_unchecked_mul_u16); + generate_unchecked_mul_harness!(u32, unchecked_mul, checked_unchecked_mul_u32, 0u32, 20_000u32); + generate_unchecked_mul_harness!(u64, unchecked_mul, checked_unchecked_mul_u64, 0u64, 2_000u64); + generate_unchecked_mul_harness!(u128, unchecked_mul, checked_unchecked_mul_u128, 0u128, 1_000_000_000_000_000u128); + generate_unchecked_mul_harness!(usize, unchecked_mul, checked_unchecked_mul_usize, 0usize, 100_000usize); + + // unchecked_shr proofs + // + // Target types: + // i{8,16,32,64,128,size} and u{8,16,32,64,128,size} -- 12 types in total + // + // Target contracts: + // #[requires(rhs < <$ActualT>::BITS)] + // + // Target function: + // pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self + generate_unchecked_shift_harness!(i8, unchecked_shr, checked_unchecked_shr_i8); + generate_unchecked_shift_harness!(i16, unchecked_shr, checked_unchecked_shr_i16); + generate_unchecked_shift_harness!(i32, unchecked_shr, checked_unchecked_shr_i32); + generate_unchecked_shift_harness!(i64, unchecked_shr, checked_unchecked_shr_i64); + generate_unchecked_shift_harness!(i128, unchecked_shr, checked_unchecked_shr_i128); + generate_unchecked_shift_harness!(isize, unchecked_shr, checked_unchecked_shr_isize); + generate_unchecked_shift_harness!(u8, unchecked_shr, checked_unchecked_shr_u8); + generate_unchecked_shift_harness!(u16, unchecked_shr, checked_unchecked_shr_u16); + generate_unchecked_shift_harness!(u32, unchecked_shr, checked_unchecked_shr_u32); + generate_unchecked_shift_harness!(u64, unchecked_shr, checked_unchecked_shr_u64); + generate_unchecked_shift_harness!(u128, unchecked_shr, checked_unchecked_shr_u128); + generate_unchecked_shift_harness!(usize, unchecked_shr, checked_unchecked_shr_usize); // `unchecked_shl` proofs // diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 617db39cbc220..44f942d0cffd9 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -559,7 +559,6 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires(!self.overflowing_add(rhs).1)] - #[ensures(|ret| *ret >= $SelfT::MIN && *ret <= $SelfT::MAX)] pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -909,6 +908,7 @@ macro_rules! uint_impl { without modifying the original"] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[requires(!self.overflowing_mul(rhs).1)] pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, @@ -1616,6 +1616,7 @@ macro_rules! uint_impl { #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[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. pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 43d81348cb1a8..2f92443cf1656 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -2552,8 +2552,12 @@ mod verify { } #[kani::proof_for_contract(align_offset)] - fn check_align_offset_17() { - let p = kani::any::() as *const [char; 17]; + // The purpose of this harness is to test align_offset with a pointee type whose stride is not a power of two + // Keeping the size smaller (5) and changing the solver to kissat improves verification time + // c.f. https://github.com/model-checking/verify-rust-std/pull/89 + #[kani::solver(kissat)] + fn check_align_offset_5() { + let p = kani::any::() as *const [char; 5]; check_align_offset(p); } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index b1429fff74434..4d6e484915dbf 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -8,6 +8,11 @@ use crate::ptr::Unique; use crate::slice::{self, SliceIndex}; use crate::ub_checks::assert_unsafe_precondition; use crate::{fmt, hash, intrinsics, ptr}; +use safety::{ensures, requires}; + + +#[cfg(kani)] +use crate::kani; /// `*mut T` but non-zero and [covariant]. /// @@ -192,6 +197,8 @@ impl NonNull { #[stable(feature = "nonnull", since = "1.25.0")] #[rustc_const_stable(feature = "const_nonnull_new_unchecked", since = "1.25.0")] #[inline] + #[requires(!ptr.is_null())] + #[ensures(|result| result.as_ptr() == ptr)] pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { // SAFETY: the caller must guarantee that `ptr` is non-null. unsafe { @@ -221,6 +228,8 @@ impl NonNull { #[stable(feature = "nonnull", since = "1.25.0")] #[rustc_const_unstable(feature = "const_nonnull_new", issue = "93235")] #[inline] + #[ensures(|result| result.is_some() == !ptr.is_null())] + #[ensures(|result| result.is_none() || result.expect("ptr is null!").as_ptr() == ptr)] pub const fn new(ptr: *mut T) -> Option { if !ptr.is_null() { // SAFETY: The pointer is already checked and is not null @@ -1770,3 +1779,28 @@ impl From<&T> for NonNull { unsafe { NonNull { pointer: reference as *const T } } } } + +#[cfg(kani)] +#[unstable(feature="kani", issue="none")] +mod verify { + use super::*; + use crate::ptr::null_mut; + + // pub const unsafe fn new_unchecked(ptr: *mut T) -> Self + #[kani::proof_for_contract(NonNull::new_unchecked)] + pub fn non_null_check_new_unchecked() { + let raw_ptr = kani::any::() as *mut i32; + unsafe { + let _ = NonNull::new_unchecked(raw_ptr); + } + } + + // pub const unsafe fn new(ptr: *mut T) -> Option + #[kani::proof_for_contract(NonNull::new)] + pub fn non_null_check_new() { + let mut x: i32 = kani::any(); + let xptr = &mut x; + let maybe_null_ptr = if kani::any() { xptr as *mut i32 } else { null_mut() }; + let _ = NonNull::new(maybe_null_ptr); + } +} From 2b3771f64f0cb31eb652fec5aab3eb828a73ee02 Mon Sep 17 00:00:00 2001 From: MWDZ Date: Wed, 2 Oct 2024 23:06:17 -0700 Subject: [PATCH 34/67] feat: use macro rules for unchecked_shl harness --- library/core/src/num/int_macros.rs | 3 +- library/core/src/num/mod.rs | 110 +++------------------------- library/core/src/num/uint_macros.rs | 3 +- 3 files changed, 14 insertions(+), 102 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 29c9e048bd93a..17c9ac57b3626 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1299,8 +1299,7 @@ macro_rules! int_impl { #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[requires(rhs < Self::BITS)] - #[ensures(|ret| *ret >= Self::MIN && *ret <= Self::MAX)] + #[requires(rhs < <$ActualT>::BITS)] pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 6c96c769cc736..c0ea579486d14 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1738,103 +1738,17 @@ mod verify { // pub const unsafe fn unchecked_shl(self, shift: u32) -> Self // // This function performs an unchecked bitwise left shift operation. - #[kani::proof_for_contract(i8::unchecked_shl)] - pub fn check_unchecked_shl_i8() { - let num1: i8 = kani::any::(); - let shift: u32 = kani::any::(); + generate_unchecked_shift_harness!(i8, unchecked_shl, checked_unchecked_shl_i8); + generate_unchecked_shift_harness!(i16, unchecked_shl, checked_unchecked_shl_i16); + generate_unchecked_shift_harness!(i32, unchecked_shl, checked_unchecked_shl_i32); + generate_unchecked_shift_harness!(i64, unchecked_shl, checked_unchecked_shl_i64); + generate_unchecked_shift_harness!(i128, unchecked_shl, checked_unchecked_shl_i128); + generate_unchecked_shift_harness!(isize, unchecked_shl, checked_unchecked_shl_isize); + generate_unchecked_shift_harness!(u8, unchecked_shl, checked_unchecked_shl_u8); + generate_unchecked_shift_harness!(u16, unchecked_shl, checked_unchecked_shl_u16); + generate_unchecked_shift_harness!(u32, unchecked_shl, checked_unchecked_shl_u32); + generate_unchecked_shift_harness!(u64, unchecked_shl, checked_unchecked_shl_u64); + generate_unchecked_shift_harness!(u128, unchecked_shl, checked_unchecked_shl_u128); + generate_unchecked_shift_harness!(usize, unchecked_shl, checked_unchecked_shl_usize); - unsafe { - num1.unchecked_shl(shift); - } - } - - #[kani::proof_for_contract(i16::unchecked_shl)] - pub fn check_unchecked_shl_i16() { - let num1: i16 = kani::any::(); - let shift: u32 = kani::any::(); - - unsafe { - num1.unchecked_shl(shift); - } - } - - #[kani::proof_for_contract(i32::unchecked_shl)] - pub fn check_unchecked_shl_i32() { - let num1: i32 = kani::any::(); - let shift: u32 = kani::any::(); - - unsafe { - num1.unchecked_shl(shift); - } - } - - #[kani::proof_for_contract(i64::unchecked_shl)] - pub fn check_unchecked_shl_i64() { - let num1: i64 = kani::any::(); - let shift: u32 = kani::any::(); - - unsafe { - num1.unchecked_shl(shift); - } - } - - #[kani::proof_for_contract(i128::unchecked_shl)] - pub fn check_unchecked_shl_i128() { - let num1: i128 = kani::any::(); - let shift: u32 = kani::any::(); - - unsafe { - num1.unchecked_shl(shift); - } - } - - #[kani::proof_for_contract(u8::unchecked_shl)] - pub fn check_unchecked_shl_u8() { - let num1: u8 = kani::any::(); - let shift: u32 = kani::any::(); - - unsafe { - num1.unchecked_shl(shift); - } - } - - #[kani::proof_for_contract(u16::unchecked_shl)] - pub fn check_unchecked_shl_u16() { - let num1: u16 = kani::any::(); - let shift: u32 = kani::any::(); - - unsafe { - num1.unchecked_shl(shift); - } - } - - #[kani::proof_for_contract(u32::unchecked_shl)] - pub fn check_unchecked_shl_u32() { - let num1: u32 = kani::any::(); - let shift: u32 = kani::any::(); - - unsafe { - num1.unchecked_shl(shift); - } - } - - #[kani::proof_for_contract(u64::unchecked_shl)] - pub fn check_unchecked_shl_u64() { - let num1: u64 = kani::any::(); - let shift: u32 = kani::any::(); - - unsafe { - num1.unchecked_shl(shift); - } - } - - #[kani::proof_for_contract(u128::unchecked_shl)] - pub fn check_unchecked_shl_u128() { - let num1: u128 = kani::any::(); - let shift: u32 = kani::any::(); - - unsafe { - num1.unchecked_shl(shift); - } - } } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 44f942d0cffd9..fca28f5556994 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1488,8 +1488,7 @@ macro_rules! uint_impl { #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - #[requires(rhs < Self::BITS)] - #[ensures(|ret| *ret >= Self::MIN && *ret <= Self::MAX)] + #[requires(rhs < <$ActualT>::BITS)] pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { assert_unsafe_precondition!( check_language_ub, From 49b0a6b7d51dcbabeb8abda06a9a1dabe7837748 Mon Sep 17 00:00:00 2001 From: rajathmCMU Date: Fri, 4 Oct 2024 23:58:49 -0700 Subject: [PATCH 35/67] add interval checks to unchecked_mul --- library/core/src/num/mod.rs | 90 +++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 24 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index c07eadc1f518d..b949d2c11aca5 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1610,20 +1610,25 @@ mod verify { // Improve unchecked_mul performance for {32, 64, 128}-bit integer types // by adding upper and lower limits for inputs - macro_rules! generate_unchecked_mul_harness { - ($type:ty, $method:ident, $harness_name:ident, $min:expr, $max:expr) => { - #[kani::proof_for_contract($type::$method)] - pub fn $harness_name() { - let num1: $type = kani::any::<$type>(); - let num2: $type = kani::any::<$type>(); - - kani::assume(num1 >= $min && num1 <= $max); - kani::assume(num2 >= $min && num2 <= $max); + macro_rules! generate_unchecked_mul_intervals { + ($type:ty, $method:ident, $($harness_name:ident, $min:expr, $max:expr),+) => { + $( + #[kani::proof_for_contract($type::$method)] + pub fn $harness_name() { + let num1: $type = kani::any::<$type>(); + let num2: $type = kani::any::<$type>(); - unsafe { - num1.$method(num2); + kani::assume(num1 >= $min && num1 <= $max); + kani::assume(num2 >= $min && num2 <= $max); + + // Ensure that multiplication does not overflow + kani::assume(!num1.overflowing_mul(num2).1); + + unsafe { + num1.$method(num2); + } } - } + )+ } } @@ -1689,18 +1694,55 @@ mod verify { // Target function: // pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self // exponential state spaces for 32,64 and 128, hence provided limited range for verification. - generate_unchecked_math_harness!(i8, unchecked_mul, checked_unchecked_mul_i8); - generate_unchecked_math_harness!(i16, unchecked_mul, checked_unchecked_mul_i16); - generate_unchecked_mul_harness!(i32, unchecked_mul, checked_unchecked_mul_i32, -10_000i32, 10_000i32); - generate_unchecked_mul_harness!(i64, unchecked_mul, checked_unchecked_mul_i64, -1_000i64, 1_000i64); - generate_unchecked_mul_harness!(i128, unchecked_mul, checked_unchecked_mul_i128, -1_000_000_000_000_000i128, 1_000_000_000_000_000i128); - generate_unchecked_mul_harness!(isize, unchecked_mul, checked_unchecked_mul_isize, -100_000isize, 100_000isize); - generate_unchecked_math_harness!(u8, unchecked_mul, checked_unchecked_mul_u8); - generate_unchecked_math_harness!(u16, unchecked_mul, checked_unchecked_mul_u16); - generate_unchecked_mul_harness!(u32, unchecked_mul, checked_unchecked_mul_u32, 0u32, 20_000u32); - generate_unchecked_mul_harness!(u64, unchecked_mul, checked_unchecked_mul_u64, 0u64, 2_000u64); - generate_unchecked_mul_harness!(u128, unchecked_mul, checked_unchecked_mul_u128, 0u128, 1_000_000_000_000_000u128); - generate_unchecked_mul_harness!(usize, unchecked_mul, checked_unchecked_mul_usize, 0usize, 100_000usize); + + // ====================== i128 Harnesses ====================== + generate_unchecked_mul_intervals!(i128, unchecked_mul, + unchecked_mul_i128_small, -10i128, 10i128, + unchecked_mul_i128_large_pos, i128::MAX - 1000i128, i128::MAX, + unchecked_mul_i128_large_neg, i128::MIN, i128::MIN + 1000i128, + unchecked_mul_i128_edge_pos, i128::MAX / 2, i128::MAX, + unchecked_mul_i128_edge_neg, i128::MIN, i128::MIN / 2 + ); + + // ====================== u128 Harnesses ====================== + generate_unchecked_mul_intervals!(u128, unchecked_mul, + unchecked_mul_u128_small, 0u128, 10u128, + unchecked_mul_u128_large, u128::MAX - 1000u128, u128::MAX, + unchecked_mul_u128_edge, u128::MAX / 2, u128::MAX + ); + + // ====================== i64 Harnesses ====================== + generate_unchecked_mul_intervals!(i64, unchecked_mul, + unchecked_mul_i64_small, -10i64, 10i64, + unchecked_mul_i64_large_pos, i64::MAX - 1000i64, i64::MAX, + unchecked_mul_i64_large_neg, i64::MIN, i64::MIN + 1000i64, + unchecked_mul_i64_edge_pos, i64::MAX / 2, i64::MAX, + unchecked_mul_i64_edge_neg, i64::MIN, i64::MIN / 2 + ); + + // ====================== u64 Harnesses ====================== + generate_unchecked_mul_intervals!(u64, unchecked_mul, + unchecked_mul_u64_small, 0u64, 10u64, + unchecked_mul_u64_large, u64::MAX - 1000u64, u64::MAX, + unchecked_mul_u64_edge, u64::MAX / 2, u64::MAX + ); + + // ====================== i32 Harnesses ====================== + generate_unchecked_mul_intervals!(i32, unchecked_mul, + unchecked_mul_i32_small, -10i32, 10i32, + unchecked_mul_i32_large_pos, i32::MAX - 1000i32, i32::MAX, + unchecked_mul_i32_large_neg, i32::MIN, i32::MIN + 1000i32, + unchecked_mul_i32_edge_pos, i32::MAX / 2, i32::MAX, + unchecked_mul_i32_edge_neg, i32::MIN, i32::MIN / 2 + ); + + // ====================== u32 Harnesses ====================== + generate_unchecked_mul_intervals!(u32, unchecked_mul, + unchecked_mul_u32_small, 0u32, 10u32, + unchecked_mul_u32_large, u32::MAX - 1000u32, u32::MAX, + unchecked_mul_u32_edge, u32::MAX / 2, u32::MAX + ); + // unchecked_shr proofs // From bbb9656109d7328968afd432a0f5c7f42f6f7993 Mon Sep 17 00:00:00 2001 From: rajathmCMU Date: Sat, 5 Oct 2024 00:12:59 -0700 Subject: [PATCH 36/67] unchecked neg fix --- library/core/src/num/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 5db7e7444753f..46dd4e42f06b0 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1646,15 +1646,12 @@ mod verify { } macro_rules! generate_unchecked_neg_harness { - ($type:ty, $method:ident, $harness_name:ident) => { - #[kani::proof_for_contract($type::$method)] ($type:ty, $harness_name:ident) => { #[kani::proof_for_contract($type::unchecked_neg)] pub fn $harness_name() { let num1: $type = kani::any::<$type>(); unsafe { - num1.$method(); num1.unchecked_neg(); } } From cafeea653b59e79f713649d8567d32e3f113106b Mon Sep 17 00:00:00 2001 From: rajathmCMU Date: Sat, 5 Oct 2024 00:32:38 -0700 Subject: [PATCH 37/67] add missing i8, i16 unchecked_mul --- library/core/src/num/mod.rs | 76 ++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 46dd4e42f06b0..d38364715a730 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1684,7 +1684,7 @@ mod verify { // unchecked_mul proofs // // Target types: - // i{8,16,32,64,128, size} and u{8,16,32,64,128, size} -- 12 types in total + // i{8,16,32,64,128, size} and u{8,16,32,64,128, size} -- 36 types in total // // Target contracts: // #[requires(!self.overflowing_mul(rhs).1)] @@ -1692,7 +1692,25 @@ mod verify { // Target function: // pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self // exponential state spaces for 32,64 and 128, hence provided limited range for verification. + generate_unchecked_math_harness!(i8, unchecked_mul, checked_unchecked_mul_i8); + generate_unchecked_math_harness!(i16, unchecked_mul, checked_unchecked_mul_i16); + // ====================== i32 Harnesses ====================== + generate_unchecked_mul_intervals!(i32, unchecked_mul, + unchecked_mul_i32_small, -10i32, 10i32, + unchecked_mul_i32_large_pos, i32::MAX - 1000i32, i32::MAX, + unchecked_mul_i32_large_neg, i32::MIN, i32::MIN + 1000i32, + unchecked_mul_i32_edge_pos, i32::MAX / 2, i32::MAX, + unchecked_mul_i32_edge_neg, i32::MIN, i32::MIN / 2 + ); + // ====================== i64 Harnesses ====================== + generate_unchecked_mul_intervals!(i64, unchecked_mul, + unchecked_mul_i64_small, -10i64, 10i64, + unchecked_mul_i64_large_pos, i64::MAX - 1000i64, i64::MAX, + unchecked_mul_i64_large_neg, i64::MIN, i64::MIN + 1000i64, + unchecked_mul_i64_edge_pos, i64::MAX / 2, i64::MAX, + unchecked_mul_i64_edge_neg, i64::MIN, i64::MIN / 2 + ); // ====================== i128 Harnesses ====================== generate_unchecked_mul_intervals!(i128, unchecked_mul, unchecked_mul_i128_small, -10i128, 10i128, @@ -1701,47 +1719,43 @@ mod verify { unchecked_mul_i128_edge_pos, i128::MAX / 2, i128::MAX, unchecked_mul_i128_edge_neg, i128::MIN, i128::MIN / 2 ); - - // ====================== u128 Harnesses ====================== - generate_unchecked_mul_intervals!(u128, unchecked_mul, - unchecked_mul_u128_small, 0u128, 10u128, - unchecked_mul_u128_large, u128::MAX - 1000u128, u128::MAX, - unchecked_mul_u128_edge, u128::MAX / 2, u128::MAX + // ====================== isize Harnesses ====================== + generate_unchecked_mul_intervals!(isize, unchecked_mul, + unchecked_mul_isize_small, -10isize, 10isize, + unchecked_mul_isize_large_pos, isize::MAX - 1000isize, isize::MAX, + unchecked_mul_isize_large_neg, isize::MIN, isize::MIN + 1000isize, + unchecked_mul_isize_edge_pos, isize::MAX / 2, isize::MAX, + unchecked_mul_isize_edge_neg, isize::MIN, isize::MIN / 2 ); - // ====================== i64 Harnesses ====================== - generate_unchecked_mul_intervals!(i64, unchecked_mul, - unchecked_mul_i64_small, -10i64, 10i64, - unchecked_mul_i64_large_pos, i64::MAX - 1000i64, i64::MAX, - unchecked_mul_i64_large_neg, i64::MIN, i64::MIN + 1000i64, - unchecked_mul_i64_edge_pos, i64::MAX / 2, i64::MAX, - unchecked_mul_i64_edge_neg, i64::MIN, i64::MIN / 2 - ); + generate_unchecked_math_harness!(u8, unchecked_mul, checked_unchecked_mul_u8); + generate_unchecked_math_harness!(u16, unchecked_mul, checked_unchecked_mul_u16); + // ====================== u32 Harnesses ====================== + generate_unchecked_mul_intervals!(u32, unchecked_mul, + unchecked_mul_u32_small, 0u32, 10u32, + unchecked_mul_u32_large, u32::MAX - 1000u32, u32::MAX, + unchecked_mul_u32_edge, u32::MAX / 2, u32::MAX + ); // ====================== u64 Harnesses ====================== generate_unchecked_mul_intervals!(u64, unchecked_mul, unchecked_mul_u64_small, 0u64, 10u64, unchecked_mul_u64_large, u64::MAX - 1000u64, u64::MAX, unchecked_mul_u64_edge, u64::MAX / 2, u64::MAX ); - - // ====================== i32 Harnesses ====================== - generate_unchecked_mul_intervals!(i32, unchecked_mul, - unchecked_mul_i32_small, -10i32, 10i32, - unchecked_mul_i32_large_pos, i32::MAX - 1000i32, i32::MAX, - unchecked_mul_i32_large_neg, i32::MIN, i32::MIN + 1000i32, - unchecked_mul_i32_edge_pos, i32::MAX / 2, i32::MAX, - unchecked_mul_i32_edge_neg, i32::MIN, i32::MIN / 2 + // ====================== u128 Harnesses ====================== + generate_unchecked_mul_intervals!(u128, unchecked_mul, + unchecked_mul_u128_small, 0u128, 10u128, + unchecked_mul_u128_large, u128::MAX - 1000u128, u128::MAX, + unchecked_mul_u128_edge, u128::MAX / 2, u128::MAX ); - - // ====================== u32 Harnesses ====================== - generate_unchecked_mul_intervals!(u32, unchecked_mul, - unchecked_mul_u32_small, 0u32, 10u32, - unchecked_mul_u32_large, u32::MAX - 1000u32, u32::MAX, - unchecked_mul_u32_edge, u32::MAX / 2, u32::MAX + // ====================== usize Harnesses ====================== + generate_unchecked_mul_intervals!(usize, unchecked_mul, + unchecked_mul_usize_small, 0usize, 10usize, + unchecked_mul_usize_large, usize::MAX - 1000usize, usize::MAX, + unchecked_mul_usize_edge, usize::MAX / 2, usize::MAX ); - // unchecked_shr proofs // // Target types: @@ -1768,7 +1782,7 @@ mod verify { // `unchecked_shl` proofs // // Target types: - // i{8,16,32,64,128} and u{8,16,32,64,128} -- 10 types in total + // i{8,16,32,64,128} and u{8,16,32,64,128} -- 12 types in total // // Target contracts: // #[requires(shift < Self::BITS)] From 7f4421cfcc4b3f5372a509913739caf7f3bcd383 Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Sat, 5 Oct 2024 23:49:14 -0700 Subject: [PATCH 38/67] deleted the postconditions of unchecked_sub --- library/core/src/num/int_macros.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 086f9230edc82..cf04b9fcbb707 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -665,7 +665,6 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires(!self.overflowing_sub(rhs).1)] // Preconditions: No overflow should occur - #[ensures(|ret| *ret >= Self::MIN && *ret <= Self::MAX)] // Postconditions: Result must be within valid bounds pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, From 1331a778111d21860b7083359b1c56c6ba9b93d2 Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Sat, 5 Oct 2024 23:50:28 -0700 Subject: [PATCH 39/67] deleted the postconditions for the unchecked_sub in uint_macros --- library/core/src/num/uint_macros.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 647a94bca00a8..0576e087935bb 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -752,7 +752,6 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[requires(!self.overflowing_sub(rhs).1)] // Preconditions: No overflow should occur - #[ensures(|ret| *ret >= Self::MIN && *ret <= Self::MAX)] // Postconditions: Result must be within valid bounds pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, From c413cac2b149dbaba334dda0d6c96a4789060dc6 Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Sun, 6 Oct 2024 00:14:30 -0700 Subject: [PATCH 40/67] refacted uncheck_sub harnesses --- library/core/src/num/mod.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index f318f72382008..231c6f7ac41d1 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1805,4 +1805,17 @@ mod verify { generate_unchecked_shift_harness!(u64, unchecked_shl, checked_unchecked_shl_u64); generate_unchecked_shift_harness!(u128, unchecked_shl, checked_unchecked_shl_u128); generate_unchecked_shift_harness!(usize, unchecked_shl, checked_unchecked_shl_usize); + + generate_unchecked_math_harness!(i8, unchecked_sub, checked_unchecked_sub_i8); + generate_unchecked_math_harness!(i16, unchecked_sub, checked_unchecked_sub_i16); + generate_unchecked_math_harness!(i32, unchecked_sub, checked_unchecked_sub_i32); + generate_unchecked_math_harness!(i64, unchecked_sub, checked_unchecked_sub_i64); + generate_unchecked_math_harness!(i128, unchecked_sub, checked_unchecked_sub_i128); + generate_unchecked_math_harness!(isize, unchecked_sub, checked_unchecked_sub_isize); + generate_unchecked_math_harness!(u8, unchecked_sub, checked_unchecked_sub_u8); + generate_unchecked_math_harness!(u16, unchecked_sub, checked_unchecked_sub_u16); + generate_unchecked_math_harness!(u32, unchecked_sub, checked_unchecked_sub_u32); + generate_unchecked_math_harness!(u64, unchecked_sub, checked_unchecked_sub_u64); + generate_unchecked_math_harness!(u128, unchecked_sub, checked_unchecked_sub_u128); + generate_unchecked_math_harness!(usize, unchecked_sub, checked_unchecked_sub_usize); } From 059dc022f866f88d71a1e3ae3dca44365637ab43 Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Fri, 11 Oct 2024 02:29:14 -0400 Subject: [PATCH 41/67] Add carrying_mul verification harnesses for u8, u16, u32, and u64 --- library/core/src/num/mod.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index e8b7130995831..95b01f0508c78 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1658,6 +1658,22 @@ mod verify { } } + macro_rules! generate_carrying_mul_harness { + ($type:ty, $carry_type:ty, $harness_name:ident) => { + #[kani::proof_for_contract($type::carrying_mul)] + pub fn $harness_name() { + let num1: $type = kani::any::<$type>(); + let num2: $type = kani::any::<$type>(); + + // Simulate carry multiplication + let (result, carry) = num1.carrying_mul(num2); + + // Check if the carry is within bounds of the carry type + assert!(carry <= <$carry_type>::MAX); + } + } + } + // `unchecked_add` proofs // // Target types: @@ -1849,4 +1865,21 @@ mod verify { generate_unchecked_math_harness!(u64, unchecked_sub, checked_unchecked_sub_u64); generate_unchecked_math_harness!(u128, unchecked_sub, checked_unchecked_sub_u128); generate_unchecked_math_harness!(usize, unchecked_sub, checked_unchecked_sub_usize); + + + + + + // ====================== u8 Carrying Mul ====================== + generate_carrying_mul_harness!(u8, u8, carrying_mul_u8); + + // ====================== u16 Carrying Mul ====================== + generate_carrying_mul_harness!(u16, u16, carrying_mul_u16); + + // ====================== u32 Carrying Mul ====================== + generate_carrying_mul_harness!(u32, u32, carrying_mul_u32); + + // ====================== u64 Carrying Mul ====================== + generate_carrying_mul_harness!(u64, u64, carrying_mul_u64); + } From 29ea0ce7fff699975d4f86b64ba760facce0634f Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Fri, 11 Oct 2024 15:10:01 -0400 Subject: [PATCH 42/67] updated the macro args for carrying_mult with initial carrying value --- library/core/src/num/mod.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 95b01f0508c78..6d42a83250c8d 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1664,15 +1664,17 @@ mod verify { pub fn $harness_name() { let num1: $type = kani::any::<$type>(); let num2: $type = kani::any::<$type>(); + let initial_carry: $type = kani::any::<$type>(); // Add the carry value - // Simulate carry multiplication - let (result, carry) = num1.carrying_mul(num2); + // Simulate carry multiplication with three arguments + let (result, carry) = num1.carrying_mul(num2, initial_carry); // Provide the carry as the third argument // Check if the carry is within bounds of the carry type assert!(carry <= <$carry_type>::MAX); } } } + // `unchecked_add` proofs // @@ -1867,19 +1869,18 @@ mod verify { generate_unchecked_math_harness!(usize, unchecked_sub, checked_unchecked_sub_usize); +// ====================== u8 Carrying Mul ====================== +generate_carrying_mul_harness!(u8, u8, carrying_mul_u8); +// ====================== u16 Carrying Mul ====================== +generate_carrying_mul_harness!(u16, u16, carrying_mul_u16); +// ====================== u32 Carrying Mul ====================== +generate_carrying_mul_harness!(u32, u32, carrying_mul_u32); - // ====================== u8 Carrying Mul ====================== - generate_carrying_mul_harness!(u8, u8, carrying_mul_u8); - - // ====================== u16 Carrying Mul ====================== - generate_carrying_mul_harness!(u16, u16, carrying_mul_u16); +// ====================== u64 Carrying Mul ====================== +generate_carrying_mul_harness!(u64, u64, carrying_mul_u64); - // ====================== u32 Carrying Mul ====================== - generate_carrying_mul_harness!(u32, u32, carrying_mul_u32); - - // ====================== u64 Carrying Mul ====================== - generate_carrying_mul_harness!(u64, u64, carrying_mul_u64); } + From 6f7deae1a2ebf576d80679d43bb6420b6012f834 Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:01:21 -0700 Subject: [PATCH 43/67] resolved the compile error for carrying_mul --- library/core/src/num/mod.rs | 69 ++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 6d42a83250c8d..6a5044abe6f13 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1658,23 +1658,39 @@ mod verify { } } - macro_rules! generate_carrying_mul_harness { - ($type:ty, $carry_type:ty, $harness_name:ident) => { - #[kani::proof_for_contract($type::carrying_mul)] - pub fn $harness_name() { - let num1: $type = kani::any::<$type>(); - let num2: $type = kani::any::<$type>(); - let initial_carry: $type = kani::any::<$type>(); // Add the carry value + macro_rules! generate_carrying_mul_intervals { + ($type:ty, $wide_type:ty, $($harness_name:ident, $min:expr, $max:expr),+) => { + $( + #[kani::proof] + pub fn $harness_name() { + let lhs: $type = kani::any::<$type>(); + let rhs: $type = kani::any::<$type>(); + let carry_in: $type = kani::any::<$type>(); - // Simulate carry multiplication with three arguments - let (result, carry) = num1.carrying_mul(num2, initial_carry); // Provide the carry as the third argument + kani::assume(lhs >= $min && lhs <= $max); + kani::assume(rhs >= $min && rhs <= $max); + kani::assume(carry_in >= $min && carry_in <= $max); - // Check if the carry is within bounds of the carry type - assert!(carry <= <$carry_type>::MAX); - } + // Perform the carrying multiplication + let (result, carry_out) = lhs.carrying_mul(rhs, carry_in); + + // Manually compute the expected result and carry using wider type + let wide_result = (lhs as $wide_type) + .wrapping_mul(rhs as $wide_type) + .wrapping_add(carry_in as $wide_type); + + let expected_result = wide_result as $type; + let expected_carry = (wide_result >> <$type>::BITS) as $type; + + // Assert the result and carry are correct + assert_eq!(result, expected_result); + assert_eq!(carry_out, expected_carry); + } + )+ } } + // `unchecked_add` proofs // @@ -1869,17 +1885,30 @@ mod verify { generate_unchecked_math_harness!(usize, unchecked_sub, checked_unchecked_sub_usize); -// ====================== u8 Carrying Mul ====================== -generate_carrying_mul_harness!(u8, u8, carrying_mul_u8); +// ====================== u8 Harnesses ====================== +generate_carrying_mul_intervals!(u8, u16, + carrying_mul_u8_small, 0u8, 10u8, + carrying_mul_u8_large, u8::MAX - 10u8, u8::MAX +); + +// ====================== u16 Harnesses ====================== +generate_carrying_mul_intervals!(u16, u32, + carrying_mul_u16_small, 0u16, 10u16, + carrying_mul_u16_large, u16::MAX - 10u16, u16::MAX +); -// ====================== u16 Carrying Mul ====================== -generate_carrying_mul_harness!(u16, u16, carrying_mul_u16); +// ====================== u32 Harnesses ====================== +generate_carrying_mul_intervals!(u32, u64, + carrying_mul_u32_small, 0u32, 10u32, + carrying_mul_u32_large, u32::MAX - 10u32, u32::MAX +); -// ====================== u32 Carrying Mul ====================== -generate_carrying_mul_harness!(u32, u32, carrying_mul_u32); +// ====================== u64 Harnesses ====================== +generate_carrying_mul_intervals!(u64, u128, + carrying_mul_u64_small, 0u64, 10u64, + carrying_mul_u64_large, u64::MAX - 10u64, u64::MAX +); -// ====================== u64 Carrying Mul ====================== -generate_carrying_mul_harness!(u64, u64, carrying_mul_u64); } From ad031a1b72f5752607834d95a8f45be3af4e7afb Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:11:57 -0700 Subject: [PATCH 44/67] deleted the kani submodule for merge --- kani | 1 - 1 file changed, 1 deletion(-) delete mode 160000 kani diff --git a/kani b/kani deleted file mode 160000 index 4f75199d476c1..0000000000000 --- a/kani +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4f75199d476c10316e43171b662bf97a35cfdb29 From ddabe958591bffee7654735193d4a6e5d9ac38bc Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:13:05 -0700 Subject: [PATCH 45/67] formatted the style --- library/core/src/num/mod.rs | 46 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 6a5044abe6f13..3fc7ccc6f2929 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1885,29 +1885,29 @@ mod verify { generate_unchecked_math_harness!(usize, unchecked_sub, checked_unchecked_sub_usize); -// ====================== u8 Harnesses ====================== -generate_carrying_mul_intervals!(u8, u16, - carrying_mul_u8_small, 0u8, 10u8, - carrying_mul_u8_large, u8::MAX - 10u8, u8::MAX -); - -// ====================== u16 Harnesses ====================== -generate_carrying_mul_intervals!(u16, u32, - carrying_mul_u16_small, 0u16, 10u16, - carrying_mul_u16_large, u16::MAX - 10u16, u16::MAX -); - -// ====================== u32 Harnesses ====================== -generate_carrying_mul_intervals!(u32, u64, - carrying_mul_u32_small, 0u32, 10u32, - carrying_mul_u32_large, u32::MAX - 10u32, u32::MAX -); - -// ====================== u64 Harnesses ====================== -generate_carrying_mul_intervals!(u64, u128, - carrying_mul_u64_small, 0u64, 10u64, - carrying_mul_u64_large, u64::MAX - 10u64, u64::MAX -); + // ====================== u8 Harnesses ====================== + generate_carrying_mul_intervals!(u8, u16, + carrying_mul_u8_small, 0u8, 10u8, + carrying_mul_u8_large, u8::MAX - 10u8, u8::MAX + ); + + // ====================== u16 Harnesses ====================== + generate_carrying_mul_intervals!(u16, u32, + carrying_mul_u16_small, 0u16, 10u16, + carrying_mul_u16_large, u16::MAX - 10u16, u16::MAX + ); + + // ====================== u32 Harnesses ====================== + generate_carrying_mul_intervals!(u32, u64, + carrying_mul_u32_small, 0u32, 10u32, + carrying_mul_u32_large, u32::MAX - 10u32, u32::MAX + ); + + // ====================== u64 Harnesses ====================== + generate_carrying_mul_intervals!(u64, u128, + carrying_mul_u64_small, 0u64, 10u64, + carrying_mul_u64_large, u64::MAX - 10u64, u64::MAX + ); From 27522b45660186dc636cb3cbf54c763056d55415 Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Fri, 18 Oct 2024 01:01:32 -0700 Subject: [PATCH 46/67] Added documentation for generate_carrying_mul_intervals macro --- library/core/src/num/mod.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 3fc7ccc6f2929..cdeaa6f9644f9 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1657,7 +1657,18 @@ mod verify { } } } - + + /// A macro to generate Kani proof harnesses for the `carrying_mul` method, + /// + /// The macro creates multiple harnesses for different ranges of input values, + /// allowing testing of both small and large inputs. + /// + /// # Parameters: + /// - `$type`: The integer type (e.g., u8, u16) for which the `carrying_mul` function is being tested. + /// - `$wide_type`: A wider type to simulate the multiplication (e.g., u16 for u8, u32 for u16). + /// - `$harness_name`: The name of the Kani harness to be generated. + /// - `$min`: The minimum value for the range of inputs for `lhs`, `rhs`, and `carry_in`. + /// - `$max`: The maximum value for the range of inputs for `lhs`, `rhs`, and `carry_in`. macro_rules! generate_carrying_mul_intervals { ($type:ty, $wide_type:ty, $($harness_name:ident, $min:expr, $max:expr),+) => { $( @@ -1886,6 +1897,7 @@ mod verify { // ====================== u8 Harnesses ====================== + /// Kani proof harness for `carrying_mul` on `u8` type with small values. generate_carrying_mul_intervals!(u8, u16, carrying_mul_u8_small, 0u8, 10u8, carrying_mul_u8_large, u8::MAX - 10u8, u8::MAX From 604d7461737bf78b526f0a1b7f18b170684da2ff Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Sat, 19 Oct 2024 00:26:20 -0700 Subject: [PATCH 47/67] added full range interval for u8 and u16 --- library/core/src/num/mod.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index cdeaa6f9644f9..9be7698e41efa 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1895,33 +1895,31 @@ mod verify { generate_unchecked_math_harness!(u128, unchecked_sub, checked_unchecked_sub_u128); generate_unchecked_math_harness!(usize, unchecked_sub, checked_unchecked_sub_usize); - + // Part2 `carrying_mul` proofs + // // ====================== u8 Harnesses ====================== - /// Kani proof harness for `carrying_mul` on `u8` type with small values. + /// Kani proof harness for `carrying_mul` on `u8` type with full range of values. generate_carrying_mul_intervals!(u8, u16, - carrying_mul_u8_small, 0u8, 10u8, - carrying_mul_u8_large, u8::MAX - 10u8, u8::MAX + carrying_mul_u8_full_range, 0u8, u8::MAX ); // ====================== u16 Harnesses ====================== + /// Kani proof harness for `carrying_mul` on `u16` type with full range of values. generate_carrying_mul_intervals!(u16, u32, - carrying_mul_u16_small, 0u16, 10u16, - carrying_mul_u16_large, u16::MAX - 10u16, u16::MAX + carrying_mul_u16_full_range, 0u16, u16::MAX ); // ====================== u32 Harnesses ====================== + /// Kani proof harness for `carrying_mul` on `u32` type with full range of values. generate_carrying_mul_intervals!(u32, u64, - carrying_mul_u32_small, 0u32, 10u32, - carrying_mul_u32_large, u32::MAX - 10u32, u32::MAX + carrying_mul_u32_full_range, 0u32, u32::MAX ); // ====================== u64 Harnesses ====================== + /// Kani proof harness for `carrying_mul` on `u64` type with full range of values. generate_carrying_mul_intervals!(u64, u128, - carrying_mul_u64_small, 0u64, 10u64, - carrying_mul_u64_large, u64::MAX - 10u64, u64::MAX + carrying_mul_u64_full_range, 0u64, u64::MAX ); - - } From 71399d8da51cef0b2211f6e55c02217569408eda Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Sat, 19 Oct 2024 23:17:06 -0700 Subject: [PATCH 48/67] reduced the interval for carrying_mul u32,u64 --- library/core/src/num/mod.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 9be7698e41efa..81cfa52b16d3e 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1910,16 +1910,17 @@ mod verify { ); // ====================== u32 Harnesses ====================== - /// Kani proof harness for `carrying_mul` on `u32` type with full range of values. generate_carrying_mul_intervals!(u32, u64, - carrying_mul_u32_full_range, 0u32, u32::MAX + carrying_mul_u32_small, 0u32, 10u32, + carrying_mul_u32_large, u32::MAX - 10u32, u32::MAX, + carrying_mul_u32_mid_edge, (u32::MAX / 2) - 10u32, (u32::MAX / 2) + 10u32 ); // ====================== u64 Harnesses ====================== - /// Kani proof harness for `carrying_mul` on `u64` type with full range of values. generate_carrying_mul_intervals!(u64, u128, - carrying_mul_u64_full_range, 0u64, u64::MAX + carrying_mul_u64_small, 0u64, 10u64, + carrying_mul_u64_large, u64::MAX - 10u64, u64::MAX, + carrying_mul_u64_mid_edge, (u64::MAX / 2) - 10u64, (u64::MAX / 2) + 10u64 ); - } From 95d11f330b67d440ad0ccff4f9a4b375d3cccba9 Mon Sep 17 00:00:00 2001 From: Yenyun035 Date: Tue, 22 Oct 2024 09:46:48 -0700 Subject: [PATCH 49/67] f32 test harness & contracts --- library/core/src/num/f32.rs | 7 +++++++ library/core/src/num/mod.rs | 12 +++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index c1adcc753f2e5..2b17d327c2f04 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -17,6 +17,11 @@ use crate::intrinsics; use crate::mem; use crate::num::FpCategory; +use safety::{requires, ensures}; + +#[cfg(kani)] +use crate::kani; + /// The radix or base of the internal representation of `f32`. /// Use [`f32::RADIX`] instead. /// @@ -1086,6 +1091,8 @@ impl f32 { without modifying the original"] #[stable(feature = "float_approx_unchecked_to", since = "1.44.0")] #[inline] + #[requires(!self.is_nan() && !self.is_infinite())] + #[requires(self >= Self::MIN && self <= Self::MAX)] pub unsafe fn to_int_unchecked(self) -> Int where Self: FloatToInt, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index b123d998a8466..64232558c7a33 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1590,7 +1590,7 @@ from_str_radix_size_impl! { i64 isize, u64 usize } #[unstable(feature = "kani", issue = "none")] mod verify { use super::*; - +/* // Verify `unchecked_{add, sub, mul}` macro_rules! generate_unchecked_math_harness { ($type:ty, $method:ident, $harness_name:ident) => { @@ -1967,4 +1967,14 @@ mod verify { generate_wrapping_shift_harness!(u64, wrapping_shr, checked_wrapping_shr_u64); generate_wrapping_shift_harness!(u128, wrapping_shr, checked_wrapping_shr_u128); generate_wrapping_shift_harness!(usize, wrapping_shr, checked_wrapping_shr_usize); +*/ + + #[kani::proof_for_contract(f32::to_int_unchecked)] + pub fn checked_to_int_unchecked_f32() { + let num1: f32 = kani::any::(); + + let result = unsafe { num1.to_int_unchecked::() }; + + assert_eq!(result, num1 as i32); + } } From 227d9ac7886258bfb62ee42ef87eab0bff92451c Mon Sep 17 00:00:00 2001 From: Yenyun035 Date: Wed, 23 Oct 2024 21:02:27 -0700 Subject: [PATCH 50/67] Fix contracts && Initial draft of macro and harnesses --- library/core/src/num/f32.rs | 4 +-- library/core/src/num/mod.rs | 53 ++++++++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 2b17d327c2f04..df21835cbae20 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1091,8 +1091,8 @@ impl f32 { without modifying the original"] #[stable(feature = "float_approx_unchecked_to", since = "1.44.0")] #[inline] - #[requires(!self.is_nan() && !self.is_infinite())] - #[requires(self >= Self::MIN && self <= Self::MAX)] + // is_finite() checks if the given float is neither infinite nor NaN. + #[requires(self.is_finite() && self >= Self::MIN && self <= Self::MAX)] pub unsafe fn to_int_unchecked(self) -> Int where Self: FloatToInt, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 64232558c7a33..8cdade6d3afb5 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1590,7 +1590,7 @@ from_str_radix_size_impl! { i64 isize, u64 usize } #[unstable(feature = "kani", issue = "none")] mod verify { use super::*; -/* + // Verify `unchecked_{add, sub, mul}` macro_rules! generate_unchecked_math_harness { ($type:ty, $method:ident, $harness_name:ident) => { @@ -1700,6 +1700,21 @@ mod verify { } } + // Part 3: Float to Integer Conversion function Harness Generation Macros + macro_rules! generate_to_int_unchecked_harness { + ($floatType:ty, $($intType:ty, $harness_name:ident),+) => { + $( + #[kani::proof_for_contract($floatType::to_int_unchecked)] + pub fn $harness_name() { + let num1: $floatType = kani::any::<$floatType>(); + let result = unsafe { num1.to_int_unchecked::<$intType>() }; + + assert_eq!(result, num1 as $intType); + } + )+ + } + } + // `unchecked_add` proofs // // Target types: @@ -1967,14 +1982,32 @@ mod verify { generate_wrapping_shift_harness!(u64, wrapping_shr, checked_wrapping_shr_u64); generate_wrapping_shift_harness!(u128, wrapping_shr, checked_wrapping_shr_u128); generate_wrapping_shift_harness!(usize, wrapping_shr, checked_wrapping_shr_usize); -*/ - - #[kani::proof_for_contract(f32::to_int_unchecked)] - pub fn checked_to_int_unchecked_f32() { - let num1: f32 = kani::any::(); - let result = unsafe { num1.to_int_unchecked::() }; - - assert_eq!(result, num1 as i32); - } + // `f32::to_int_unchecked` proofs + // + // Target integer types: + // i{8,16,32,64,128,size} and u{8,16,32,64,128,size} -- 12 types in total + // + // Target contracts: + // 1. Float is not `NaN` and infinite + // 2. Float is representable in the return type `Int`, after truncating + // off its fractional part + // [requires(self.is_finite() && self >= Self::MIN && self <= Self::MAX)] + // + // Target function: + // pub unsafe fn to_int_unchecked(self) -> Int where Self: FloatToInt + generate_to_int_unchecked_harness!(f32, + i8, checked_f32_to_int_unchecked_i8, + i16, checked_f32_to_int_unchecked_i16, + i32, checked_f32_to_int_unchecked_i32, + i64, checked_f32_to_int_unchecked_i64, + i128, checked_f32_to_int_unchecked_i128, + isize, checked_f32_to_int_unchecked_isize, + u8, checked_f32_to_int_unchecked_u8, + u16, checked_f32_to_int_unchecked_u16, + u32, checked_f32_to_int_unchecked_u32, + u64, checked_f32_to_int_unchecked_u64, + u128, checked_f32_to_int_unchecked_u128, + usize, checked_f32_to_int_unchecked_usize + ); } From 082ea5ade17ca30fc01144d9818f6c243b882b19 Mon Sep 17 00:00:00 2001 From: rajathmCMU Date: Wed, 30 Oct 2024 00:39:47 -0700 Subject: [PATCH 51/67] f128_float_to_int proofs --- library/core/src/num/f128.rs | 5 +++++ library/core/src/num/mod.rs | 30 +++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index d4236e47bfe3b..47098a8fdd5c8 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -16,6 +16,9 @@ use crate::convert::FloatToInt; use crate::intrinsics; use crate::mem; use crate::num::FpCategory; +use safety::{requires, ensures}; +#[cfg(kani)] +use crate::kani; /// Basic mathematical constants. #[unstable(feature = "f128", issue = "116909")] @@ -886,6 +889,8 @@ impl f128 { #[inline] #[unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] + // is_finite() checks if the given float is neither infinite nor NaN. + #[requires(self.is_finite() && self >= Self::MIN && self <= Self::MAX)] pub unsafe fn to_int_unchecked(self) -> Int where Self: FloatToInt, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 8987bc58f1283..3d0634aa2c2b7 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -2084,4 +2084,32 @@ mod verify { u128, checked_f32_to_int_unchecked_u128, usize, checked_f32_to_int_unchecked_usize ); -} \ No newline at end of file + + // `f128::to_int_unchecked` proofs + // + // Target integer types: + // i{8,16,32,64,128,size} and u{8,16,32,64,128,size} -- 12 types in total + // + // Target contracts: + // 1. Float is not `NaN` or infinite + // 2. Float is representable in the return type `Int`, after truncating + // off its fractional part + // [requires(self.is_finite() && self >= Self::MIN && self <= Self::MAX)] + // + // Target function: + // pub unsafe fn to_int_unchecked(self) -> Int where Self: FloatToInt + generate_to_int_unchecked_harness!(f128, + i8, checked_f128_to_int_unchecked_i8, + i16, checked_f128_to_int_unchecked_i16, + i32, checked_f128_to_int_unchecked_i32, + i64, checked_f128_to_int_unchecked_i64, + i128, checked_f128_to_int_unchecked_i128, + isize, checked_f128_to_int_unchecked_isize, + u8, checked_f128_to_int_unchecked_u8, + u16, checked_f128_to_int_unchecked_u16, + u32, checked_f128_to_int_unchecked_u32, + u64, checked_f128_to_int_unchecked_u64, + u128, checked_f128_to_int_unchecked_u128, + usize, checked_f128_to_int_unchecked_usize + ); +} From bb00a726a36242f9fc8fcb1fadb292732173ebd4 Mon Sep 17 00:00:00 2001 From: Yenyun035 Date: Wed, 30 Oct 2024 09:19:55 -0700 Subject: [PATCH 52/67] Fix comment --- library/core/src/num/mod.rs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 3d0634aa2c2b7..7b1375eef1b1b 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -2057,7 +2057,7 @@ mod verify { generate_wrapping_shift_harness!(u128, wrapping_shr, checked_wrapping_shr_u128); generate_wrapping_shift_harness!(usize, wrapping_shr, checked_wrapping_shr_usize); - // `f32::to_int_unchecked` proofs + // `f{16,32,64,128}::to_int_unchecked` proofs // // Target integer types: // i{8,16,32,64,128,size} and u{8,16,32,64,128,size} -- 12 types in total @@ -2085,19 +2085,6 @@ mod verify { usize, checked_f32_to_int_unchecked_usize ); - // `f128::to_int_unchecked` proofs - // - // Target integer types: - // i{8,16,32,64,128,size} and u{8,16,32,64,128,size} -- 12 types in total - // - // Target contracts: - // 1. Float is not `NaN` or infinite - // 2. Float is representable in the return type `Int`, after truncating - // off its fractional part - // [requires(self.is_finite() && self >= Self::MIN && self <= Self::MAX)] - // - // Target function: - // pub unsafe fn to_int_unchecked(self) -> Int where Self: FloatToInt generate_to_int_unchecked_harness!(f128, i8, checked_f128_to_int_unchecked_i8, i16, checked_f128_to_int_unchecked_i16, From 3e9613b7bb143359589623f6f786e6f9d4a36b1e Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Fri, 1 Nov 2024 23:59:52 -0700 Subject: [PATCH 53/67] added the contracts and harnesses for f16-int_unchecked --- kani | 1 + library/core/src/num/f16.rs | 7 ++ library/core/src/num/mod.rs | 243 +++++++++++++++++++++++++++++++++--- 3 files changed, 233 insertions(+), 18 deletions(-) create mode 160000 kani diff --git a/kani b/kani new file mode 160000 index 0000000000000..5b03a9f3bafeb --- /dev/null +++ b/kani @@ -0,0 +1 @@ +Subproject commit 5b03a9f3bafeb3d4087e75a8411ac56face49e85 diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 1e2f841aca733..b97c8ed72ad86 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -17,6 +17,11 @@ use crate::intrinsics; use crate::mem; use crate::num::FpCategory; +use safety::{requires, ensures}; + +#[cfg(kani)] +use crate::kani; + /// Basic mathematical constants. #[unstable(feature = "f16", issue = "116909")] pub mod consts { @@ -893,6 +898,8 @@ impl f16 { /// * Not be infinite /// * Be representable in the return type `Int`, after truncating off its fractional part #[inline] + #[requires(!self.is_nan() && !self.is_infinite())] + #[requires(self >= Self::MIN && self <= Self::MAX)] #[unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub unsafe fn to_int_unchecked(self) -> Int diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 3f747ffd64551..e4ab2e9b6672e 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1657,7 +1657,18 @@ mod verify { } } } - + + /// A macro to generate Kani proof harnesses for the `carrying_mul` method, + /// + /// The macro creates multiple harnesses for different ranges of input values, + /// allowing testing of both small and large inputs. + /// + /// # Parameters: + /// - `$type`: The integer type (e.g., u8, u16) for which the `carrying_mul` function is being tested. + /// - `$wide_type`: A wider type to simulate the multiplication (e.g., u16 for u8, u32 for u16). + /// - `$harness_name`: The name of the Kani harness to be generated. + /// - `$min`: The minimum value for the range of inputs for `lhs`, `rhs`, and `carry_in`. + /// - `$max`: The maximum value for the range of inputs for `lhs`, `rhs`, and `carry_in`. macro_rules! generate_carrying_mul_intervals { ($type:ty, $wide_type:ty, $($harness_name:ident, $min:expr, $max:expr),+) => { $( @@ -1689,6 +1700,65 @@ mod verify { )+ } } + + + + // Part 2 : Nested unsafe functions Generation Macros --> https://github.com/verify-rust-std/blob/main/doc/src/challenges/0011-floats-ints.md + + // Verify `widening_mul`, which internally uses `unchecked_mul` + macro_rules! generate_widening_mul_intervals { + ($type:ty, $wide_type:ty, $($harness_name:ident, $min:expr, $max:expr),+) => { + $( + #[kani::proof] + pub fn $harness_name() { + let lhs: $type = kani::any::<$type>(); + let rhs: $type = kani::any::<$type>(); + + kani::assume(lhs >= $min && lhs <= $max); + kani::assume(rhs >= $min && rhs <= $max); + + let (result_low, result_high) = lhs.widening_mul(rhs); + + // Compute expected result using wider type + let expected = (lhs as $wide_type) * (rhs as $wide_type); + + let expected_low = expected as $type; + let expected_high = (expected >> <$type>::BITS) as $type; + + assert_eq!(result_low, expected_low); + assert_eq!(result_high, expected_high); + } + )+ + } + } + + // Verify `wrapping_{shl, shr}` which internally uses `unchecked_{shl,shr}` + macro_rules! generate_wrapping_shift_harness { + ($type:ty, $method:ident, $harness_name:ident) => { + #[kani::proof_for_contract($type::$method)] + pub fn $harness_name() { + let num1: $type = kani::any::<$type>(); + let num2: u32 = kani::any::(); + + let _ = num1.$method(num2); + } + } + } + + // Part 3: Float to Integer Conversion function Harness Generation Macros + macro_rules! generate_to_int_unchecked_harness { + ($floatType:ty, $($intType:ty, $harness_name:ident),+) => { + $( + #[kani::proof_for_contract($floatType::to_int_unchecked)] + pub fn $harness_name() { + let num1: $floatType = kani::any::<$floatType>(); + let result = unsafe { num1.to_int_unchecked::<$intType>() }; + + assert_eq!(result, num1 as $intType); + } + )+ + } + } // `unchecked_add` proofs // @@ -1731,7 +1801,7 @@ mod verify { generate_unchecked_neg_harness!(i128, checked_unchecked_neg_i128); generate_unchecked_neg_harness!(isize, checked_unchecked_neg_isize); - // unchecked_mul proofs + // `unchecked_mul` proofs // // Target types: // i{8,16,32,64,128,size} and u{8,16,32,64,128,size} -- 12 types in total, with different interval checks for each. @@ -1883,23 +1953,53 @@ mod verify { generate_unchecked_math_harness!(usize, unchecked_sub, checked_unchecked_sub_usize); -// ====================== u8 Harnesses ====================== -generate_carrying_mul_intervals!(u8, u16, - carrying_mul_u8_small, 0u8, 10u8, - carrying_mul_u8_large, u8::MAX - 10u8, u8::MAX -); + // Part_2 `carrying_mul` proofs + // + // ====================== u8 Harnesses ====================== + /// Kani proof harness for `carrying_mul` on `u8` type with full range of values. + generate_carrying_mul_intervals!(u8, u16, + carrying_mul_u8_full_range, 0u8, u8::MAX + ); + + // ====================== u16 Harnesses ====================== + /// Kani proof harness for `carrying_mul` on `u16` type with full range of values. + generate_carrying_mul_intervals!(u16, u32, + carrying_mul_u16_full_range, 0u16, u16::MAX + ); + + // ====================== u32 Harnesses ====================== + generate_carrying_mul_intervals!(u32, u64, + carrying_mul_u32_small, 0u32, 10u32, + carrying_mul_u32_large, u32::MAX - 10u32, u32::MAX, + carrying_mul_u32_mid_edge, (u32::MAX / 2) - 10u32, (u32::MAX / 2) + 10u32 + ); -// ====================== u16 Harnesses ====================== -generate_carrying_mul_intervals!(u16, u32, - carrying_mul_u16_small, 0u16, 10u16, - carrying_mul_u16_large, u16::MAX - 10u16, u16::MAX -); + // ====================== u64 Harnesses ====================== + generate_carrying_mul_intervals!(u64, u128, + carrying_mul_u64_small, 0u64, 10u64, + carrying_mul_u64_large, u64::MAX - 10u64, u64::MAX, + carrying_mul_u64_mid_edge, (u64::MAX / 2) - 10u64, (u64::MAX / 2) + 10u64 + ); + + + // Part_2 `widening_mul` proofs + + // ====================== u8 Harnesses ====================== + generate_widening_mul_intervals!(u8, u16, widening_mul_u8, 0u8, u8::MAX); + + // ====================== u16 Harnesses ====================== + generate_widening_mul_intervals!(u16, u32, + widening_mul_u16_small, 0u16, 10u16, + widening_mul_u16_large, u16::MAX - 10u16, u16::MAX, + widening_mul_u16_mid_edge, (u16::MAX / 2) - 10u16, (u16::MAX / 2) + 10u16 + ); -// ====================== u32 Harnesses ====================== -generate_carrying_mul_intervals!(u32, u64, - carrying_mul_u32_small, 0u32, 10u32, - carrying_mul_u32_large, u32::MAX - 10u32, u32::MAX -); + // ====================== u32 Harnesses ====================== + generate_widening_mul_intervals!(u32, u64, + widening_mul_u32_small, 0u32, 10u32, + widening_mul_u32_large, u32::MAX - 10u32, u32::MAX, + widening_mul_u32_mid_edge, (u32::MAX / 2) - 10u32, (u32::MAX / 2) + 10u32 + ); // ====================== u64 Harnesses ====================== generate_widening_mul_intervals!(u64, u128, @@ -1907,4 +2007,111 @@ generate_carrying_mul_intervals!(u32, u64, widening_mul_u64_large, u64::MAX - 10u64, u64::MAX, widening_mul_u64_mid_edge, (u64::MAX / 2) - 10u64, (u64::MAX / 2) + 10u64 ); -} + + // Part_2 `wrapping_shl` proofs + // + // Target types: + // i{8,16,32,64,128,size} and u{8,16,32,64,128,size} -- 12 types in total + // + // Target contracts: + // #[ensures(|result| *result == self << (rhs & (Self::BITS - 1)))] + // + // Target function: + // pub const fn wrapping_shl(self, rhs: u32) -> Self + // + // This function performs an panic-free bitwise left shift operation. + generate_wrapping_shift_harness!(i8, wrapping_shl, checked_wrapping_shl_i8); + generate_wrapping_shift_harness!(i16, wrapping_shl, checked_wrapping_shl_i16); + generate_wrapping_shift_harness!(i32, wrapping_shl, checked_wrapping_shl_i32); + generate_wrapping_shift_harness!(i64, wrapping_shl, checked_wrapping_shl_i64); + generate_wrapping_shift_harness!(i128, wrapping_shl, checked_wrapping_shl_i128); + generate_wrapping_shift_harness!(isize, wrapping_shl, checked_wrapping_shl_isize); + generate_wrapping_shift_harness!(u8, wrapping_shl, checked_wrapping_shl_u8); + generate_wrapping_shift_harness!(u16, wrapping_shl, checked_wrapping_shl_u16); + generate_wrapping_shift_harness!(u32, wrapping_shl, checked_wrapping_shl_u32); + generate_wrapping_shift_harness!(u64, wrapping_shl, checked_wrapping_shl_u64); + generate_wrapping_shift_harness!(u128, wrapping_shl, checked_wrapping_shl_u128); + generate_wrapping_shift_harness!(usize, wrapping_shl, checked_wrapping_shl_usize); + + // Part_2 `wrapping_shr` proofs + // + // Target types: + // i{8,16,32,64,128,size} and u{8,16,32,64,128,size} -- 12 types in total + // + // Target contracts: + // #[ensures(|result| *result == self >> (rhs & (Self::BITS - 1)))] + // Target function: + // pub const fn wrapping_shr(self, rhs: u32) -> Self { + // + // This function performs an panic-free bitwise right shift operation. + generate_wrapping_shift_harness!(i8, wrapping_shr, checked_wrapping_shr_i8); + generate_wrapping_shift_harness!(i16, wrapping_shr, checked_wrapping_shr_i16); + generate_wrapping_shift_harness!(i32, wrapping_shr, checked_wrapping_shr_i32); + generate_wrapping_shift_harness!(i64, wrapping_shr, checked_wrapping_shr_i64); + generate_wrapping_shift_harness!(i128, wrapping_shr, checked_wrapping_shr_i128); + generate_wrapping_shift_harness!(isize, wrapping_shr, checked_wrapping_shr_isize); + generate_wrapping_shift_harness!(u8, wrapping_shr, checked_wrapping_shr_u8); + generate_wrapping_shift_harness!(u16, wrapping_shr, checked_wrapping_shr_u16); + generate_wrapping_shift_harness!(u32, wrapping_shr, checked_wrapping_shr_u32); + generate_wrapping_shift_harness!(u64, wrapping_shr, checked_wrapping_shr_u64); + generate_wrapping_shift_harness!(u128, wrapping_shr, checked_wrapping_shr_u128); + generate_wrapping_shift_harness!(usize, wrapping_shr, checked_wrapping_shr_usize); + + // `f{16,32,64,128}::to_int_unchecked` proofs + // + // Target integer types: + // i{8,16,32,64,128,size} and u{8,16,32,64,128,size} -- 12 types in total + // + // Target contracts: + // 1. Float is not `NaN` and infinite + // 2. Float is representable in the return type `Int`, after truncating + // off its fractional part + // [requires(self.is_finite() && self >= Self::MIN && self <= Self::MAX)] + // + // Target function: + // pub unsafe fn to_int_unchecked(self) -> Int where Self: FloatToInt + generate_to_int_unchecked_harness!(f16, + i8, checked_f128_to_int_unchecked_i8, + i16, checked_f128_to_int_unchecked_i16, + i32, checked_f128_to_int_unchecked_i32, + i64, checked_f128_to_int_unchecked_i64, + i128, checked_f128_to_int_unchecked_i128, + isize, checked_f128_to_int_unchecked_isize, + u8, checked_f128_to_int_unchecked_u8, + u16, checked_f128_to_int_unchecked_u16, + u32, checked_f128_to_int_unchecked_u32, + u64, checked_f128_to_int_unchecked_u64, + u128, checked_f128_to_int_unchecked_u128, + usize, checked_f128_to_int_unchecked_usize + ); + + generate_to_int_unchecked_harness!(f32, + i8, checked_f32_to_int_unchecked_i8, + i16, checked_f32_to_int_unchecked_i16, + i32, checked_f32_to_int_unchecked_i32, + i64, checked_f32_to_int_unchecked_i64, + i128, checked_f32_to_int_unchecked_i128, + isize, checked_f32_to_int_unchecked_isize, + u8, checked_f32_to_int_unchecked_u8, + u16, checked_f32_to_int_unchecked_u16, + u32, checked_f32_to_int_unchecked_u32, + u64, checked_f32_to_int_unchecked_u64, + u128, checked_f32_to_int_unchecked_u128, + usize, checked_f32_to_int_unchecked_usize + ); + + generate_to_int_unchecked_harness!(f128, + i8, checked_f128_to_int_unchecked_i8, + i16, checked_f128_to_int_unchecked_i16, + i32, checked_f128_to_int_unchecked_i32, + i64, checked_f128_to_int_unchecked_i64, + i128, checked_f128_to_int_unchecked_i128, + isize, checked_f128_to_int_unchecked_isize, + u8, checked_f128_to_int_unchecked_u8, + u16, checked_f128_to_int_unchecked_u16, + u32, checked_f128_to_int_unchecked_u32, + u64, checked_f128_to_int_unchecked_u64, + u128, checked_f128_to_int_unchecked_u128, + usize, checked_f128_to_int_unchecked_usize + ); +} \ No newline at end of file From bf613a0305a0439c64a517c35edc6605caf116ed Mon Sep 17 00:00:00 2001 From: Yenyun035 Date: Thu, 21 Nov 2024 22:32:57 -0800 Subject: [PATCH 54/67] Test CStr safety invariant --- library/core/src/ffi/c_str.rs | 8 ++++++++ library/core/src/ffi/mod.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 93dd351b02958..70c52e1cbda0c 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -9,6 +9,11 @@ use crate::ptr::NonNull; use crate::slice::memchr; use crate::{fmt, intrinsics, ops, slice, str}; +// use safety::{requires, ensures}; + +#[cfg(kani)] +use crate::kani; + // FIXME: because this is doc(inline)d, we *have* to use intra-doc links because the actual link // depends on where the item is being documented. however, since this is libcore, we can't // actually reference libstd or liballoc in intra-doc links. so, the best we can do is remove the @@ -100,6 +105,9 @@ use crate::{fmt, intrinsics, ops, slice, str}; // want `repr(transparent)` but we don't want it to show up in rustdoc, so we hide it under // `cfg(doc)`. This is an ad-hoc implementation of attribute privacy. #[repr(transparent)] +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +#[safety_constraint(inner.len() > 0 && inner[inner.len() - 1] == 0 && !inner[..inner.len() - 1].contains(&0))] pub struct CStr { // FIXME: this should not be represented with a DST slice but rather with // just a raw `c_char` along with some form of marker to make diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index dc107c5d22cdd..38b9974420bd5 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -20,6 +20,11 @@ pub use self::c_str::FromBytesUntilNulError; pub use self::c_str::FromBytesWithNulError; use crate::fmt; +use safety::{requires, ensures}; + +#[cfg(kani)] +use crate::kani; + #[unstable(feature = "c_str_module", issue = "112134")] pub mod c_str; @@ -223,3 +228,24 @@ impl fmt::Debug for c_void { )] #[link(name = "/defaultlib:libcmt", modifiers = "+verbatim", cfg(target_feature = "crt-static"))] extern "C" {} + +#[cfg(kani)] +#[unstable(feature = "kani", issue = "none")] +mod verify { + use super::*; + + // pub const fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> + #[kani::proof] + #[kani::unwind(16)] // Proof bounded by array length + fn check_from_bytes_until_nul() { + const ARR_LEN: usize = 16; + let mut string: [u8; ARR_LEN] = kani::any(); + // ensure that there is at least one null byte + let idx: usize = kani::any_where(|x| *x >= 0 && *x < ARR_LEN); + string[idx] = 0; + + let c_str = CStr::from_bytes_until_nul(&string).unwrap(); + assert!(c_str.is_safe()); + } + +} \ No newline at end of file From 90ae7a5ccc9ac866056f5355fdf9325e5d8d6eb0 Mon Sep 17 00:00:00 2001 From: Yenyun035 Date: Fri, 22 Nov 2024 13:00:28 -0800 Subject: [PATCH 55/67] Add CStr safety invariant && from_bytes_until_null harnesses --- library/core/src/ffi/c_str.rs | 67 +++++++++++++++++++++++++++++++++-- library/core/src/ffi/mod.rs | 26 -------------- 2 files changed, 64 insertions(+), 29 deletions(-) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 70c52e1cbda0c..5be4c2054809e 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -10,6 +10,7 @@ use crate::slice::memchr; use crate::{fmt, intrinsics, ops, slice, str}; // use safety::{requires, ensures}; +use crate::ub_checks::Invariant; #[cfg(kani)] use crate::kani; @@ -105,9 +106,6 @@ use crate::kani; // want `repr(transparent)` but we don't want it to show up in rustdoc, so we hide it under // `cfg(doc)`. This is an ad-hoc implementation of attribute privacy. #[repr(transparent)] -#[derive(kani::Arbitrary)] -#[derive(kani::Invariant)] -#[safety_constraint(inner.len() > 0 && inner[inner.len() - 1] == 0 && !inner[..inner.len() - 1].contains(&0))] pub struct CStr { // FIXME: this should not be represented with a DST slice but rather with // just a raw `c_char` along with some form of marker to make @@ -215,6 +213,23 @@ impl fmt::Display for FromBytesWithNulError { } } +#[unstable(feature = "ub_checks", issue = "none")] +impl Invariant for &CStr { + fn is_safe(&self) -> bool { + let bytes: &[c_char] = &self.inner; + let len = bytes.len(); + + // An empty CStr should has a null byte. + // A valid CStr should end with a null-terminator and contains + // no intermediate null bytes. + if bytes.is_empty() || bytes[len - 1] != 0 || bytes[..len-1].contains(&0) { + return false; + } + + true + } +} + impl CStr { /// Wraps a raw C string with a safe C string wrapper. /// @@ -841,3 +856,49 @@ impl Iterator for Bytes<'_> { #[unstable(feature = "cstr_bytes", issue = "112115")] impl FusedIterator for Bytes<'_> {} + +#[cfg(kani)] +#[unstable(feature = "kani", issue = "none")] +mod verify { + use super::*; + + // pub const fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> + #[kani::proof] + #[kani::unwind(32)] // Proof bounded by array length + fn check_from_bytes_until_nul_random_nul_byte() { + const ARR_LEN: usize = 1000; + let mut string: [u8; ARR_LEN] = kani::any(); + + // ensure that there is at least one null byte + let idx: usize = kani::any_where(|x: &usize| *x >= 0 && *x < ARR_LEN); + string[idx] = 0; + + let c_str = CStr::from_bytes_until_nul(&string).unwrap(); + assert!(c_str.is_safe()); + } + + #[kani::proof] + #[kani::unwind(32)] // Proof bounded by array length + fn check_from_bytes_until_nul_single_nul_byte_end() { + const ARR_LEN: usize = 1000; + // ensure that the string does not have intermediate null bytes + let mut string: [u8; ARR_LEN] = kani::any_where(|x: &[u8; ARR_LEN]| !x[..ARR_LEN-1].contains(&0)); + // ensure that the string is properly null-terminated + string[ARR_LEN - 1] = 0; + + let c_str = CStr::from_bytes_until_nul(&string).unwrap(); + assert!(c_str.is_safe()); + } + + #[kani::proof] + #[kani::unwind(8)] // Proof bounded by array length + fn check_from_bytes_until_nul_single_nul_byte_head() { + const ARR_LEN: usize = 8; + let mut string: [u8; ARR_LEN] = kani::any(); + // The first byte is a null byte should result in an empty CStr. + string[0] = 0; + + let c_str = CStr::from_bytes_until_nul(&string).unwrap(); + assert!(c_str.is_safe()); + } +} \ No newline at end of file diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 38b9974420bd5..dc107c5d22cdd 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -20,11 +20,6 @@ pub use self::c_str::FromBytesUntilNulError; pub use self::c_str::FromBytesWithNulError; use crate::fmt; -use safety::{requires, ensures}; - -#[cfg(kani)] -use crate::kani; - #[unstable(feature = "c_str_module", issue = "112134")] pub mod c_str; @@ -228,24 +223,3 @@ impl fmt::Debug for c_void { )] #[link(name = "/defaultlib:libcmt", modifiers = "+verbatim", cfg(target_feature = "crt-static"))] extern "C" {} - -#[cfg(kani)] -#[unstable(feature = "kani", issue = "none")] -mod verify { - use super::*; - - // pub const fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> - #[kani::proof] - #[kani::unwind(16)] // Proof bounded by array length - fn check_from_bytes_until_nul() { - const ARR_LEN: usize = 16; - let mut string: [u8; ARR_LEN] = kani::any(); - // ensure that there is at least one null byte - let idx: usize = kani::any_where(|x| *x >= 0 && *x < ARR_LEN); - string[idx] = 0; - - let c_str = CStr::from_bytes_until_nul(&string).unwrap(); - assert!(c_str.is_safe()); - } - -} \ No newline at end of file From 55dfaaf031fffc47e65ef5004831a9099e2a5e5e Mon Sep 17 00:00:00 2001 From: Yenyun035 Date: Fri, 22 Nov 2024 14:43:55 -0800 Subject: [PATCH 56/67] Add comments --- library/core/src/ffi/c_str.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 5be4c2054809e..44bd0a2d061ee 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -863,10 +863,18 @@ mod verify { use super::*; // pub const fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> + // Check 1: A random index in a u8 array is guaranteed to be a null byte + // Check 2: Only the last byte of a u8 array is a null byte + // Check 3: The first byte of a u8 array is a null byte + // + // Proofs are bounded (kani::unwind) by the length of the input array. + // Check 1: 2.08 sec when 32; 7.49 sec when 40 + // Check 2: 6.72 sec when 32; 9.2 sec when 40 + // Check 3: 0.33 sec when 8; 2.06 sec when 16 #[kani::proof] - #[kani::unwind(32)] // Proof bounded by array length + #[kani::unwind(32)] fn check_from_bytes_until_nul_random_nul_byte() { - const ARR_LEN: usize = 1000; + const ARR_LEN: usize = 32; let mut string: [u8; ARR_LEN] = kani::any(); // ensure that there is at least one null byte @@ -878,10 +886,11 @@ mod verify { } #[kani::proof] - #[kani::unwind(32)] // Proof bounded by array length + #[kani::unwind(32)] fn check_from_bytes_until_nul_single_nul_byte_end() { - const ARR_LEN: usize = 1000; + const ARR_LEN: usize = 32; // ensure that the string does not have intermediate null bytes + // TODO: there might be a better way let mut string: [u8; ARR_LEN] = kani::any_where(|x: &[u8; ARR_LEN]| !x[..ARR_LEN-1].contains(&0)); // ensure that the string is properly null-terminated string[ARR_LEN - 1] = 0; @@ -891,9 +900,9 @@ mod verify { } #[kani::proof] - #[kani::unwind(8)] // Proof bounded by array length + #[kani::unwind(16)] fn check_from_bytes_until_nul_single_nul_byte_head() { - const ARR_LEN: usize = 8; + const ARR_LEN: usize = 16; let mut string: [u8; ARR_LEN] = kani::any(); // The first byte is a null byte should result in an empty CStr. string[0] = 0; From b49f4eee9f2036bb5cdb243b75c2a8705a359dff Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Fri, 22 Nov 2024 21:55:06 -0800 Subject: [PATCH 57/67] merge recent cstr --- kani | 2 +- library/stdarch | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kani b/kani index 5b03a9f3bafeb..8400296f5280b 160000 --- a/kani +++ b/kani @@ -1 +1 @@ -Subproject commit 5b03a9f3bafeb3d4087e75a8411ac56face49e85 +Subproject commit 8400296f5280be4f99820129bc66447e8dff63f4 diff --git a/library/stdarch b/library/stdarch index ff9a4445038ea..d9466edb4c53c 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit ff9a4445038eae46fd095188740946808581bc0e +Subproject commit d9466edb4c53cece8686ee6e17b028436ddf4151 From f03b4188398c75e204c7e5dcdaa5c07a1f1ac15c Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Fri, 22 Nov 2024 22:24:47 -0800 Subject: [PATCH 58/67] added harnesses for is_empty --- library/core/src/ffi/c_str.rs | 73 ++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 44bd0a2d061ee..e80fc95ea1bef 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -910,4 +910,75 @@ mod verify { let c_str = CStr::from_bytes_until_nul(&string).unwrap(); assert!(c_str.is_safe()); } -} \ No newline at end of file + + + + + + + /// Verifies the behavior of the `CStr::is_empty` method. + /// + /// # Purpose + /// This harness ensures that the `is_empty` method behaves correctly under various scenarios: + /// - Identifying valid empty `CStr` instances (only null terminator). + /// - Identifying valid non-empty `CStr` instances (characters followed by a null terminator). + /// - Rejecting invalid `CStr` instances, such as those missing a null terminator or containing interior null bytes. + /// + /// # Test Cases + /// 1. **Valid Empty CStr:** Ensure `is_empty` returns `true` for a `CStr` with only a null terminator. + /// 2. **Valid Non-Empty CStr:** Ensure `is_empty` returns `false` for a `CStr` with characters followed by a null terminator. + /// 3. **Invalid CStr (No Null Terminator):** Verify that constructing a `CStr` without a null terminator fails. + /// 4. **Invalid CStr (Interior Null Byte):** Verify that constructing a `CStr` with interior null bytes fails. + /// + /// # Assumptions + /// - The test harness assumes that the `CStr::from_bytes_with_nul` function rejects invalid `CStr` inputs. + /// + /// # Safety + /// - This harness operates entirely on safe methods (`CStr::from_bytes_with_nul`) for constructing valid `CStr` instances. + /// - Invalid cases are explicitly tested to ensure they do not violate Rust’s safety guarantees. + /// + /// # Kani Integration + /// - The harness uses the Kani verification tool to exhaustively check all possible inputs within defined bounds. + /// + #[kani::proof] + fn check_is_empty() { + // Case 1: Valid Empty CStr + // This test ensures that `is_empty` correctly identifies an empty `CStr` (only null terminator). + // A valid `CStr` with no characters other than the null terminator should return `true`. + let empty_cstr = CStr::from_bytes_with_nul(b"\0").unwrap(); + assert!(empty_cstr.is_empty(), "Expected `is_empty` to return true for an empty CStr."); + + // Case 2: Valid Non-Empty CStr + // This test ensures that `is_empty` correctly identifies a non-empty `CStr`. + // A valid `CStr` with characters followed by a null terminator should return `false`. + let non_empty_cstr = CStr::from_bytes_with_nul(b"hello\0").unwrap(); + assert!(!non_empty_cstr.is_empty(), "Expected `is_empty` to return false for a non-empty CStr."); + + // Case 3: Invalid CStr (No Null Terminator) + // Attempt to create a `CStr` from a byte slice without a null terminator. + // This should fail because the invariant of a null-terminated string is violated. + let invalid_bytes = b"hello"; // No null terminator at the end. + let invalid_result = CStr::from_bytes_with_nul(invalid_bytes); + assert!( + invalid_result.is_err(), + "Expected CStr::from_bytes_with_nul to return an error for a missing null terminator." + ); + + // Case 4: Invalid CStr (Interior Null Byte) + // Attempt to create a `CStr` from a byte slice with an interior null byte. + // This should fail because the invariant of no intermediate null bytes is violated. + let interior_null = b"he\0llo\0"; // Null byte in the middle of the string. + let invalid_result = CStr::from_bytes_with_nul(interior_null); + assert!( + invalid_result.is_err(), + "Expected CStr::from_bytes_with_nul to return an error for interior null bytes." + ); + } + + +} + + + + + From 64ddbfffb110777c62408b642ec554a808474fd9 Mon Sep 17 00:00:00 2001 From: Yenyun035 Date: Wed, 27 Nov 2024 00:00:07 -0800 Subject: [PATCH 59/67] Add to_bytes and to_bytes_with_nul harnesses --- library/core/src/ffi/c_str.rs | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 2a65b2415f7d3..176a760303940 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -875,4 +875,42 @@ mod verify { assert!(c_str.is_safe()); } } + + // pub const fn to_bytes(&self) -> &[u8] + #[kani::proof] + #[kani::unwind(32)] + fn check_to_bytes() { + const MAX_SIZE: usize = 32; + let string: [u8; MAX_SIZE] = kani::any(); + let slice = kani::slice::any_slice_of_array(&string); + + let result = CStr::from_bytes_until_nul(slice); + if let Ok(c_str) = result { + // Find the index of the first null byte in the slice since + // from_bytes_until_nul stops by there + let end_idx = slice.iter().position(|x| *x == 0).unwrap(); + // Comparison does not include the null byte + assert_eq!(c_str.to_bytes(), &slice[..end_idx]); + assert!(c_str.is_safe()); + } + } + + // pub const fn to_bytes_with_nul(&self) -> &[u8] + #[kani::proof] + #[kani::unwind(33)] // 101.7 seconds when 33; 17.9 seconds for 17 + fn check_to_bytes_with_nul() { + const MAX_SIZE: usize = 32; + let string: [u8; MAX_SIZE] = kani::any(); + let slice = kani::slice::any_slice_of_array(&string); + + let result = CStr::from_bytes_until_nul(slice); + if let Ok(c_str) = result { + // Find the index of the first null byte in the slice since + // from_bytes_until_nul stops by there + let end_idx = slice.iter().position(|x| *x == 0).unwrap(); + // Comparison includes the null byte + assert_eq!(c_str.to_bytes_with_nul(), &slice[..end_idx + 1]); + assert!(c_str.is_safe()); + } + } } \ No newline at end of file From 1fc9540895d0c3f12c2e65a589cccdda4c388ebc Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:35:19 -0800 Subject: [PATCH 60/67] combined the is_empty with the latest repo --- library/core/src/ffi/c_str.rs | 259 ++-------------------------------- 1 file changed, 11 insertions(+), 248 deletions(-) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 18636685e93dd..aea139734efa5 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -14,12 +14,6 @@ use crate::ub_checks::Invariant; #[cfg(kani)] use crate::kani; -// use safety::{requires, ensures}; -use crate::ub_checks::Invariant; - -#[cfg(kani)] -use crate::kani; - // FIXME: because this is doc(inline)d, we *have* to use intra-doc links because the actual link // depends on where the item is being documented. however, since this is libcore, we can't // actually reference libstd or liballoc in intra-doc links. so, the best we can do is remove the @@ -867,185 +861,21 @@ mod verify { use super::*; // pub const fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> - // Check 1: A random index in a u8 array is guaranteed to be a null byte - // Check 2: Only the last byte of a u8 array is a null byte - // Check 3: The first byte of a u8 array is a null byte - // - // Proofs are bounded (kani::unwind) by the length of the input array. - // Check 1: 2.08 sec when 32; 7.49 sec when 40 - // Check 2: 6.72 sec when 32; 9.2 sec when 40 - // Check 3: 0.33 sec when 8; 2.06 sec when 16 - #[kani::proof] - #[kani::unwind(32)] - fn check_from_bytes_until_nul_random_nul_byte() { - const ARR_LEN: usize = 32; - let mut string: [u8; ARR_LEN] = kani::any(); - - // ensure that there is at least one null byte - let idx: usize = kani::any_where(|x: &usize| *x >= 0 && *x < ARR_LEN); - string[idx] = 0; - - let c_str = CStr::from_bytes_until_nul(&string).unwrap(); - assert!(c_str.is_safe()); - } - - #[kani::proof] - #[kani::unwind(32)] - fn check_from_bytes_until_nul_single_nul_byte_end() { - const ARR_LEN: usize = 32; - // ensure that the string does not have intermediate null bytes - // TODO: there might be a better way - let mut string: [u8; ARR_LEN] = kani::any_where(|x: &[u8; ARR_LEN]| !x[..ARR_LEN-1].contains(&0)); - // ensure that the string is properly null-terminated - string[ARR_LEN - 1] = 0; - - let c_str = CStr::from_bytes_until_nul(&string).unwrap(); - assert!(c_str.is_safe()); - } - - #[kani::proof] - #[kani::unwind(16)] - fn check_from_bytes_until_nul_single_nul_byte_head() { - const ARR_LEN: usize = 16; - let mut string: [u8; ARR_LEN] = kani::any(); - // The first byte is a null byte should result in an empty CStr. - string[0] = 0; - - let c_str = CStr::from_bytes_until_nul(&string).unwrap(); - assert!(c_str.is_safe()); - } - - - - - - - /// Verifies the behavior of the `CStr::is_empty` method. - /// - /// # Purpose - /// This harness ensures that the `is_empty` method behaves correctly under various scenarios: - /// - Identifying valid empty `CStr` instances (only null terminator). - /// - Identifying valid non-empty `CStr` instances (characters followed by a null terminator). - /// - Rejecting invalid `CStr` instances, such as those missing a null terminator or containing interior null bytes. - /// - /// # Test Cases - /// 1. **Valid Empty CStr:** Ensure `is_empty` returns `true` for a `CStr` with only a null terminator. - /// 2. **Valid Non-Empty CStr:** Ensure `is_empty` returns `false` for a `CStr` with characters followed by a null terminator. - /// 3. **Invalid CStr (No Null Terminator):** Verify that constructing a `CStr` without a null terminator fails. - /// 4. **Invalid CStr (Interior Null Byte):** Verify that constructing a `CStr` with interior null bytes fails. - /// - /// # Assumptions - /// - The test harness assumes that the `CStr::from_bytes_with_nul` function rejects invalid `CStr` inputs. - /// - /// # Safety - /// - This harness operates entirely on safe methods (`CStr::from_bytes_with_nul`) for constructing valid `CStr` instances. - /// - Invalid cases are explicitly tested to ensure they do not violate Rust’s safety guarantees. - /// - /// # Kani Integration - /// - The harness uses the Kani verification tool to exhaustively check all possible inputs within defined bounds. - /// - #[kani::proof] - fn check_is_empty() { - // Case 1: Valid Empty CStr - // This test ensures that `is_empty` correctly identifies an empty `CStr` (only null terminator). - // A valid `CStr` with no characters other than the null terminator should return `true`. - let empty_cstr = CStr::from_bytes_with_nul(b"\0").unwrap(); - assert!(empty_cstr.is_empty(), "Expected `is_empty` to return true for an empty CStr."); - - // Case 2: Valid Non-Empty CStr - // This test ensures that `is_empty` correctly identifies a non-empty `CStr`. - // A valid `CStr` with characters followed by a null terminator should return `false`. - let non_empty_cstr = CStr::from_bytes_with_nul(b"hello\0").unwrap(); - assert!(!non_empty_cstr.is_empty(), "Expected `is_empty` to return false for a non-empty CStr."); - - // Case 3: Invalid CStr (No Null Terminator) - // Attempt to create a `CStr` from a byte slice without a null terminator. - // This should fail because the invariant of a null-terminated string is violated. - let invalid_bytes = b"hello"; // No null terminator at the end. - let invalid_result = CStr::from_bytes_with_nul(invalid_bytes); - assert!( - invalid_result.is_err(), - "Expected CStr::from_bytes_with_nul to return an error for a missing null terminator." - ); - - // Case 4: Invalid CStr (Interior Null Byte) - // Attempt to create a `CStr` from a byte slice with an interior null byte. - // This should fail because the invariant of no intermediate null bytes is violated. - let interior_null = b"he\0llo\0"; // Null byte in the middle of the string. - let invalid_result = CStr::from_bytes_with_nul(interior_null); - assert!( - invalid_result.is_err(), - "Expected CStr::from_bytes_with_nul to return an error for interior null bytes." - ); - } - - -} - - - - - - -#[cfg(kani)] -#[unstable(feature = "kani", issue = "none")] -mod verify { - use super::*; - - // pub const fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> - // Check 1: A random index in a u8 array is guaranteed to be a null byte - // Check 2: Only the last byte of a u8 array is a null byte - // Check 3: The first byte of a u8 array is a null byte - // - // Proofs are bounded (kani::unwind) by the length of the input array. - // Check 1: 2.08 sec when 32; 7.49 sec when 40 - // Check 2: 6.72 sec when 32; 9.2 sec when 40 - // Check 3: 0.33 sec when 8; 2.06 sec when 16 - #[kani::proof] - #[kani::unwind(32)] - fn check_from_bytes_until_nul_random_nul_byte() { - const ARR_LEN: usize = 32; - let mut string: [u8; ARR_LEN] = kani::any(); - - // ensure that there is at least one null byte - let idx: usize = kani::any_where(|x: &usize| *x >= 0 && *x < ARR_LEN); - string[idx] = 0; - - let c_str = CStr::from_bytes_until_nul(&string).unwrap(); - assert!(c_str.is_safe()); - } - #[kani::proof] - #[kani::unwind(32)] - fn check_from_bytes_until_nul_single_nul_byte_end() { - const ARR_LEN: usize = 32; - // ensure that the string does not have intermediate null bytes - // TODO: there might be a better way - let mut string: [u8; ARR_LEN] = kani::any_where(|x: &[u8; ARR_LEN]| !x[..ARR_LEN-1].contains(&0)); - // ensure that the string is properly null-terminated - string[ARR_LEN - 1] = 0; - - let c_str = CStr::from_bytes_until_nul(&string).unwrap(); - assert!(c_str.is_safe()); - } - - #[kani::proof] - #[kani::unwind(16)] - fn check_from_bytes_until_nul_single_nul_byte_head() { - const ARR_LEN: usize = 16; - let mut string: [u8; ARR_LEN] = kani::any(); - // The first byte is a null byte should result in an empty CStr. - string[0] = 0; + #[kani::unwind(32)] // 7.3 seconds when 16; 33.1 seconds when 32 + fn check_from_bytes_until_nul() { + const MAX_SIZE: usize = 32; + let string: [u8; MAX_SIZE] = kani::any(); + // Covers the case of a single null byte at the end, no null bytes, as + // well as intermediate null bytes + let slice = kani::slice::any_slice_of_array(&string); - let c_str = CStr::from_bytes_until_nul(&string).unwrap(); - assert!(c_str.is_safe()); + let result = CStr::from_bytes_until_nul(slice); + if let Ok(c_str) = result { + assert!(c_str.is_safe()); + } } - - - - - /// Verifies the behavior of the `CStr::is_empty` method. /// /// # Purpose @@ -1104,71 +934,4 @@ mod verify { "Expected CStr::from_bytes_with_nul to return an error for interior null bytes." ); } - - -} - - - - - - -#[cfg(kani)] -#[unstable(feature = "kani", issue = "none")] -mod verify { - use super::*; - - // pub const fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> - #[kani::proof] - #[kani::unwind(32)] // 7.3 seconds when 16; 33.1 seconds when 32 - fn check_from_bytes_until_nul() { - const MAX_SIZE: usize = 32; - let string: [u8; MAX_SIZE] = kani::any(); - // Covers the case of a single null byte at the end, no null bytes, as - // well as intermediate null bytes - let slice = kani::slice::any_slice_of_array(&string); - - let result = CStr::from_bytes_until_nul(slice); - if let Ok(c_str) = result { - assert!(c_str.is_safe()); - } - } - - // pub const fn to_bytes(&self) -> &[u8] - #[kani::proof] - #[kani::unwind(32)] - fn check_to_bytes() { - const MAX_SIZE: usize = 32; - let string: [u8; MAX_SIZE] = kani::any(); - let slice = kani::slice::any_slice_of_array(&string); - - let result = CStr::from_bytes_until_nul(slice); - if let Ok(c_str) = result { - // Find the index of the first null byte in the slice since - // from_bytes_until_nul stops by there - let end_idx = slice.iter().position(|x| *x == 0).unwrap(); - // Comparison does not include the null byte - assert_eq!(c_str.to_bytes(), &slice[..end_idx]); - assert!(c_str.is_safe()); - } - } - - // pub const fn to_bytes_with_nul(&self) -> &[u8] - #[kani::proof] - #[kani::unwind(33)] // 101.7 seconds when 33; 17.9 seconds for 17 - fn check_to_bytes_with_nul() { - const MAX_SIZE: usize = 32; - let string: [u8; MAX_SIZE] = kani::any(); - let slice = kani::slice::any_slice_of_array(&string); - - let result = CStr::from_bytes_until_nul(slice); - if let Ok(c_str) = result { - // Find the index of the first null byte in the slice since - // from_bytes_until_nul stops by there - let end_idx = slice.iter().position(|x| *x == 0).unwrap(); - // Comparison includes the null byte - assert_eq!(c_str.to_bytes_with_nul(), &slice[..end_idx + 1]); - assert!(c_str.is_safe()); - } - } } \ No newline at end of file From 7e75de1486ccc11b9ef6832847e09b57906f2a2e Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:24:51 -0800 Subject: [PATCH 61/67] finished testing for is_empty --- library/core/src/ffi/c_str.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index aea139734efa5..3214153d18e58 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -934,4 +934,5 @@ mod verify { "Expected CStr::from_bytes_with_nul to return an error for interior null bytes." ); } + } \ No newline at end of file From ae7af7c65139d259aaf8b1d951697f25b9add832 Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Wed, 27 Nov 2024 19:32:50 -0800 Subject: [PATCH 62/67] used kani_anywhere to generate the values --- library/core/src/ffi/c_str.rs | 96 ++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 3214153d18e58..b9afe46416d48 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -876,63 +876,75 @@ mod verify { } } - /// Verifies the behavior of the `CStr::is_empty` method. + #[kani::proof] + #[kani::unwind(32)] + fn check_count_bytes() { + const MAX_SIZE: usize = 32; + let mut bytes: [u8; MAX_SIZE] = kani::any(); + + // Non-deterministically generate a length within the valid range [0, MAX_SIZE] + let mut len: usize = kani::any_where(|&x| x < MAX_SIZE); + + // If a null byte exists before the generated length + // adjust len to its position + if let Some(pos) = bytes[..len].iter().position(|&x| x == 0) { + len = pos; + } else { + // If no null byte, insert one at the chosen length + bytes[len] = 0; + } + + let c_str = CStr::from_bytes_until_nul(&bytes).unwrap(); + // Verify that count_bytes matches the adjusted length + assert_eq!(c_str.count_bytes(), len); + } + + /// Verifies the `CStr::is_empty` method. /// - /// # Purpose + /// /// This harness ensures that the `is_empty` method behaves correctly under various scenarios: /// - Identifying valid empty `CStr` instances (only null terminator). /// - Identifying valid non-empty `CStr` instances (characters followed by a null terminator). - /// - Rejecting invalid `CStr` instances, such as those missing a null terminator or containing interior null bytes. + /// - Rejecting invalid `CStr` instances: those missing a null terminator or containing interior null bytes. /// /// # Test Cases /// 1. **Valid Empty CStr:** Ensure `is_empty` returns `true` for a `CStr` with only a null terminator. /// 2. **Valid Non-Empty CStr:** Ensure `is_empty` returns `false` for a `CStr` with characters followed by a null terminator. /// 3. **Invalid CStr (No Null Terminator):** Verify that constructing a `CStr` without a null terminator fails. /// 4. **Invalid CStr (Interior Null Byte):** Verify that constructing a `CStr` with interior null bytes fails. - /// - /// # Assumptions - /// - The test harness assumes that the `CStr::from_bytes_with_nul` function rejects invalid `CStr` inputs. - /// - /// # Safety - /// - This harness operates entirely on safe methods (`CStr::from_bytes_with_nul`) for constructing valid `CStr` instances. - /// - Invalid cases are explicitly tested to ensure they do not violate Rust’s safety guarantees. - /// - /// # Kani Integration - /// - The harness uses the Kani verification tool to exhaustively check all possible inputs within defined bounds. - /// #[kani::proof] + #[kani::unwind(32)] fn check_is_empty() { + const MAX_LEN: usize = 32; + // Case 1: Valid Empty CStr - // This test ensures that `is_empty` correctly identifies an empty `CStr` (only null terminator). - // A valid `CStr` with no characters other than the null terminator should return `true`. - let empty_cstr = CStr::from_bytes_with_nul(b"\0").unwrap(); - assert!(empty_cstr.is_empty(), "Expected `is_empty` to return true for an empty CStr."); - + let empty_bytes: [u8; MAX_LEN] = + kani::any_where(|b: &[u8; MAX_LEN]| b[0] == 0 && !b[1..].contains(&0)); + let empty_cstr = CStr::from_bytes_with_nul(&empty_bytes[..1]).unwrap(); + assert!(empty_cstr.is_empty()); + // Case 2: Valid Non-Empty CStr - // This test ensures that `is_empty` correctly identifies a non-empty `CStr`. - // A valid `CStr` with characters followed by a null terminator should return `false`. - let non_empty_cstr = CStr::from_bytes_with_nul(b"hello\0").unwrap(); - assert!(!non_empty_cstr.is_empty(), "Expected `is_empty` to return false for a non-empty CStr."); - + let mut non_empty_bytes: [u8; MAX_LEN] = kani::any(); + let len = kani::any_where(|&l: &usize| l >= 2 && l < MAX_LEN); // Ensure len >= 2 + non_empty_bytes[len - 1] = 0; // Ensure null terminator + non_empty_bytes[..len - 1] + .iter_mut() + .for_each(|b| *b = kani::any_where(|&c: &u8| c != 0)); // Ensure no interior null bytes + let non_empty_cstr = CStr::from_bytes_with_nul(&non_empty_bytes[..len]).unwrap(); + assert!(!non_empty_cstr.is_empty()); + // Case 3: Invalid CStr (No Null Terminator) - // Attempt to create a `CStr` from a byte slice without a null terminator. - // This should fail because the invariant of a null-terminated string is violated. - let invalid_bytes = b"hello"; // No null terminator at the end. - let invalid_result = CStr::from_bytes_with_nul(invalid_bytes); - assert!( - invalid_result.is_err(), - "Expected CStr::from_bytes_with_nul to return an error for a missing null terminator." - ); - + let invalid_bytes: [u8; MAX_LEN] = + kani::any_where(|b: &[u8; MAX_LEN]| !b.contains(&0)); // No null byte + let invalid_result = CStr::from_bytes_with_nul(&invalid_bytes[..MAX_LEN - 1]); // Missing null terminator + assert!(invalid_result.is_err()); + // Case 4: Invalid CStr (Interior Null Byte) - // Attempt to create a `CStr` from a byte slice with an interior null byte. - // This should fail because the invariant of no intermediate null bytes is violated. - let interior_null = b"he\0llo\0"; // Null byte in the middle of the string. - let invalid_result = CStr::from_bytes_with_nul(interior_null); - assert!( - invalid_result.is_err(), - "Expected CStr::from_bytes_with_nul to return an error for interior null bytes." - ); + let mut interior_null_bytes: [u8; MAX_LEN] = kani::any(); + let pos = kani::any_where(|&p: &usize| p < MAX_LEN - 1); // Non-deterministic position for the null byte + interior_null_bytes[pos] = 0; // Insert null byte inside the array + interior_null_bytes[MAX_LEN - 1] = 0; // Ensure null terminator at the end + let invalid_result = CStr::from_bytes_with_nul(&interior_null_bytes); + assert!(invalid_result.is_err()); } - } \ No newline at end of file From 43e588c02a49d667cf25c845dbcbd6af5f878f57 Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:30:12 -0800 Subject: [PATCH 63/67] edited the documentation --- kani | 2 +- library/Cargo.lock | 88 +++++++++++++++++++++++++++++++++++ library/core/src/ffi/c_str.rs | 10 ++-- library/core/src/num/f128.rs | 5 -- library/core/src/num/f16.rs | 7 --- library/core/src/num/f32.rs | 7 --- library/core/src/num/mod.rs | 15 ------ 7 files changed, 94 insertions(+), 40 deletions(-) diff --git a/kani b/kani index 8400296f5280b..17f4ff1fe68ca 160000 --- a/kani +++ b/kani @@ -1 +1 @@ -Subproject commit 8400296f5280be4f99820129bc66447e8dff63f4 +Subproject commit 17f4ff1fe68caab3de0c88e355cd4d024e9290aa diff --git a/library/Cargo.lock b/library/Cargo.lock index 5defd2950e8ea..e351f640e1171 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -32,6 +32,7 @@ dependencies = [ "core", "rand", "rand_xorshift", + "safety", ] [[package]] @@ -75,6 +76,7 @@ version = "0.0.0" dependencies = [ "rand", "rand_xorshift", + "safety", ] [[package]] @@ -222,6 +224,39 @@ dependencies = [ "unwind", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + [[package]] name = "proc_macro" version = "0.0.0" @@ -239,6 +274,15 @@ dependencies = [ "core", ] +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + [[package]] name = "r-efi" version = "4.5.0" @@ -315,6 +359,16 @@ dependencies = [ "std", ] +[[package]] +name = "safety" +version = "0.1.0" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "shlex" version = "1.3.0" @@ -345,6 +399,7 @@ dependencies = [ "rand", "rand_xorshift", "rustc-demangle", + "safety", "std_detect", "unwind", "wasi", @@ -362,6 +417,27 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sysroot" version = "0.0.0" @@ -382,6 +458,12 @@ dependencies = [ "std", ] +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + [[package]] name = "unicode-width" version = "0.1.14" @@ -415,6 +497,12 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index b9afe46416d48..d30a079036c70 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -907,11 +907,11 @@ mod verify { /// - Identifying valid non-empty `CStr` instances (characters followed by a null terminator). /// - Rejecting invalid `CStr` instances: those missing a null terminator or containing interior null bytes. /// - /// # Test Cases - /// 1. **Valid Empty CStr:** Ensure `is_empty` returns `true` for a `CStr` with only a null terminator. - /// 2. **Valid Non-Empty CStr:** Ensure `is_empty` returns `false` for a `CStr` with characters followed by a null terminator. - /// 3. **Invalid CStr (No Null Terminator):** Verify that constructing a `CStr` without a null terminator fails. - /// 4. **Invalid CStr (Interior Null Byte):** Verify that constructing a `CStr` with interior null bytes fails. + /// Test Cases + /// 1. Valid Empty CStr:Ensure `is_empty` returns `true` for a `CStr` with only a null terminator. + /// 2. Valid Non-Empty CStr:Ensure `is_empty` returns `false` for a `CStr` with characters followed by a null terminator. + /// 3. Invalid CStr (No Null Terminator): Verify that constructing a `CStr` without a null terminator fails. + /// 4. Invalid CStr (Interior Null Byte): Verify that constructing a `CStr` with interior null bytes fails. #[kani::proof] #[kani::unwind(32)] fn check_is_empty() { diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 722d613de3ac7..e8161cce2fe29 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -16,9 +16,6 @@ use crate::convert::FloatToInt; use crate::intrinsics; use crate::mem; use crate::num::FpCategory; -use safety::{requires, ensures}; -#[cfg(kani)] -use crate::kani; /// Basic mathematical constants. #[unstable(feature = "f128", issue = "116909")] @@ -884,8 +881,6 @@ impl f128 { #[inline] #[unstable(feature = "f128", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] - // is_finite() checks if the given float is neither infinite nor NaN. - #[requires(self.is_finite() && self >= Self::MIN && self <= Self::MAX)] pub unsafe fn to_int_unchecked(self) -> Int where Self: FloatToInt, diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index d65fe0d5a54fe..8b3f3b7d19bf7 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -17,11 +17,6 @@ use crate::intrinsics; use crate::mem; use crate::num::FpCategory; -use safety::{requires, ensures}; - -#[cfg(kani)] -use crate::kani; - /// Basic mathematical constants. #[unstable(feature = "f16", issue = "116909")] pub mod consts { @@ -868,8 +863,6 @@ impl f16 { /// * Not be infinite /// * Be representable in the return type `Int`, after truncating off its fractional part #[inline] - #[requires(!self.is_nan() && !self.is_infinite())] - #[requires(self >= Self::MIN && self <= Self::MAX)] #[unstable(feature = "f16", issue = "116909")] #[must_use = "this returns the result of the operation, without modifying the original"] pub unsafe fn to_int_unchecked(self) -> Int diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 0952767f369de..a01761ee5d4a3 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -17,11 +17,6 @@ use crate::intrinsics; use crate::mem; use crate::num::FpCategory; -use safety::{requires, ensures}; - -#[cfg(kani)] -use crate::kani; - /// The radix or base of the internal representation of `f32`. /// Use [`f32::RADIX`] instead. /// @@ -1069,8 +1064,6 @@ impl f32 { without modifying the original"] #[stable(feature = "float_approx_unchecked_to", since = "1.44.0")] #[inline] - // is_finite() checks if the given float is neither infinite nor NaN. - #[requires(self.is_finite() && self >= Self::MIN && self <= Self::MAX)] pub unsafe fn to_int_unchecked(self) -> Int where Self: FloatToInt, diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 77826eb29d3cf..765052e9cbaa6 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -1843,21 +1843,6 @@ mod verify { } } - // Part 3: Float to Integer Conversion function Harness Generation Macros - macro_rules! generate_to_int_unchecked_harness { - ($floatType:ty, $($intType:ty, $harness_name:ident),+) => { - $( - #[kani::proof_for_contract($floatType::to_int_unchecked)] - pub fn $harness_name() { - let num1: $floatType = kani::any::<$floatType>(); - let result = unsafe { num1.to_int_unchecked::<$intType>() }; - - assert_eq!(result, num1 as $intType); - } - )+ - } - } - // `unchecked_add` proofs // // Target types: From fdefcc08d12cf77020a3624fc3b07b2e46eccf6e Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Thu, 28 Nov 2024 02:24:40 -0800 Subject: [PATCH 64/67] remove the irrelevant kani folder and cargo file --- kani | 1 - library/Cargo.lock | 90 +--------------------------------------------- 2 files changed, 1 insertion(+), 90 deletions(-) delete mode 160000 kani diff --git a/kani b/kani deleted file mode 160000 index 17f4ff1fe68ca..0000000000000 --- a/kani +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 17f4ff1fe68caab3de0c88e355cd4d024e9290aa diff --git a/library/Cargo.lock b/library/Cargo.lock index e351f640e1171..9e8256fa11c43 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -32,7 +32,6 @@ dependencies = [ "core", "rand", "rand_xorshift", - "safety", ] [[package]] @@ -76,7 +75,6 @@ version = "0.0.0" dependencies = [ "rand", "rand_xorshift", - "safety", ] [[package]] @@ -224,39 +222,6 @@ dependencies = [ "unwind", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" -dependencies = [ - "unicode-ident", -] - [[package]] name = "proc_macro" version = "0.0.0" @@ -274,15 +239,6 @@ dependencies = [ "core", ] -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - [[package]] name = "r-efi" version = "4.5.0" @@ -359,16 +315,6 @@ dependencies = [ "std", ] -[[package]] -name = "safety" -version = "0.1.0" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.89", -] - [[package]] name = "shlex" version = "1.3.0" @@ -399,7 +345,6 @@ dependencies = [ "rand", "rand_xorshift", "rustc-demangle", - "safety", "std_detect", "unwind", "wasi", @@ -417,27 +362,6 @@ dependencies = [ "rustc-std-workspace-core", ] -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "sysroot" version = "0.0.0" @@ -458,12 +382,6 @@ dependencies = [ "std", ] -[[package]] -name = "unicode-ident" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" - [[package]] name = "unicode-width" version = "0.1.14" @@ -497,12 +415,6 @@ dependencies = [ "rustc-std-workspace-core", ] -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -589,4 +501,4 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" \ No newline at end of file From 1f5e28a5d24353b02fbd5058238866822af4163b Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Thu, 28 Nov 2024 02:32:55 -0800 Subject: [PATCH 65/67] reverted Cargo.lock to the stable one --- library/Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Cargo.lock b/library/Cargo.lock index 9e8256fa11c43..5defd2950e8ea 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -501,4 +501,4 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" \ No newline at end of file +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" From 8cc0b125be266a38553853c69d0cda1312e10146 Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Thu, 28 Nov 2024 04:26:41 -0800 Subject: [PATCH 66/67] Reset stdarch submodule to commit ff9a444 --- library/stdarch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/stdarch b/library/stdarch index d9466edb4c53c..ff9a4445038ea 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit d9466edb4c53cece8686ee6e17b028436ddf4151 +Subproject commit ff9a4445038eae46fd095188740946808581bc0e From 41ddead047517f8fd73638704aec1d971539515b Mon Sep 17 00:00:00 2001 From: Lanfei Ma <99311320+lanfeima@users.noreply.github.com> Date: Thu, 28 Nov 2024 18:30:08 -0800 Subject: [PATCH 67/67] revised is_empty harness without using arbitrary_cstr --- library/core/src/ffi/c_str.rs | 62 ++++++++++------------------------- 1 file changed, 18 insertions(+), 44 deletions(-) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index d30a079036c70..ca46e848997eb 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -900,51 +900,25 @@ mod verify { } /// Verifies the `CStr::is_empty` method. - /// - /// - /// This harness ensures that the `is_empty` method behaves correctly under various scenarios: - /// - Identifying valid empty `CStr` instances (only null terminator). - /// - Identifying valid non-empty `CStr` instances (characters followed by a null terminator). - /// - Rejecting invalid `CStr` instances: those missing a null terminator or containing interior null bytes. - /// - /// Test Cases - /// 1. Valid Empty CStr:Ensure `is_empty` returns `true` for a `CStr` with only a null terminator. - /// 2. Valid Non-Empty CStr:Ensure `is_empty` returns `false` for a `CStr` with characters followed by a null terminator. - /// 3. Invalid CStr (No Null Terminator): Verify that constructing a `CStr` without a null terminator fails. - /// 4. Invalid CStr (Interior Null Byte): Verify that constructing a `CStr` with interior null bytes fails. #[kani::proof] - #[kani::unwind(32)] + #[kani::unwind(33)] fn check_is_empty() { - const MAX_LEN: usize = 32; - - // Case 1: Valid Empty CStr - let empty_bytes: [u8; MAX_LEN] = - kani::any_where(|b: &[u8; MAX_LEN]| b[0] == 0 && !b[1..].contains(&0)); - let empty_cstr = CStr::from_bytes_with_nul(&empty_bytes[..1]).unwrap(); - assert!(empty_cstr.is_empty()); - - // Case 2: Valid Non-Empty CStr - let mut non_empty_bytes: [u8; MAX_LEN] = kani::any(); - let len = kani::any_where(|&l: &usize| l >= 2 && l < MAX_LEN); // Ensure len >= 2 - non_empty_bytes[len - 1] = 0; // Ensure null terminator - non_empty_bytes[..len - 1] - .iter_mut() - .for_each(|b| *b = kani::any_where(|&c: &u8| c != 0)); // Ensure no interior null bytes - let non_empty_cstr = CStr::from_bytes_with_nul(&non_empty_bytes[..len]).unwrap(); - assert!(!non_empty_cstr.is_empty()); - - // Case 3: Invalid CStr (No Null Terminator) - let invalid_bytes: [u8; MAX_LEN] = - kani::any_where(|b: &[u8; MAX_LEN]| !b.contains(&0)); // No null byte - let invalid_result = CStr::from_bytes_with_nul(&invalid_bytes[..MAX_LEN - 1]); // Missing null terminator - assert!(invalid_result.is_err()); - - // Case 4: Invalid CStr (Interior Null Byte) - let mut interior_null_bytes: [u8; MAX_LEN] = kani::any(); - let pos = kani::any_where(|&p: &usize| p < MAX_LEN - 1); // Non-deterministic position for the null byte - interior_null_bytes[pos] = 0; // Insert null byte inside the array - interior_null_bytes[MAX_LEN - 1] = 0; // Ensure null terminator at the end - let invalid_result = CStr::from_bytes_with_nul(&interior_null_bytes); - assert!(invalid_result.is_err()); + const MAX_SIZE: usize = 32; + let string: [u8; MAX_SIZE] = kani::any(); + let slice = kani::slice::any_slice_of_array(&string); + + // Attempt to create a CStr and handle possible failure + if let Ok(c_str) = CStr::from_bytes_with_nul(slice) { + // Validate the safety of the constructed CStr + assert!(c_str.is_safe(), "Constructed CStr failed the safety invariant."); + + // Test the is_empty method + let bytes = c_str.to_bytes(); // Excludes the null terminator + let expected_is_empty = bytes.len() == 0; + assert_eq!(expected_is_empty, c_str.is_empty()); + } else { + // If the slice is invalid, confirm that constructing CStr fails + assert!(slice.last() != Some(&0) || slice[..slice.len() - 1].contains(&0)); + } } } \ No newline at end of file