Skip to content

Commit f804a33

Browse files
authored
Add autoharness to run-kani script and use in CI (model-checking#334)
Run a selected set of automatic harnesses where their previous, manual harnesses are being removed in this PR. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.
1 parent f1aaa87 commit f804a33

File tree

11 files changed

+133
-268
lines changed

11 files changed

+133
-268
lines changed

.github/workflows/kani-metrics.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
python-version: '3.x'
2727

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

3131
- name: Create Pull Request
3232
uses: peter-evans/create-pull-request@v7

.github/workflows/kani.yml

+81-2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,79 @@ jobs:
5353
- name: Run Kani Verification
5454
run: head/scripts/run-kani.sh --path ${{github.workspace}}/head
5555

56+
kani-autoharness:
57+
name: Verify std library using autoharness
58+
runs-on: ${{ matrix.os }}
59+
strategy:
60+
matrix:
61+
os: [ubuntu-latest, macos-latest]
62+
include:
63+
- os: ubuntu-latest
64+
base: ubuntu
65+
- os: macos-latest
66+
base: macos
67+
fail-fast: false
68+
69+
steps:
70+
# Step 1: Check out the repository
71+
- name: Checkout Repository
72+
uses: actions/checkout@v4
73+
with:
74+
submodules: true
75+
76+
# Step 2: Run Kani autoharness on the std library for selected functions.
77+
# Uses "--include-pattern" to make sure we do not try to run across all
78+
# possible functions as that may take a lot longer than expected. Instead,
79+
# explicitly list all functions (or prefixes thereof) the proofs of which
80+
# are known to pass.
81+
- name: Run Kani Verification
82+
run: |
83+
scripts/run-kani.sh --run autoharness --kani-args \
84+
--include-pattern alloc::layout::Layout::from_size_align \
85+
--include-pattern ascii::ascii_char::AsciiChar::from_u8 \
86+
--include-pattern char::convert::from_u32_unchecked \
87+
--include-pattern "num::nonzero::NonZero::<i8>::count_ones" \
88+
--include-pattern "num::nonzero::NonZero::<i16>::count_ones" \
89+
--include-pattern "num::nonzero::NonZero::<i32>::count_ones" \
90+
--include-pattern "num::nonzero::NonZero::<i64>::count_ones" \
91+
--include-pattern "num::nonzero::NonZero::<i128>::count_ones" \
92+
--include-pattern "num::nonzero::NonZero::<isize>::count_ones" \
93+
--include-pattern "num::nonzero::NonZero::<u8>::count_ones" \
94+
--include-pattern "num::nonzero::NonZero::<u16>::count_ones" \
95+
--include-pattern "num::nonzero::NonZero::<u32>::count_ones" \
96+
--include-pattern "num::nonzero::NonZero::<u64>::count_ones" \
97+
--include-pattern "num::nonzero::NonZero::<u128>::count_ones" \
98+
--include-pattern "num::nonzero::NonZero::<usize>::count_ones" \
99+
--include-pattern "num::nonzero::NonZero::<i8>::rotate_" \
100+
--include-pattern "num::nonzero::NonZero::<i16>::rotate_" \
101+
--include-pattern "num::nonzero::NonZero::<i32>::rotate_" \
102+
--include-pattern "num::nonzero::NonZero::<i64>::rotate_" \
103+
--include-pattern "num::nonzero::NonZero::<i128>::rotate_" \
104+
--include-pattern "num::nonzero::NonZero::<isize>::rotate_" \
105+
--include-pattern "num::nonzero::NonZero::<u8>::rotate_" \
106+
--include-pattern "num::nonzero::NonZero::<u16>::rotate_" \
107+
--include-pattern "num::nonzero::NonZero::<u32>::rotate_" \
108+
--include-pattern "num::nonzero::NonZero::<u64>::rotate_" \
109+
--include-pattern "num::nonzero::NonZero::<u128>::rotate_" \
110+
--include-pattern "num::nonzero::NonZero::<usize>::rotate_" \
111+
--include-pattern ptr::align_offset::mod_inv \
112+
--include-pattern ptr::alignment::Alignment::as_nonzero \
113+
--include-pattern ptr::alignment::Alignment::as_usize \
114+
--include-pattern ptr::alignment::Alignment::log2 \
115+
--include-pattern ptr::alignment::Alignment::mask \
116+
--include-pattern ptr::alignment::Alignment::new \
117+
--include-pattern ptr::alignment::Alignment::new_unchecked \
118+
--include-pattern time::Duration::from_micros \
119+
--include-pattern time::Duration::from_millis \
120+
--include-pattern time::Duration::from_nanos \
121+
--include-pattern time::Duration::from_secs \
122+
--exclude-pattern time::Duration::from_secs_f \
123+
--include-pattern unicode::unicode_data::conversions::to_ \
124+
--exclude-pattern ::precondition_check \
125+
--harness-timeout 5m \
126+
--default-unwind 1000 \
127+
--jobs=3 --output-format=terse
128+
56129
run-kani-list:
57130
name: Kani List
58131
runs-on: ubuntu-latest
@@ -66,8 +139,14 @@ jobs:
66139

67140
# Step 2: Run list on the std library
68141
- name: Run Kani List
69-
run: head/scripts/run-kani.sh --run list --path ${{github.workspace}}/head
70-
142+
run: |
143+
head/scripts/run-kani.sh --run list --with-autoharness --path ${{github.workspace}}/head
144+
# remove duplicate white space to reduce file size (GitHub permits at
145+
# most one 1MB)
146+
ls -l ${{github.workspace}}/head/kani-list.md
147+
perl -p -i -e 's/ +/ /g' ${{github.workspace}}/head/kani-list.md
148+
ls -l ${{github.workspace}}/head/kani-list.md
149+
71150
# Step 3: Add output to job summary
72151
- name: Add Kani List output to job summary
73152
uses: actions/github-script@v6

library/core/src/alloc/layout.rs

-18
Original file line numberDiff line numberDiff line change
@@ -637,24 +637,6 @@ mod verify {
637637
}
638638
}
639639

