Skip to content

Add autoharness to run-kani script and use in CI #334

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
Apr 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3c79dfa
Add autoharness to run-kani script and use in CI
tautschnig Apr 18, 2025
5adba8b
Remove Kani harnesses from time::Duration that autoharness takes care of
tautschnig Apr 22, 2025
60cb531
Use autoharness for ptr::align_offset::mod_inv
tautschnig Apr 22, 2025
ef9b0fb
Use autoharness for several Alignment functions
tautschnig Apr 22, 2025
2ce92f2
Don't use doc comment
tautschnig Apr 22, 2025
a1bbbbb
Squash duplicate space in markdown file
tautschnig Apr 23, 2025
267fa99
Use .0 instead of .as_usize
tautschnig Apr 24, 2025
d4c6ec5
Make autoharness in run-kani.sh --run list optional
tautschnig Apr 24, 2025
8064562
Amend help
tautschnig Apr 24, 2025
96baf9d
Fix help
tautschnig Apr 24, 2025
e7d4428
Use autoharness for unicode::unicode_data::conversions::to_{lower,upper}
tautschnig Apr 25, 2025
721fb78
Use autoharness for alloc::layout::Layout::from_size_align{,_unchecked}
tautschnig Apr 25, 2025
2fd26a6
Use autoharness for ascii::ascii_char::AsciiChar::from_u8{,_unchecked}
tautschnig Apr 25, 2025
889b8cd
Use autoharness for char::convert::from_u32_unchecked
tautschnig Apr 25, 2025
0c1b5c3
Use autoharness for num::<impl *>::wrapping_sh{l,r}
tautschnig Apr 25, 2025
2144ebe
Use autoharness for num::<impl *>::unchecked_sh{l,r}
tautschnig Apr 25, 2025
e7b0dc4
Use autoharness for num::<impl i*>::unchecked_neg
tautschnig Apr 25, 2025
153f5d1
Use autoharness for num::<impl *>::unchecked_{add,sub}
tautschnig Apr 25, 2025
bc70538
Use autoharness for convert::num::<impl convert::From<num::nonzero::N…
tautschnig Apr 25, 2025
a0daec5
Use autoharness for num::nonzero::NonZero::<*>::count_ones
tautschnig Apr 25, 2025
f079d4c
Use autoharness for num::nonzero::NonZero::<*>::rotate_{left,right}
tautschnig Apr 25, 2025
1e17495
Use array for command_args to ensure quoting is retained
tautschnig Apr 25, 2025
7798495
Do not try to use unset CBMC_PATCH variable
tautschnig Apr 25, 2025
308f6a1
Revert "Use autoharness for convert::num::<impl convert::From<num::no…
tautschnig Apr 25, 2025
a90ba68
Revert "Use autoharness for num::<impl *>::unchecked_{add,sub}"
tautschnig Apr 25, 2025
3aefe47
Revert "Use autoharness for num::<impl i*>::unchecked_neg"
tautschnig Apr 25, 2025
dcc1959
Revert "Use autoharness for num::<impl *>::unchecked_sh{l,r}"
tautschnig Apr 25, 2025
7162c25
Revert "Use autoharness for num::<impl *>::wrapping_sh{l,r}"
tautschnig Apr 25, 2025
83dfb82
Clarify use
tautschnig Apr 29, 2025
01ae84e
Merge remote-tracking branch 'origin/main' into autoharness-support
tautschnig Apr 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/kani-metrics.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
python-version: '3.x'

- name: Compute Kani Metrics
run: ./scripts/run-kani.sh --run metrics --path ${{github.workspace}}
run: ./scripts/run-kani.sh --run metrics --with-autoharness --path ${{github.workspace}}

- name: Create Pull Request
uses: peter-evans/create-pull-request@v7
Expand Down
83 changes: 81 additions & 2 deletions .github/workflows/kani.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,79 @@ jobs:
- name: Run Kani Verification
run: head/scripts/run-kani.sh --path ${{github.workspace}}/head

kani-autoharness:
name: Verify std library using autoharness
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
include:
- os: ubuntu-latest
base: ubuntu
- os: macos-latest
base: macos
fail-fast: false

steps:
# Step 1: Check out the repository
- name: Checkout Repository
uses: actions/checkout@v4
with:
submodules: true

# Step 2: Run Kani autoharness on the std library for selected functions.
# Uses "--include-pattern" to make sure we do not try to run across all
# possible functions as that may take a lot longer than expected. Instead,
# explicitly list all functions (or prefixes thereof) the proofs of which
# are known to pass.
- name: Run Kani Verification
run: |
scripts/run-kani.sh --run autoharness --kani-args \
--include-pattern alloc::layout::Layout::from_size_align \
--include-pattern ascii::ascii_char::AsciiChar::from_u8 \
--include-pattern char::convert::from_u32_unchecked \
--include-pattern "num::nonzero::NonZero::<i8>::count_ones" \
--include-pattern "num::nonzero::NonZero::<i16>::count_ones" \
--include-pattern "num::nonzero::NonZero::<i32>::count_ones" \
--include-pattern "num::nonzero::NonZero::<i64>::count_ones" \
--include-pattern "num::nonzero::NonZero::<i128>::count_ones" \
--include-pattern "num::nonzero::NonZero::<isize>::count_ones" \
--include-pattern "num::nonzero::NonZero::<u8>::count_ones" \
--include-pattern "num::nonzero::NonZero::<u16>::count_ones" \
--include-pattern "num::nonzero::NonZero::<u32>::count_ones" \
--include-pattern "num::nonzero::NonZero::<u64>::count_ones" \
--include-pattern "num::nonzero::NonZero::<u128>::count_ones" \
--include-pattern "num::nonzero::NonZero::<usize>::count_ones" \
--include-pattern "num::nonzero::NonZero::<i8>::rotate_" \
--include-pattern "num::nonzero::NonZero::<i16>::rotate_" \
--include-pattern "num::nonzero::NonZero::<i32>::rotate_" \
--include-pattern "num::nonzero::NonZero::<i64>::rotate_" \
--include-pattern "num::nonzero::NonZero::<i128>::rotate_" \
--include-pattern "num::nonzero::NonZero::<isize>::rotate_" \
--include-pattern "num::nonzero::NonZero::<u8>::rotate_" \
--include-pattern "num::nonzero::NonZero::<u16>::rotate_" \
--include-pattern "num::nonzero::NonZero::<u32>::rotate_" \
--include-pattern "num::nonzero::NonZero::<u64>::rotate_" \
--include-pattern "num::nonzero::NonZero::<u128>::rotate_" \
--include-pattern "num::nonzero::NonZero::<usize>::rotate_" \
--include-pattern ptr::align_offset::mod_inv \
--include-pattern ptr::alignment::Alignment::as_nonzero \
--include-pattern ptr::alignment::Alignment::as_usize \
--include-pattern ptr::alignment::Alignment::log2 \
--include-pattern ptr::alignment::Alignment::mask \
--include-pattern ptr::alignment::Alignment::new \
--include-pattern ptr::alignment::Alignment::new_unchecked \
--include-pattern time::Duration::from_micros \
--include-pattern time::Duration::from_millis \
--include-pattern time::Duration::from_nanos \
--include-pattern time::Duration::from_secs \
--exclude-pattern time::Duration::from_secs_f \
--include-pattern unicode::unicode_data::conversions::to_ \
--exclude-pattern ::precondition_check \
--harness-timeout 5m \
--default-unwind 1000 \
--jobs=3 --output-format=terse

run-kani-list:
name: Kani List
runs-on: ubuntu-latest
Expand All @@ -66,8 +139,14 @@ jobs:

# Step 2: Run list on the std library
- name: Run Kani List
run: head/scripts/run-kani.sh --run list --path ${{github.workspace}}/head

run: |
head/scripts/run-kani.sh --run list --with-autoharness --path ${{github.workspace}}/head
# remove duplicate white space to reduce file size (GitHub permits at
# most one 1MB)
ls -l ${{github.workspace}}/head/kani-list.md
perl -p -i -e 's/ +/ /g' ${{github.workspace}}/head/kani-list.md
ls -l ${{github.workspace}}/head/kani-list.md

# Step 3: Add output to job summary
- name: Add Kani List output to job summary
uses: actions/github-script@v6
Expand Down
18 changes: 0 additions & 18 deletions library/core/src/alloc/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -637,24 +637,6 @@ mod verify {
}
}

// pub const fn from_size_align(size: usize, align: usize) -> Result<Self, LayoutError>
#[kani::proof_for_contract(Layout::from_size_align)]
pub fn check_from_size_align() {
let s = kani::any::<usize>();
let a = kani::any::<usize>();
let _ = Layout::from_size_align(s, a);
}

// pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self
#[kani::proof_for_contract(Layout::from_size_align_unchecked)]
pub fn check_from_size_align_unchecked() {
let s = kani::any::<usize>();
let a = kani::any::<usize>();
unsafe {
let _ = Layout::from_size_align_unchecked(s, a);
}
}