640-
// pub const fn from_size_align(size: usize, align: usize) -> Result<Self, LayoutError>
641-
#[kani::proof_for_contract(Layout::from_size_align)]
642-
pub fn check_from_size_align() {
643-
let s = kani::any::<usize>();
644-
let a = kani::any::<usize>();
645-
let _ = Layout::from_size_align(s, a);
646-
}
647-
648-
// pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self
649-
#[kani::proof_for_contract(Layout::from_size_align_unchecked)]
650-
pub fn check_from_size_align_unchecked() {
651-
let s = kani::any::<usize>();
652-
let a = kani::any::<usize>();
653-
unsafe {
654-
let _ = Layout::from_size_align_unchecked(s, a);
655-
}
656-
}
657-
658640
// pub const fn size(&self) -> usize
659641
#[kani::proof]
660642
pub fn check_size() {

library/core/src/ascii/ascii_char.rs

-20
Original file line numberDiff line numberDiff line change
@@ -621,23 +621,3 @@ impl fmt::Debug for AsciiChar {
621621
f.write_str(buf[..len].as_str())
622622
}
623623
}
624-
625-
#[cfg(kani)]
626-
#[unstable(feature = "kani", issue = "none")]
627-
mod verify {
628-
use AsciiChar;
629-
630-
use super::*;
631-
632-
#[kani::proof_for_contract(AsciiChar::from_u8)]
633-
fn check_from_u8() {
634-
let b: u8 = kani::any();
635-
AsciiChar::from_u8(b);
636-
}
637-
638-
#[kani::proof_for_contract(AsciiChar::from_u8_unchecked)]
639-
fn check_from_u8_unchecked() {
640-
let b: u8 = kani::any();
641-
unsafe { AsciiChar::from_u8_unchecked(b) };
642-
}
643-
}

library/core/src/char/convert.rs

-12
Original file line numberDiff line numberDiff line change
@@ -288,15 +288,3 @@ pub(super) const fn from_digit(num: u32, radix: u32) -> Option<char> {
288288
None
289289
}
290290
}
291-
292-
#[cfg(kani)]
293-
#[unstable(feature = "kani", issue = "none")]
294-
mod verify {
295-
use super::*;
296-
297-
#[kani::proof_for_contract(from_u32_unchecked)]
298-
fn check_from_u32_unchecked() {
299-
let i: u32 = kani::any();
300-
unsafe { from_u32_unchecked(i) };
301-
}
302-
}

library/core/src/num/nonzero.rs