// pub const fn size(&self) -> usize
#[kani::proof]
pub fn check_size() {
Expand Down
20 changes: 0 additions & 20 deletions library/core/src/ascii/ascii_char.rs
Original file line number Diff line number Diff line change
Expand Up @@ -621,23 +621,3 @@ impl fmt::Debug for AsciiChar {
f.write_str(buf[..len].as_str())
}
}

#[cfg(kani)]
#[unstable(feature = "kani", issue = "none")]
mod verify {
use AsciiChar;

use super::*;

#[kani::proof_for_contract(AsciiChar::from_u8)]
fn check_from_u8() {
let b: u8 = kani::any();
AsciiChar::from_u8(b);
}

#[kani::proof_for_contract(AsciiChar::from_u8_unchecked)]
fn check_from_u8_unchecked() {
let b: u8 = kani::any();
unsafe { AsciiChar::from_u8_unchecked(b) };
}
}
12 changes: 0 additions & 12 deletions library/core/src/char/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,15 +288,3 @@ pub(super) const fn from_digit(num: u32, radix: u32) -> Option<char> {
None
}
}

#[cfg(kani)]
#[unstable(feature = "kani", issue = "none")]
mod verify {
use super::*;

#[kani::proof_for_contract(from_u32_unchecked)]
fn check_from_u32_unchecked() {
let i: u32 = kani::any();
unsafe { from_u32_unchecked(i) };
}
}
57 changes: 5 additions & 52 deletions library/core/src/num/nonzero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,7 @@ macro_rules! nonzero_integer {
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
#[ensures(|result| result.get() > 0)]
pub const fn count_ones(self) -> NonZero<u32> {
// SAFETY:
// `self` is non-zero, which means it has at least one bit set, which means
Expand Down Expand Up @@ -754,6 +755,8 @@ macro_rules! nonzero_integer {
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
#[ensures(|result| result.get() > 0)]
#[ensures(|result| result.rotate_right(n).get() == old(self).get())]
pub const fn rotate_left(self, n: u32) -> Self {
let result = self.get().rotate_left(n);
// SAFETY: Rotating bits preserves the property int > 0.
Expand Down Expand Up @@ -787,6 +790,8 @@ macro_rules! nonzero_integer {
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
#[ensures(|result| result.get() > 0)]
#[ensures(|result| result.rotate_left(n).get() == old(self).get())]
pub const fn rotate_right(self, n: u32) -> Self {
let result = self.get().rotate_right(n);
// SAFETY: Rotating bits preserves the property int > 0.
Expand Down Expand Up @@ -2571,56 +2576,4 @@ mod verify {
nonzero_check_clamp_panic!(core::num::NonZeroU64, nonzero_check_clamp_panic_for_u64);
nonzero_check_clamp_panic!(core::num::NonZeroU128, nonzero_check_clamp_panic_for_u128);
nonzero_check_clamp_panic!(core::num::NonZeroUsize, nonzero_check_clamp_panic_for_usize);

macro_rules! nonzero_check_count_ones {
($nonzero_type:ty, $nonzero_check_count_ones_for:ident) => {
#[kani::proof]
pub fn $nonzero_check_count_ones_for() {
let x: $nonzero_type = kani::any();
let result = x.count_ones();
// Since x is non-zero, count_ones should never return 0
assert!(result.get() > 0);
}
};
}

// Use the macro to generate different versions of the function for multiple types
nonzero_check_count_ones!(core::num::NonZeroI8, nonzero_check_count_ones_for_i8);
nonzero_check_count_ones!(core::num::NonZeroI16, nonzero_check_count_ones_for_i16);
nonzero_check_count_ones!(core::num::NonZeroI32, nonzero_check_count_ones_for_i32);
nonzero_check_count_ones!(core::num::NonZeroI64, nonzero_check_count_ones_for_i64);
nonzero_check_count_ones!(core::num::NonZeroI128, nonzero_check_count_ones_for_i128);
nonzero_check_count_ones!(core::num::NonZeroIsize, nonzero_check_count_ones_for_isize);
nonzero_check_count_ones!(core::num::NonZeroU8, nonzero_check_count_ones_for_u8);
nonzero_check_count_ones!(core::num::NonZeroU16, nonzero_check_count_ones_for_u16);
nonzero_check_count_ones!(core::num::NonZeroU32, nonzero_check_count_ones_for_u32);
nonzero_check_count_ones!(core::num::NonZeroU64, nonzero_check_count_ones_for_u64);
nonzero_check_count_ones!(core::num::NonZeroU128, nonzero_check_count_ones_for_u128);
nonzero_check_count_ones!(core::num::NonZeroUsize, nonzero_check_count_ones_for_usize);

macro_rules! nonzero_check_rotate_left_and_right {
($nonzero_type:ty, $nonzero_check_rotate_for:ident) => {
#[kani::proof]
pub fn $nonzero_check_rotate_for() {
let x: $nonzero_type = kani::any();
let n: u32 = kani::any();
let result = x.rotate_left(n).rotate_right(n);
assert!(result == x);
}
};
}

// Use the macro to generate different versions of the function for multiple types
nonzero_check_rotate_left_and_right!(core::num::NonZeroI8, nonzero_check_rotate_for_i8);
nonzero_check_rotate_left_and_right!(core::num::NonZeroI16, nonzero_check_rotate_for_i16);
nonzero_check_rotate_left_and_right!(core::num::NonZeroI32, nonzero_check_rotate_for_i32);
nonzero_check_rotate_left_and_right!(core::num::NonZeroI64, nonzero_check_rotate_for_i64);
nonzero_check_rotate_left_and_right!(core::num::NonZeroI128, nonzero_check_rotate_for_i128);
nonzero_check_rotate_left_and_right!(core::num::NonZeroIsize, nonzero_check_rotate_for_isize);
nonzero_check_rotate_left_and_right!(core::num::NonZeroU8, nonzero_check_rotate_for_u8);
nonzero_check_rotate_left_and_right!(core::num::NonZeroU16, nonzero_check_rotate_for_u16);
nonzero_check_rotate_left_and_right!(core::num::NonZeroU32, nonzero_check_rotate_for_u32);
nonzero_check_rotate_left_and_right!(core::num::NonZeroU64, nonzero_check_rotate_for_u64);
nonzero_check_rotate_left_and_right!(core::num::NonZeroU128, nonzero_check_rotate_for_u128);
nonzero_check_rotate_left_and_right!(core::num::NonZeroUsize, nonzero_check_rotate_for_usize);
}
52 changes: 4 additions & 48 deletions library/core/src/ptr/alignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ use crate::{cmp, fmt, hash, mem, num};
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
#[invariant(self.as_usize().is_power_of_two())]
// uses .0 instead of .as_usize() to permit proving as_usize so that its proof does not itself use
// as_usize
#[invariant((self.0 as usize).is_power_of_two())]
pub struct Alignment(AlignmentEnum);

// Alignment is `repr(usize)`, but via extra steps.
Expand Down Expand Up @@ -404,56 +406,10 @@ mod verify {
}
}

/// FIXME, c.f. https://github.com/model-checking/kani/issues/3905
// FIXME, c.f. https://github.com/model-checking/kani/issues/3905
// // pub const fn of<T>() -> Self
// #[kani::proof_for_contract(Alignment::of)]
// pub fn check_of_i32() {
// let _ = Alignment::of::<i32>();
// }

// pub const fn new(align: usize) -> Option<Self>
#[kani::proof_for_contract(Alignment::new)]
pub fn check_new() {
let a = kani::any::<usize>();
let _ = Alignment::new(a);
}

// pub const unsafe fn new_unchecked(align: usize) -> Self
#[kani::proof_for_contract(Alignment::new_unchecked)]
pub fn check_new_unchecked() {
let a = kani::any::<usize>();
unsafe {
let _ = Alignment::new_unchecked(a);
}
}

// pub const fn as_usize(self) -> usize
#[kani::proof_for_contract(Alignment::as_usize)]
pub fn check_as_usize() {
let a = kani::any::<usize>();
if let Some(alignment) = Alignment::new(a) {
assert_eq!(alignment.as_usize(), a);
}
}

// pub const fn as_nonzero(self) -> NonZero<usize>
#[kani::proof_for_contract(Alignment::as_nonzero)]
pub fn check_as_nonzero() {
let alignment = kani::any::<Alignment>();
let _ = alignment.as_nonzero();
}

// pub const fn log2(self) -> u32
#[kani::proof_for_contract(Alignment::log2)]
pub fn check_log2() {
let alignment = kani::any::<Alignment>();
let _ = alignment.log2();
}

// pub const fn mask(self) -> usize
#[kani::proof_for_contract(Alignment::mask)]
pub fn check_mask() {
let alignment = kani::any::<Alignment>();
let _ = alignment.mask();
}
}
Loading
Loading