+5-52
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,7 @@ macro_rules! nonzero_integer {
721721
#[must_use = "this returns the result of the operation, \
722722
without modifying the original"]
723723
#[inline(always)]
724+
#[ensures(|result| result.get() > 0)]
724725
pub const fn count_ones(self) -> NonZero<u32> {
725726
// SAFETY:
726727
// `self` is non-zero, which means it has at least one bit set, which means
@@ -754,6 +755,8 @@ macro_rules! nonzero_integer {
754755
#[must_use = "this returns the result of the operation, \
755756
without modifying the original"]
756757
#[inline(always)]
758+
#[ensures(|result| result.get() > 0)]
759+
#[ensures(|result| result.rotate_right(n).get() == old(self).get())]
757760
pub const fn rotate_left(self, n: u32) -> Self {
758761
let result = self.get().rotate_left(n);
759762
// SAFETY: Rotating bits preserves the property int > 0.
@@ -787,6 +790,8 @@ macro_rules! nonzero_integer {
787790
#[must_use = "this returns the result of the operation, \
788791
without modifying the original"]
789792
#[inline(always)]
793+
#[ensures(|result| result.get() > 0)]
794+
#[ensures(|result| result.rotate_left(n).get() == old(self).get())]
790795
pub const fn rotate_right(self, n: u32) -> Self {
791796
let result = self.get().rotate_right(n);
792797
// SAFETY: Rotating bits preserves the property int > 0.
@@ -2571,56 +2576,4 @@ mod verify {
25712576
nonzero_check_clamp_panic!(core::num::NonZeroU64, nonzero_check_clamp_panic_for_u64);
25722577
nonzero_check_clamp_panic!(core::num::NonZeroU128, nonzero_check_clamp_panic_for_u128);
25732578
nonzero_check_clamp_panic!(core::num::NonZeroUsize, nonzero_check_clamp_panic_for_usize);
2574-
2575-
macro_rules! nonzero_check_count_ones {
2576-
($nonzero_type:ty, $nonzero_check_count_ones_for:ident) => {
2577-
#[kani::proof]
2578-
pub fn $nonzero_check_count_ones_for() {
2579-
let x: $nonzero_type = kani::any();
2580-
let result = x.count_ones();
2581-
// Since x is non-zero, count_ones should never return 0
2582-
assert!(result.get() > 0);
2583-
}
2584-
};
2585-
}
2586-
2587-
// Use the macro to generate different versions of the function for multiple types
2588-
nonzero_check_count_ones!(core::num::NonZeroI8, nonzero_check_count_ones_for_i8);
2589-
nonzero_check_count_ones!(core::num::NonZeroI16, nonzero_check_count_ones_for_i16);
2590-
nonzero_check_count_ones!(core::num::NonZeroI32, nonzero_check_count_ones_for_i32);
2591-
nonzero_check_count_ones!(core::num::NonZeroI64, nonzero_check_count_ones_for_i64);
2592-
nonzero_check_count_ones!(core::num::NonZeroI128, nonzero_check_count_ones_for_i128);
2593-
nonzero_check_count_ones!(core::num::NonZeroIsize, nonzero_check_count_ones_for_isize);
2594-
nonzero_check_count_ones!(core::num::NonZeroU8, nonzero_check_count_ones_for_u8);
2595-
nonzero_check_count_ones!(core::num::NonZeroU16, nonzero_check_count_ones_for_u16);
2596-
nonzero_check_count_ones!(core::num::NonZeroU32, nonzero_check_count_ones_for_u32);
2597-
nonzero_check_count_ones!(core::num::NonZeroU64, nonzero_check_count_ones_for_u64);
2598-
nonzero_check_count_ones!(core::num::NonZeroU128, nonzero_check_count_ones_for_u128);
2599-
nonzero_check_count_ones!(core::num::NonZeroUsize, nonzero_check_count_ones_for_usize);
2600-
2601-
macro_rules! nonzero_check_rotate_left_and_right {
2602-
($nonzero_type:ty, $nonzero_check_rotate_for:ident) => {
2603-
#[kani::proof]
2604-
pub fn $nonzero_check_rotate_for() {
2605-
let x: $nonzero_type = kani::any();
2606-
let n: u32 = kani::any();
2607-
let result = x.rotate_left(n).rotate_right(n);
2608-
assert!(result == x);
2609-
}
2610-
};
2611-
}
2612-
2613-
// Use the macro to generate different versions of the function for multiple types
2614-
nonzero_check_rotate_left_and_right!(core::num::NonZeroI8, nonzero_check_rotate_for_i8);
2615-
nonzero_check_rotate_left_and_right!(core::num::NonZeroI16, nonzero_check_rotate_for_i16);
2616-
nonzero_check_rotate_left_and_right!(core::num::NonZeroI32, nonzero_check_rotate_for_i32);
2617-
nonzero_check_rotate_left_and_right!(core::num::NonZeroI64, nonzero_check_rotate_for_i64);
2618-
nonzero_check_rotate_left_and_right!(core::num::NonZeroI128, nonzero_check_rotate_for_i128);
2619-
nonzero_check_rotate_left_and_right!(core::num::NonZeroIsize, nonzero_check_rotate_for_isize);
2620-
nonzero_check_rotate_left_and_right!(core::num::NonZeroU8, nonzero_check_rotate_for_u8);
2621-
nonzero_check_rotate_left_and_right!(core::num::NonZeroU16, nonzero_check_rotate_for_u16);
2622-
nonzero_check_rotate_left_and_right!(core::num::NonZeroU32, nonzero_check_rotate_for_u32);
2623-
nonzero_check_rotate_left_and_right!(core::num::NonZeroU64, nonzero_check_rotate_for_u64);
2624-
nonzero_check_rotate_left_and_right!(core::num::NonZeroU128, nonzero_check_rotate_for_u128);
2625-
nonzero_check_rotate_left_and_right!(core::num::NonZeroUsize, nonzero_check_rotate_for_usize);
26262579
}

library/core/src/ptr/alignment.rs

+4-48
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use crate::{cmp, fmt, hash, mem, num};
1616
#[unstable(feature = "ptr_alignment_type", issue = "102070")]
1717
#[derive(Copy, Clone, PartialEq, Eq)]
1818
#[repr(transparent)]
19-
#[invariant(self.as_usize().is_power_of_two())]
19+
// uses .0 instead of .as_usize() to permit proving as_usize so that its proof does not itself use
20+
// as_usize
21+
#[invariant((self.0 as usize).is_power_of_two())]
2022
pub struct Alignment(AlignmentEnum);
2123

2224
// Alignment is `repr(usize)`, but via extra steps.
@@ -404,56 +406,10 @@ mod verify {
404406
}
405407
}
406408

407-
/// FIXME, c.f. https://github.com/model-checking/kani/issues/3905
409+
// FIXME, c.f. https://github.com/model-checking/kani/issues/3905
408410
// // pub const fn of<T>() -> Self
409411
// #[kani::proof_for_contract(Alignment::of)]
410412
// pub fn check_of_i32() {
411413
// let _ = Alignment::of::<i32>();
412414
// }
413-
414-
// pub const fn new(align: usize) -> Option<Self>
415-
#[kani::proof_for_contract(Alignment::new)]
416-
pub fn check_new() {
417-
let a = kani::any::<usize>();
418-
let _ = Alignment::new(a);
419-
}
420-
421-
// pub const unsafe fn new_unchecked(align: usize) -> Self
422-
#[kani::proof_for_contract(Alignment::new_unchecked)]
423-
pub fn check_new_unchecked() {
424-
let a = kani::any::<usize>();
425-
unsafe {
426-
let _ = Alignment::new_unchecked(a);
427-
}
428-
}
429-
430-
// pub const fn as_usize(self) -> usize
431-
#[kani::proof_for_contract(Alignment::as_usize)]
432-
pub fn check_as_usize() {
433-
let a = kani::any::<usize>();
434-
if let Some(alignment) = Alignment::new(a) {
435-
assert_eq!(alignment.as_usize(), a);
436-
}
437-
}
438-
439-
// pub const fn as_nonzero(self) -> NonZero<usize>
440-
#[kani::proof_for_contract(Alignment::as_nonzero)]
441-
pub fn check_as_nonzero() {
442-
let alignment = kani::any::<Alignment>();
443-
let _ = alignment.as_nonzero();
444-
}
445-
446-
// pub const fn log2(self) -> u32
447-
#[kani::proof_for_contract(Alignment::log2)]
448-
pub fn check_log2() {
449-
let alignment = kani::any::<Alignment>();
450-
let _ = alignment.log2();
451-
}
452-
453-
// pub const fn mask(self) -> usize
454-
#[kani::proof_for_contract(Alignment::mask)]
455-
pub fn check_mask() {
456-
let alignment = kani::any::<Alignment>();
457-
let _ = alignment.mask();
458-
}
459415
}

0 commit comments

Comments
 (0)