From ecbdb14bf4264cb2012a614f51626a61f30d2eb8 Mon Sep 17 00:00:00 2001 From: "Felipe R. Monteiro" Date: Tue, 28 Jan 2025 16:42:33 -0500 Subject: [PATCH 001/161] Toolchain upgrade to nightly-2025-01-28 (#3855) Resolves https://github.com/model-checking/kani/issues/3854. This upgrade requires changes to remove `RunCompiler` due to the following changes: - https://github.com/rust-lang/rust/commit/a77776cc1d Remove RunCompiler By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Signed-off-by: Felipe R. Monteiro --- kani-compiler/src/kani_compiler.rs | 15 ++++++++------- kani-compiler/src/main.rs | 5 ++--- kani-compiler/src/session.rs | 2 +- rust-toolchain.toml | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/kani-compiler/src/kani_compiler.rs b/kani-compiler/src/kani_compiler.rs index 253a9b88a52c..e4bfba3113b0 100644 --- a/kani-compiler/src/kani_compiler.rs +++ b/kani-compiler/src/kani_compiler.rs @@ -3,7 +3,7 @@ //! This module defines all compiler extensions that form the Kani compiler. //! -//! The [KaniCompiler] can be used across multiple rustc driver runs ([RunCompiler::run()]), +//! The [KaniCompiler] can be used across multiple rustc driver runs ([`rustc_driver::run_compiler`]), //! which is used to implement stubs. //! //! In the first run, [KaniCompiler::config] will implement the compiler configuration and it will @@ -25,7 +25,7 @@ use crate::kani_queries::QueryDb; use crate::session::init_session; use clap::Parser; use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_driver::{Callbacks, Compilation, RunCompiler}; +use rustc_driver::{Callbacks, Compilation, run_compiler}; use rustc_interface::Config; use rustc_middle::ty::TyCtxt; use rustc_session::config::ErrorOutputType; @@ -34,7 +34,7 @@ use std::sync::{Arc, Mutex}; use tracing::debug; /// Run the Kani flavour of the compiler. -/// This may require multiple runs of the rustc driver ([RunCompiler::run]). +/// This may require multiple runs of the rustc driver ([`rustc_driver::run_compiler`]). pub fn run(args: Vec) { let mut kani_compiler = KaniCompiler::new(); kani_compiler.run(args); @@ -96,10 +96,7 @@ impl KaniCompiler { /// actually invoke the rust compiler multiple times. pub fn run(&mut self, args: Vec) { debug!(?args, "run_compilation_session"); - let queries = self.queries.clone(); - let mut compiler = RunCompiler::new(&args, self); - compiler.set_make_codegen_backend(Some(Box::new(move |_cfg| backend(queries)))); - compiler.run(); + run_compiler(&args, self); } } @@ -108,6 +105,10 @@ impl Callbacks for KaniCompiler { /// Configure the [KaniCompiler] `self` object during the [CompilationStage::Init]. fn config(&mut self, config: &mut Config) { let mut args = vec!["kani-compiler".to_string()]; + config.make_codegen_backend = Some(Box::new({ + let queries = self.queries.clone(); + move |_cfg| backend(queries) + })); args.extend(config.opts.cg.llvm_args.iter().cloned()); let args = Arguments::parse_from(args); init_session(&args, matches!(config.opts.error_format, ErrorOutputType::Json { .. })); diff --git a/kani-compiler/src/main.rs b/kani-compiler/src/main.rs index 65424f6d46bf..c8a56d3ef062 100644 --- a/kani-compiler/src/main.rs +++ b/kani-compiler/src/main.rs @@ -50,7 +50,7 @@ mod kani_middle; mod kani_queries; mod session; -use rustc_driver::{RunCompiler, TimePassesCallbacks}; +use rustc_driver::{TimePassesCallbacks, run_compiler}; use std::env; /// Main function. Configure arguments and run the compiler. @@ -63,8 +63,7 @@ fn main() { kani_compiler::run(rustc_args); } else { let mut callbacks = TimePassesCallbacks::default(); - let compiler = RunCompiler::new(&rustc_args, &mut callbacks); - compiler.run(); + run_compiler(&rustc_args, &mut callbacks); } } diff --git a/kani-compiler/src/session.rs b/kani-compiler/src/session.rs index f448afb801cc..5b4990790a10 100644 --- a/kani-compiler/src/session.rs +++ b/kani-compiler/src/session.rs @@ -57,7 +57,7 @@ static JSON_PANIC_HOOK: LazyLock) + Sync + let mut emitter = JsonEmitter::new( Box::new(io::BufWriter::new(io::stderr())), #[allow(clippy::arc_with_non_send_sync)] - Lrc::new(SourceMap::new(FilePathMapping::empty())), + Some(Lrc::new(SourceMap::new(FilePathMapping::empty()))), fallback_bundle, false, HumanReadableErrorType::Default, diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9ae762092eb2..3741e551a5b8 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-01-24" +channel = "nightly-2025-01-28" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From cc07375c43da544f202c44b298642f9bcdaef4f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Delmas?= Date: Tue, 28 Jan 2025 16:32:53 -0600 Subject: [PATCH 002/161] Allow multiple annotations, but check for duplicate targets. (#3808) Resolves #3804. Enables multiple `stub_verified(TARGET)` annotations on a harness, but still detect and report duplicate targets. Adds positive and negative tests. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Remi Delmas Co-authored-by: Celina G. Val Co-authored-by: Felipe R. Monteiro --- kani-compiler/src/kani_middle/attributes.rs | 32 ++++++++++++++++--- .../missing_contract_for_replace.expected | 2 +- .../multiple_replace_fail.expected | 1 + .../multiple_replace_fail.rs | 31 ++++++++++++++++++ .../multiple_replace_pass.expected | 1 + .../multiple_replace_pass.rs | 30 +++++++++++++++++ 6 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 tests/expected/function-contract/multiple_replace_fail.expected create mode 100644 tests/expected/function-contract/multiple_replace_fail.rs create mode 100644 tests/expected/function-contract/multiple_replace_pass.expected create mode 100644 tests/expected/function-contract/multiple_replace_pass.rs diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index 6d366f432e2a..bdd6db831d6c 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -394,7 +394,7 @@ impl<'tcx> KaniAttributes<'tcx> { attrs.iter().for_each(|attr| self.check_proof_attribute(kind, attr)) } KaniAttributeKind::StubVerified => { - expect_single(self.tcx, kind, &attrs); + self.check_stub_verified(); } KaniAttributeKind::FnMarker | KaniAttributeKind::CheckedWith @@ -591,15 +591,29 @@ impl<'tcx> KaniAttributes<'tcx> { } } - fn handle_stub_verified(&self, harness: &mut HarnessAttributes) { + fn check_stub_verified(&self) { let dcx = self.tcx.dcx(); + let mut seen = HashSet::new(); for (name, def_id, span) in self.interpret_stub_verified_attribute() { + if seen.contains(&name) { + dcx.struct_span_warn( + span, + format!("Multiple occurrences of `stub_verified({})`.", name), + ) + .with_span_note( + self.tcx.def_span(def_id), + format!("Use a single `stub_verified({})` annotation.", name), + ) + .emit(); + } else { + seen.insert(name); + } if KaniAttributes::for_item(self.tcx, def_id).contract_attributes().is_none() { dcx.struct_span_err( span, format!( - "Failed to generate verified stub: Function `{}` has no contract.", - self.item_name(), + "Target function in `stub_verified({})` has no contract.", + name, ), ) .with_span_note( @@ -612,6 +626,16 @@ impl<'tcx> KaniAttributes<'tcx> { .emit(); return; } + } + } + + /// Adds the verified stub names to the `harness.verified_stubs`. + /// + /// This method must be called after `check_stub_verified`, to ensure that + /// the target names are known and have contracts, and there are no + /// duplicate target names. + fn handle_stub_verified(&self, harness: &mut HarnessAttributes) { + for (name, _, _) in self.interpret_stub_verified_attribute() { harness.verified_stubs.push(name.to_string()) } } diff --git a/tests/expected/function-contract/missing_contract_for_replace.expected b/tests/expected/function-contract/missing_contract_for_replace.expected index 29f3fa955307..198838feca03 100644 --- a/tests/expected/function-contract/missing_contract_for_replace.expected +++ b/tests/expected/function-contract/missing_contract_for_replace.expected @@ -1,4 +1,4 @@ -error: Failed to generate verified stub: Function `harness` has no contract. +error: Target function in `stub_verified(no_contract)` has no contract. | 8 | #[kani::stub_verified(no_contract)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/expected/function-contract/multiple_replace_fail.expected b/tests/expected/function-contract/multiple_replace_fail.expected new file mode 100644 index 000000000000..5e828259fcb6 --- /dev/null +++ b/tests/expected/function-contract/multiple_replace_fail.expected @@ -0,0 +1 @@ +warning: Multiple occurrences of `stub_verified(one)`. diff --git a/tests/expected/function-contract/multiple_replace_fail.rs b/tests/expected/function-contract/multiple_replace_fail.rs new file mode 100644 index 000000000000..ecd3c099b2ab --- /dev/null +++ b/tests/expected/function-contract/multiple_replace_fail.rs @@ -0,0 +1,31 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +#[kani::ensures(|result : &u32| *result == 1)] +fn one() -> u32 { + 1 +} + +#[kani::proof_for_contract(one)] +fn check_one() { + let _ = one(); +} + +#[kani::ensures(|result : &u32| *result == 1)] +fn one_too() -> u32 { + 1 +} + +#[kani::proof_for_contract(one_too)] +fn check_one_too() { + let _ = one_too(); +} + +#[kani::proof] +#[kani::stub_verified(one)] +#[kani::stub_verified(one)] +#[kani::stub_verified(one_too)] +fn main() { + assert_eq!(one(), one_too()); +} diff --git a/tests/expected/function-contract/multiple_replace_pass.expected b/tests/expected/function-contract/multiple_replace_pass.expected new file mode 100644 index 000000000000..34c886c358cb --- /dev/null +++ b/tests/expected/function-contract/multiple_replace_pass.expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/multiple_replace_pass.rs b/tests/expected/function-contract/multiple_replace_pass.rs new file mode 100644 index 000000000000..bd3cb81e136a --- /dev/null +++ b/tests/expected/function-contract/multiple_replace_pass.rs @@ -0,0 +1,30 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +#[kani::ensures(|result : &u32| *result == 1)] +fn one() -> u32 { + 1 +} + +#[kani::proof_for_contract(one)] +fn check_one() { + let _ = one(); +} + +#[kani::ensures(|result : &u32| *result == 1)] +fn one_too() -> u32 { + 1 +} + +#[kani::proof_for_contract(one_too)] +fn check_one_too() { + let _ = one_too(); +} + +#[kani::proof] +#[kani::stub_verified(one)] +#[kani::stub_verified(one_too)] +fn main() { + assert_eq!(one(), one_too()); +} From ad91bfacd691d8a2f9c20f6b75c09923a061d60e Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 28 Jan 2025 16:58:42 -0600 Subject: [PATCH 003/161] Move documentation of kani_core modules to right places (#3851) Resolves #3815 [Documentations](https://github.com/model-checking/kani/blob/main/library/kani_core/src/mem.rs#L3) of `kani_core` modules doesn't show up correctly on https://model-checking.github.io/kani/crates/doc/kani/mem/index.html. This PR move the comments to the right place so that they will show up correctly. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- library/kani_core/src/float.rs | 2 +- library/kani_core/src/lib.rs | 50 +++++++++++++++++++++++++++++++ library/kani_core/src/mem.rs | 40 +++---------------------- library/kani_core/src/mem_init.rs | 15 ++-------- 4 files changed, 57 insertions(+), 50 deletions(-) diff --git a/library/kani_core/src/float.rs b/library/kani_core/src/float.rs index 44475af80a51..55fe71dbf24d 100644 --- a/library/kani_core/src/float.rs +++ b/library/kani_core/src/float.rs @@ -1,7 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -//! This module contains functions useful for float-related checks +// This module contains functions useful for float-related checks. #[allow(clippy::crate_in_macro_def)] #[macro_export] diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index 3ba2f459470e..37a05821bd33 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -67,14 +67,64 @@ macro_rules! kani_lib { kani_core::generate_models!(); pub mod float { + //! This module contains functions useful for float-related checks kani_core::generate_float!(std); } pub mod mem { + //! This module contains functions useful for checking unsafe memory access. + //! + //! Given the following validity rules provided in the Rust documentation: + //! (accessed Feb 6th, 2024) + //! + //! 1. A null pointer is never valid, not even for accesses of size zero. + //! 2. For a pointer to be valid, it is necessary, but not always sufficient, that the pointer + //! be dereferenceable: the memory range of the given size starting at the pointer must all be + //! within the bounds of a single allocated object. Note that in Rust, every (stack-allocated) + //! variable is considered a separate allocated object. + //! ~~Even for operations of size zero, the pointer must not be pointing to deallocated memory, + //! i.e., deallocation makes pointers invalid even for zero-sized operations.~~ + //! ZST access is not OK for any pointer. + //! See: + //! 3. However, casting any non-zero integer literal to a pointer is valid for zero-sized + //! accesses, even if some memory happens to exist at that address and gets deallocated. + //! This corresponds to writing your own allocator: allocating zero-sized objects is not very + //! hard. The canonical way to obtain a pointer that is valid for zero-sized accesses is + //! `NonNull::dangling`. + //! 4. All accesses performed by functions in this module are non-atomic in the sense of atomic + //! operations used to synchronize between threads. + //! This means it is undefined behavior to perform two concurrent accesses to the same location + //! from different threads unless both accesses only read from memory. + //! Notice that this explicitly includes `read_volatile` and `write_volatile`: + //! Volatile accesses cannot be used for inter-thread synchronization. + //! 5. The result of casting a reference to a pointer is valid for as long as the underlying + //! object is live and no reference (just raw pointers) is used to access the same memory. + //! That is, reference and pointer accesses cannot be interleaved. + //! + //! Kani is able to verify #1 and #2 today. + //! + //! For #3, we are overly cautious, and Kani will only consider zero-sized pointer access safe if + //! the address matches `NonNull::<()>::dangling()`. + //! The way Kani tracks provenance is not enough to check if the address was the result of a cast + //! from a non-zero integer literal. + //! kani_core::kani_mem!(std); } mod mem_init { + //! This module provides instrumentation for tracking memory initialization of raw pointers. + //! + //! Currently, memory initialization is tracked on per-byte basis, so each byte of memory pointed to + //! by raw pointers could be either initialized or uninitialized. Padding bytes are always + //! considered uninitialized when read as data bytes. Each type has a type layout to specify which + //! bytes are considered to be data and which -- padding. This is determined at compile time and + //! statically injected into the program (see `Layout`). + //! + //! Compiler automatically inserts calls to `is_xxx_initialized` and `set_xxx_initialized` at + //! appropriate locations to get or set the initialization status of the memory pointed to. + //! + //! Note that for each harness, tracked object and tracked offset are chosen non-deterministically, + //! so calls to `is_xxx_initialized` should be only used in assertion contexts. kani_core::kani_mem_init!(std); } }; diff --git a/library/kani_core/src/mem.rs b/library/kani_core/src/mem.rs index c09d67ca51ff..baf90ed98158 100644 --- a/library/kani_core/src/mem.rs +++ b/library/kani_core/src/mem.rs @@ -1,41 +1,9 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -//! This module contains functions useful for checking unsafe memory access. -//! -//! Given the following validity rules provided in the Rust documentation: -//! (accessed Feb 6th, 2024) -//! -//! 1. A null pointer is never valid, not even for accesses of size zero. -//! 2. For a pointer to be valid, it is necessary, but not always sufficient, that the pointer -//! be dereferenceable: the memory range of the given size starting at the pointer must all be -//! within the bounds of a single allocated object. Note that in Rust, every (stack-allocated) -//! variable is considered a separate allocated object. -//! ~~Even for operations of size zero, the pointer must not be pointing to deallocated memory, -//! i.e., deallocation makes pointers invalid even for zero-sized operations.~~ -//! ZST access is not OK for any pointer. -//! See: -//! 3. However, casting any non-zero integer literal to a pointer is valid for zero-sized -//! accesses, even if some memory happens to exist at that address and gets deallocated. -//! This corresponds to writing your own allocator: allocating zero-sized objects is not very -//! hard. The canonical way to obtain a pointer that is valid for zero-sized accesses is -//! `NonNull::dangling`. -//! 4. All accesses performed by functions in this module are non-atomic in the sense of atomic -//! operations used to synchronize between threads. -//! This means it is undefined behavior to perform two concurrent accesses to the same location -//! from different threads unless both accesses only read from memory. -//! Notice that this explicitly includes `read_volatile` and `write_volatile`: -//! Volatile accesses cannot be used for inter-thread synchronization. -//! 5. The result of casting a reference to a pointer is valid for as long as the underlying -//! object is live and no reference (just raw pointers) is used to access the same memory. -//! That is, reference and pointer accesses cannot be interleaved. -//! -//! Kani is able to verify #1 and #2 today. -//! -//! For #3, we are overly cautious, and Kani will only consider zero-sized pointer access safe if -//! the address matches `NonNull::<()>::dangling()`. -//! The way Kani tracks provenance is not enough to check if the address was the result of a cast -//! from a non-zero integer literal. -//! + +// This module contains functions useful for checking unsafe memory access. +// For full documentation, see the usage of `kani_core::kani_mem!(std);` in library/kani_core/src/lib.rs + // TODO: This module is currently tightly coupled with CBMC's memory model, and it needs some // refactoring to be used with other backends. diff --git a/library/kani_core/src/mem_init.rs b/library/kani_core/src/mem_init.rs index 935f14d71c5d..3fafcb1d8388 100644 --- a/library/kani_core/src/mem_init.rs +++ b/library/kani_core/src/mem_init.rs @@ -1,19 +1,8 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -//! This module provides instrumentation for tracking memory initialization of raw pointers. -//! -//! Currently, memory initialization is tracked on per-byte basis, so each byte of memory pointed to -//! by raw pointers could be either initialized or uninitialized. Padding bytes are always -//! considered uninitialized when read as data bytes. Each type has a type layout to specify which -//! bytes are considered to be data and which -- padding. This is determined at compile time and -//! statically injected into the program (see `Layout`). -//! -//! Compiler automatically inserts calls to `is_xxx_initialized` and `set_xxx_initialized` at -//! appropriate locations to get or set the initialization status of the memory pointed to. -//! -//! Note that for each harness, tracked object and tracked offset are chosen non-deterministically, -//! so calls to `is_xxx_initialized` should be only used in assertion contexts. +// This module provides instrumentation for tracking memory initialization of raw pointers. +// For full documentation, see the usage of `kani_core::kani_mem_init!(std);` in library/kani_core/src/lib.rs // Definitions in this module are not meant to be visible to the end user, only the compiler. #![allow(dead_code)] From 53013f35d8a78db685e2ce6cf95b0146df148ecb Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 29 Jan 2025 00:24:15 -0800 Subject: [PATCH 004/161] Fix missing function declaration issue (#3862) In cases where a function pointer is created, but its value is never used, Kani crashes due to missing function declaration. For now, collect any instance that has its address taken even if its never used. Fixes #3799 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- kani-compiler/src/kani_middle/reachability.rs | 16 +++++++++++ tests/kani/CodegenMisc/missing_mut_fn.rs | 27 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/kani/CodegenMisc/missing_mut_fn.rs diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index dea7cca60844..2267af56a684 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -470,6 +470,22 @@ impl MirVisitor for MonoItemsFnCollector<'_, '_> { self.super_terminator(terminator, location); } + + /// Collect any function definition that may occur as a type. + /// + /// The codegen stage will require the definition to be available. + /// This is a conservative approach, since there are cases where the function is never + /// actually used, so we don't need the body. + /// + /// Another alternative would be to lazily declare functions, but it would require a bigger + /// change to codegen. + fn visit_ty(&mut self, ty: &Ty, _: Location) { + if let TyKind::RigidTy(RigidTy::FnDef(def, args)) = ty.kind() { + let instance = Instance::resolve(def, &args).unwrap(); + self.collect_instance(instance, true); + } + self.super_ty(ty); + } } fn extract_unsize_coercion(tcx: TyCtxt, orig_ty: Ty, dst_trait: Ty) -> (Ty, Ty) { diff --git a/tests/kani/CodegenMisc/missing_mut_fn.rs b/tests/kani/CodegenMisc/missing_mut_fn.rs new file mode 100644 index 000000000000..ae7bfe753bf0 --- /dev/null +++ b/tests/kani/CodegenMisc/missing_mut_fn.rs @@ -0,0 +1,27 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +//! Ensure Kani can codegen code with a pointer to a function that is never used +//! See for more details. +fn foo(_func: &mut F) {} +fn foo_dyn(_func: &mut dyn Fn()) {} + +#[kani::proof] +fn check_foo() { + fn f() {} + + foo(&mut f); +} + +#[kani::proof] +fn check_foo_dyn() { + fn f() {} + + foo_dyn(&mut f); +} + +#[kani::proof] +fn check_foo_unused() { + fn f() {} + + let ptr = &mut f; +} From 5024b631d83c6b20a6e45f1b9ba7c6f51bf3e4b6 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 29 Jan 2025 11:02:27 -0800 Subject: [PATCH 005/161] Fix transmute codegen when sizes are different (#3861) Instead of crashing, add a safety check that ensures the transmute is not reachable. Resolves #3839 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../codegen_cprover_gotoc/codegen/rvalue.rs | 26 ++++++++++- .../intrinsics/transmute_diff_size.expected | 2 + .../intrinsics/transmute_diff_size.rs | 33 ++++++++++++++ .../transmute_unchecked_size.expected | 24 ++++++++++ .../intrinsics/transmute_unchecked_size.rs | 44 +++++++++++++++++++ 5 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 tests/expected/intrinsics/transmute_diff_size.expected create mode 100644 tests/expected/intrinsics/transmute_diff_size.rs create mode 100644 tests/expected/intrinsics/transmute_unchecked_size.expected create mode 100644 tests/expected/intrinsics/transmute_unchecked_size.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index f4ca557afbdb..7f5568ccf152 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -801,8 +801,30 @@ impl GotocCtx<'_> { self.codegen_pointer_cast(k, e, *t, loc) } Rvalue::Cast(CastKind::Transmute, operand, ty) => { - let goto_typ = self.codegen_ty_stable(*ty); - self.codegen_operand_stable(operand).transmute_to(goto_typ, &self.symbol_table) + let src_ty = operand.ty(self.current_fn().locals()).unwrap(); + // Transmute requires sized types. + let src_sz = LayoutOf::new(src_ty).size_of().unwrap(); + let dst_sz = LayoutOf::new(*ty).size_of().unwrap(); + let dst_type = self.codegen_ty_stable(*ty); + if src_sz != dst_sz { + Expr::statement_expression( + vec![ + self.codegen_assert_assume_false( + PropertyClass::SafetyCheck, + &format!( + "Cannot transmute between types of different sizes. \ + Transmuting from `{src_sz}` to `{dst_sz}` bytes" + ), + loc, + ), + dst_type.nondet().as_stmt(loc), + ], + dst_type, + loc, + ) + } else { + self.codegen_operand_stable(operand).transmute_to(dst_type, &self.symbol_table) + } } Rvalue::BinaryOp(op, e1, e2) => self.codegen_rvalue_binary_op(res_ty, op, e1, e2, loc), Rvalue::CheckedBinaryOp(op, e1, e2) => { diff --git a/tests/expected/intrinsics/transmute_diff_size.expected b/tests/expected/intrinsics/transmute_diff_size.expected new file mode 100644 index 000000000000..efc770882422 --- /dev/null +++ b/tests/expected/intrinsics/transmute_diff_size.expected @@ -0,0 +1,2 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types +error: aborting due to 3 previous errors diff --git a/tests/expected/intrinsics/transmute_diff_size.rs b/tests/expected/intrinsics/transmute_diff_size.rs new file mode 100644 index 000000000000..a38c37a379d9 --- /dev/null +++ b/tests/expected/intrinsics/transmute_diff_size.rs @@ -0,0 +1,33 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Checks that compilation fails when trying to transmute with different src and target sizes. + +#![feature(core_intrinsics)] +use std::intrinsics::transmute; + +/// This should fail due to UB detection. +#[kani::proof] +pub fn transmute_diff_size() { + let a: u32 = kani::any(); + if kani::any() { + let smaller: u16 = unsafe { transmute(a) }; + std::hint::black_box(smaller); + } else { + let bigger: (u64, isize) = unsafe { transmute(a) }; + std::hint::black_box(bigger); + } +} + +/// Generic transmute wrapper. +pub unsafe fn generic_transmute(src: S) -> D { + transmute(src) +} + +/// This should also fail due to UB detection. +#[kani::proof] +pub fn transmute_wrapper_diff_size() { + let a: (u32, char) = kani::any(); + let b: u128 = unsafe { generic_transmute(a) }; + std::hint::black_box(b); +} diff --git a/tests/expected/intrinsics/transmute_unchecked_size.expected b/tests/expected/intrinsics/transmute_unchecked_size.expected new file mode 100644 index 000000000000..6ff9a1f67366 --- /dev/null +++ b/tests/expected/intrinsics/transmute_unchecked_size.expected @@ -0,0 +1,24 @@ +Checking harness transmute_wrapper_diff_size... +Status: UNREACHABLE\ +Description: ""Unreachable expected"" + +Failed Checks: Cannot transmute between types of different sizes. Transmuting from `8` to `16` bytes + +VERIFICATION:- FAILED + +Checking harness transmute_diff_size... + +Status: UNREACHABLE\ +Description: ""This should never be reached"" + +Status: UNREACHABLE\ +Description: ""Neither this one"" + +Failed Checks: Cannot transmute between types of different sizes. Transmuting from `4` to `2` bytes +Failed Checks: Cannot transmute between types of different sizes. Transmuting from `4` to `16` bytes + +VERIFICATION:- FAILED + +Verification failed for - transmute_wrapper_diff_size +Verification failed for - transmute_diff_size +0 successfully verified harnesses, 2 failures, 2 total diff --git a/tests/expected/intrinsics/transmute_unchecked_size.rs b/tests/expected/intrinsics/transmute_unchecked_size.rs new file mode 100644 index 000000000000..704448dcc6c6 --- /dev/null +++ b/tests/expected/intrinsics/transmute_unchecked_size.rs @@ -0,0 +1,44 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Checks that Kani correctly identify UB when invoking `transmute_unchecked` with different sizes. +//! See for more details. + +#![feature(core_intrinsics)] +use std::intrinsics::transmute_unchecked; + +/// Kani reachability checks are not currently applied to `unreachable` statements. +macro_rules! unreachable { + ($msg:literal) => { + assert!(false, $msg) + }; +} + +/// This should fail due to UB detection. +#[kani::proof] +pub fn transmute_diff_size() { + let a: u32 = kani::any(); + if kani::any() { + let smaller: u16 = unsafe { transmute_unchecked(a) }; + std::hint::black_box(smaller); + unreachable!("This should never be reached"); + } else { + let bigger: (u64, isize) = unsafe { transmute_unchecked(a) }; + std::hint::black_box(bigger); + unreachable!("Neither this one"); + } +} + +/// Generic transmute wrapper. +pub unsafe fn generic_transmute(src: S) -> D { + transmute_unchecked(src) +} + +/// This should also fail due to UB detection. +#[kani::proof] +pub fn transmute_wrapper_diff_size() { + let a: (u32, char) = kani::any(); + let b: u128 = unsafe { generic_transmute(a) }; + std::hint::black_box(b); + unreachable!("Unreachable expected"); +} From afd0469fa1f41b838037c1a961b1ef18642de48e Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Thu, 30 Jan 2025 02:31:47 -0800 Subject: [PATCH 006/161] Remove symtab2gb from bundle (#3865) CBMC's `symtab2gb` binary is no longer used since Kani now generates a goto binary directly. Removing it from the Kani bundle. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- src/os_hacks.rs | 3 +-- tools/build-kani/src/main.rs | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/os_hacks.rs b/src/os_hacks.rs index 8d3a20575d7d..0a149a005934 100644 --- a/src/os_hacks.rs +++ b/src/os_hacks.rs @@ -81,8 +81,7 @@ fn setup_nixos_patchelf(kani_dir: &Path) -> Result<()> { for filename in &["kani-compiler", "kani-driver"] { patch_interp(&bin.join(filename))?; } - for filename in &["cbmc", "goto-analyzer", "goto-cc", "goto-instrument", "kissat", "symtab2gb"] - { + for filename in &["cbmc", "goto-analyzer", "goto-cc", "goto-instrument", "kissat"] { let file = bin.join(filename); patch_interp(&file)?; patch_rpath(&file)?; diff --git a/tools/build-kani/src/main.rs b/tools/build-kani/src/main.rs index c45e9a72b189..3ff017e37509 100644 --- a/tools/build-kani/src/main.rs +++ b/tools/build-kani/src/main.rs @@ -138,7 +138,6 @@ fn bundle_cbmc(dir: &Path) -> Result<()> { cp(&which::which("cbmc")?, &bin)?; cp(&which::which("goto-instrument")?, &bin)?; cp(&which::which("goto-cc")?, &bin)?; - cp(&which::which("symtab2gb")?, &bin)?; cp(&which::which("goto-analyzer")?, &bin)?; Ok(()) From edb1173c76eaabfe3d8f014e39c22edfe09d7fe7 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Fri, 31 Jan 2025 14:00:22 -0800 Subject: [PATCH 007/161] Update the rustc hack for CLion / RustRover (#3868) The old hack doesn't work anymore. I pulled this from #3546 per PR comment By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- docs/src/rustc-hacks.md | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/docs/src/rustc-hacks.md b/docs/src/rustc-hacks.md index a6047613288a..9ee5b522d894 100644 --- a/docs/src/rustc-hacks.md +++ b/docs/src/rustc-hacks.md @@ -52,19 +52,27 @@ Note that pretty printing for the Rust nightly toolchain (which Kani uses) is no For example, a vector may be displayed as `vec![{...}, {...}]` on nightly Rust, when it would be displayed as `vec![Some(0), None]` on stable Rust. Hopefully, this will be fixed soon. -### CLion / IntelliJ +### RustRover / CLion This is not a great solution, but it works for now (see for more details). -Edit the `Cargo.toml` of the package that you're working on and add artificial dependencies on the `rustc` packages that you would like to explore. + +Open the `Cargo.toml` of your crate (e.g.: `kani-compiler`), and do the following: + +1. Add optional dependencies on the `rustc` crates you are using. +2. Add a feature that enable those dependencies. +3. Toggle that feature using the IDE GUI. + +Here is an example: ```toml -# This configuration doesn't exist so it shouldn't affect your build. -[target.'cfg(KANI_DEV)'.dependencies] +# ** At the bottom of the dependencies section: ** # Adjust the path here to point to a local copy of the rust compiler. -# The best way is to use the rustup path. Replace with the -# proper name to your toolchain. -rustc_driver = { path = "~/.rustup/toolchains//lib/rustlib/rustc-src/rust/compiler/rustc_driver" } -rustc_interface = { path = "~/.rustup/toolchains//lib/rustlib/rustc-src/rust/compiler/rustc_interface" } +# E.g.: ~/.rustup/toolchains//lib/rustlib/rustc-src/rust/compiler +rustc_smir = { path = "/rustc_smir", optional = true } +stable_mir = { path = "/stable_mir", optional = true } + +[features] +clion = ['rustc_smir', 'stable_mir'] ``` **Don't forget to rollback the changes before you create your PR.** From 9872f12a1cf7cf4833e5e198d393415de3739b21 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 18:00:12 +0000 Subject: [PATCH 008/161] Bump tests/perf/s2n-quic from `4500593` to `82dd0b5` (#3872) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `4500593` to `82dd0b5`.
Commits
  • 82dd0b5 fix: process packets from different sources before handshake confirmed (#2463)
  • 8bfc8a8 build(deps): bump aws-actions/configure-aws-credentials (#2461)
  • eace166 fix: ignore local address when considering path migration (#2458)
  • 75c64e4 Revert "fix(s2n-quic-dc): derive crypto before opening TCP stream (#2451)" (#...
  • See full diff in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 450059379ac2..82dd0b595e7e 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 450059379ac24f9ba73c8ac940b1c6820af401ce +Subproject commit 82dd0b595e7e9e58531ff26b908fd0f6bbc36e45 From b29868a4cd7b64f7dfb0164bb6c15f682a5f3e61 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 11:24:07 -0800 Subject: [PATCH 009/161] Automatic cargo update to 2025-02-03 (#3869) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 76 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a047b63f88b..a009b26b7303 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -230,9 +230,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.10" +version = "1.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf" dependencies = [ "shlex", ] @@ -656,7 +656,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets", ] [[package]] @@ -1007,7 +1019,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -1335,7 +1347,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -1447,9 +1459,9 @@ checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "same-file" @@ -1518,9 +1530,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.137" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "indexmap", "itoa", @@ -1669,9 +1681,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -1686,13 +1698,13 @@ checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "tempfile" -version = "3.15.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" dependencies = [ "cfg-if", "fastrand", - "getrandom", + "getrandom 0.3.1", "once_cell", "rustix", "windows-sys 0.59.0", @@ -1834,9 +1846,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" dependencies = [ "indexmap", "serde", @@ -1959,9 +1971,9 @@ dependencies = [ [[package]] name = "tree-sitter-language" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c199356c799a8945965bb5f2c55b2ad9d9aa7c4b4f6e587fe9dea0bc715e5f9c" +checksum = "38eee4db33814de3d004de9d8d825627ed3320d0989cce0dea30efaf5be4736c" [[package]] name = "tree-sitter-rust" @@ -1975,9 +1987,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "unicode-segmentation" @@ -2046,6 +2058,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "which" version = "7.0.1" @@ -2173,9 +2194,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.24" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +checksum = "7e49d2d35d3fad69b39b94139037ecfb4f359f08958b9c11e7315ce770462419" dependencies = [ "memchr", ] @@ -2186,6 +2207,15 @@ version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + [[package]] name = "zerocopy" version = "0.7.35" From 380b1fb096c393407134258c8ced2c46661449f6 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 4 Feb 2025 13:15:07 -0600 Subject: [PATCH 010/161] Add reference for loop contracts (#3849) This PR adds reference of loop contracts: Rendered version: https://github.com/qinheping/kani/blob/docs/loop-contracts/docs/src/reference/experimental/loop-contracts.md By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> --- .../reference/experimental/loop-contracts.md | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 docs/src/reference/experimental/loop-contracts.md diff --git a/docs/src/reference/experimental/loop-contracts.md b/docs/src/reference/experimental/loop-contracts.md new file mode 100644 index 000000000000..3cf5ecd429cc --- /dev/null +++ b/docs/src/reference/experimental/loop-contracts.md @@ -0,0 +1,154 @@ +# Loop Contracts + +Loop contract are used to specify invariants for loops for the sake of extending Kani's *bounded proofs* to *unbounded proofs*. +A [loop invariant](https://en.wikipedia.org/wiki/Loop_invariant) is an expression that holds upon entering a loop and after every execution of the loop body. +It captures something that does not change about every step of the loop. + +It is worth revisiting the discussion about [bounded proof](../../tutorial-loop-unwinding.md#bounded-proof) and +[loop unwinding](../../tutorial-loop-unwinding.md#loops-unwinding-and-bounds). In short, bounds on the number of times Kani unwinds loops also bound the size of inputs, +and hence result in a bounded proof. +Loop contracts are used to abstract out loops as non-loop blocks to avoid loop unwinding, and hence remove the bounds on the inputs. + +Consider the following example: + +``` Rust +fn simple_loop() { + let mut x: u64 = kani::any_where(|i| *i >= 1); + + while x > 1 { + x = x - 1; + } + + assert!(x == 1); +} +``` + +In this program, the loop repeatedly decrements `x` until it equals `1`. Because we haven't specified an upper bound for `x`, to verify this function, +Kani needs to unwind the loop for `u64::MAX` iterations, which is computationally expensive. Loop contracts allow us to abstract the loop behavior, significantly reducing the verification cost. + +With loop contracts, we can specify the loop’s behavior using invariants. For example: + +``` Rust +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + +fn simple_loop_with_loop_contracts() { + let mut x: u64 = kani::any_where(|i| *i >= 1); + + #[kani::loop_invariant(x >= 1)] + while x > 1 { + x = x - 1; + } + + assert!(x == 1); +} +``` + +Here, the loop invariant `#[kani::loop_invariant(x >= 1)]` specifies that the condition `x >= 1` must hold true at the start of each iteration before the loop guard is +checked. Once Kani verifies that the loop invariant is inductive, it will use the invariant to abstract the loop and avoid unwinding. + +Now let's run the proof with loop contracts through kani: +``` bash +kani simple_loop_with_loop_contracts.rs -Z loop-contracts +``` +The output reported by Kani on the example will be +``` +... + + +Check 10: simple_loop_with_loop_contracts.loop_invariant_base.1 + - Status: SUCCESS + - Description: "Check invariant before entry for loop simple_loop_with_loop_contracts.0" + - Location: simple_while_loop.rs:15:5 in function simple_loop_with_loop_contracts + +Check 11: simple_loop_with_loop_contracts.loop_assigns.1 + - Status: SUCCESS + - Description: "Check assigns clause inclusion for loop simple_loop_with_loop_contracts.0" + - Location: simple_while_loop.rs:15:5 in function simple_loop_with_loop_contracts + +Check 13: simple_loop_with_loop_contracts.assigns.1 + - Status: SUCCESS + - Description: "Check that x is assignable" + - Location: simple_while_loop.rs:17:9 in function simple_loop_with_loop_contracts + +Check 14: simple_loop_with_loop_contracts.loop_invariant_step.1 + - Status: SUCCESS + - Description: "Check invariant after step for loop simple_loop_with_loop_contracts.0" + - Location: simple_while_loop.rs:15:5 in function simple_loop_with_loop_contracts + +Check 15: simple_loop_with_loop_contracts.loop_invariant_step.2 + - Status: SUCCESS + - Description: "Check invariant after step for loop simple_loop_with_loop_contracts.0" + - Location: simple_while_loop.rs:15:5 in function simple_loop_with_loop_contracts + +... + +SUMMARY: + ** 0 of 99 failed + +VERIFICATION:- SUCCESSFUL +Verification Time: 0.3897019s + +Complete - 1 successfully verified harnesses, 0 failures, 1 total. +``` + + +## Loop contracts for `while` loops + +### Syntax +> +> \#\[kani::loop_invariant\( [_Expression_](https://doc.rust-lang.org/reference/expressions.html) \)\] +> +> `while` [_Expression_](https://doc.rust-lang.org/reference/expressions.html)_except struct expression_ [_BlockExpression_](https://doc.rust-lang.org/reference/expressions/block-expr.html) + + +An invariant contract `#[kani::loop_invariant(cond)]` accepts a valid Boolean expression `cond` over the variables visible at the same scope as the loop. + +### Semantics +A loop invariant contract expands to several assumptions and assertions: +1. The invariant is asserted just before the first iteration. +2. The invariant is assumed on a non-deterministic state to model a non-deterministic iteration. +3. The invariant is finally asserted again to establish its inductiveness. + +Mathematical induction is the working principle here. (1) establishes the base case for induction, and (2) & (3) establish the inductive case. +Therefore, the invariant must hold after the loop execution for any number of iterations. The invariant, together with the negation of the loop guard, +must be sufficient to establish subsequent assertions. If it is not, the abstraction is too imprecise and the user must supply a stronger invariant. + +To illustrate the key idea, we show how Kani abstracts the loop in `simple_loop_with_loop_contracts` as a non-loop block: +``` Rust +assert!(x >= 1) // check loop invariant for the base case. +x = kani::any(); +kani::assume(x >= 1); +if x > 1 { + // proof path 1: + // both loop guard and loop invariant are satisfied. + x = x - 1; + assert!(x >= 1); // check that loop invariant is inductive. + kani::assume(false) // block this proof path. +} +// proof path 2: +// loop invariant is satisfied and loop guard is violated. +assert!(x == 1); +``` +That is, we assume that we are in an arbitrary iteration after checking that the loop invariant holds for the base case. With the inductive hypothesis (`kani::assume(x >= 1);`), +we will either enter the loop (proof path 1) or leave the loop (proof path 2). We prove the two paths separately by killing path 1 with `kani::assume(false);`. +Note that all assertions after `kani::assume(false)` will be ignored as `false => p` can be deduced as `true` for any `p`. + +In proof path 1, we prove properties inside the loop and at last check that the loop invariant is inductive. + +In proof path 2, we prove properties after leaving the loop. As we leave the loop only when the loop guard is violated, the post condition of the loop can be expressed as +`!guard && inv`, which is `x <= 1 && x >= 1` in the example. The postcondition implies `x == 1`—the property we want to prove at the end of `simple_loop_with_loop_contracts`. + + +## Limitations + +Loop contracts comes with the following limitations. + +1. Only `while` loops are supported. The other three kinds of loops are not supported: [`loop` loops](https://doc.rust-lang.org/reference/expressions/loop-expr.html#infinite-loops) + , [`while let` loops](https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-pattern-loops), and [`for` loops](https://doc.rust-lang.org/reference/expressions/loop-expr.html#iterator-loops). +2. Kani infers *loop modifies* with alias analysis. Loop modifies are those variables we assume to be arbitrary in the inductive hypothesis, and should cover all memory locations that are written to during + the execution of the loops. A proof will fail if the inferred loop modifies misses some targets written in the loops. + We observed this happens when some fields of structs are modified by some other functions called in the loops. +3. Kani doesn't check if a loop will always terminate in proofs with loop contracts. So it could be that some properties are proved successfully with Kani but actually are unreachable due to the + non-termination of some loops. +4. We don't check if loop invariants are side-effect free. A loop invariant with a side effect could lead to an unsound proof result. Make sure that the specified loop contracts are side-effect free. From d87dd2341fd297ebd90c84b0aaf0e4d3be44fea1 Mon Sep 17 00:00:00 2001 From: rajath-mk Date: Wed, 5 Feb 2025 09:17:50 -0800 Subject: [PATCH 011/161] remove flag float-overflow-check (#3873) Resolves #3620 Removing default flag float overflow check, which reports overflow for arithmetic operations over floating-point numbers that result in +/-Inf. This doesn't panic in Rust and it shouldn't be consider a verification failure by default. Users can enable them using --cbmc-args as and when required. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- kani-driver/src/call_cbmc.rs | 1 - .../{expected => check_message.expected} | 0 .../float-overflow/check_message.rs | 1 + .../check_message_overflow.expected | 1 + .../float-overflow/check_message_overflow.rs | 22 +++++++++++++++++++ 5 files changed, 24 insertions(+), 1 deletion(-) rename tests/ui/cbmc_checks/float-overflow/{expected => check_message.expected} (100%) create mode 100644 tests/ui/cbmc_checks/float-overflow/check_message_overflow.expected create mode 100644 tests/ui/cbmc_checks/float-overflow/check_message_overflow.rs diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index 4e10cb98c650..b6050d915666 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -233,7 +233,6 @@ impl KaniSession { args.push("--no-pointer-check".into()); } if self.args.checks.overflow_on() { - args.push("--float-overflow-check".into()); args.push("--nan-check".into()); // TODO: Implement conversion checks as an optional check. diff --git a/tests/ui/cbmc_checks/float-overflow/expected b/tests/ui/cbmc_checks/float-overflow/check_message.expected similarity index 100% rename from tests/ui/cbmc_checks/float-overflow/expected rename to tests/ui/cbmc_checks/float-overflow/check_message.expected diff --git a/tests/ui/cbmc_checks/float-overflow/check_message.rs b/tests/ui/cbmc_checks/float-overflow/check_message.rs index 590a2be20230..229bc373a883 100644 --- a/tests/ui/cbmc_checks/float-overflow/check_message.rs +++ b/tests/ui/cbmc_checks/float-overflow/check_message.rs @@ -1,6 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // +// kani-flags: --enable-unstable --cbmc-args --float-overflow-check // Check we don't print temporary variables as part of CBMC messages. extern crate kani; diff --git a/tests/ui/cbmc_checks/float-overflow/check_message_overflow.expected b/tests/ui/cbmc_checks/float-overflow/check_message_overflow.expected new file mode 100644 index 000000000000..880f00714b32 --- /dev/null +++ b/tests/ui/cbmc_checks/float-overflow/check_message_overflow.expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL \ No newline at end of file diff --git a/tests/ui/cbmc_checks/float-overflow/check_message_overflow.rs b/tests/ui/cbmc_checks/float-overflow/check_message_overflow.rs new file mode 100644 index 000000000000..5b57e4b4377d --- /dev/null +++ b/tests/ui/cbmc_checks/float-overflow/check_message_overflow.rs @@ -0,0 +1,22 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// This test verifies that Kani does not report floating-point overflow by default +// for operations that result in +/-Infinity. +extern crate kani; + +// Use the result so rustc doesn't optimize them away. +fn dummy(result: f32) -> f32 { + result +} + +#[kani::proof] +fn main() { + let a = kani::any_where(|&x: &f32| x.is_finite()); + let b = kani::any_where(|&x: &f32| x.is_finite()); + + dummy(a + b); + dummy(a - b); + dummy(a * b); + dummy(-a); +} From 2955d5e61f95d9c762bd4f6f344539cc88e85e5c Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Wed, 5 Feb 2025 18:48:26 -0500 Subject: [PATCH 012/161] Bump Kani version to 0.59.0 (#3876) Github generated release notes: ## What's Changed * Automatic toolchain upgrade to nightly-2025-01-08 by @github-actions in https://github.com/model-checking/kani/pull/3821 * Automatic cargo update to 2025-01-13 by @github-actions in https://github.com/model-checking/kani/pull/3824 * Automatic toolchain upgrade to nightly-2025-01-09 by @github-actions in https://github.com/model-checking/kani/pull/3825 * Bump ncipollo/release-action from 1.14.0 to 1.15.0 by @dependabot in https://github.com/model-checking/kani/pull/3826 * Bump tests/perf/s2n-quic from `ac52a48` to `adc7ba9` by @dependabot in https://github.com/model-checking/kani/pull/3827 * Automatic toolchain upgrade to nightly-2025-01-10 by @github-actions in https://github.com/model-checking/kani/pull/3828 * Automatic toolchain upgrade to nightly-2025-01-11 by @github-actions in https://github.com/model-checking/kani/pull/3830 * Verify contracts/stubs for generic types with multiple inherent implementations by @carolynzech in https://github.com/model-checking/kani/pull/3829 * Update Charon submodule by @thanhnguyen-aws in https://github.com/model-checking/kani/pull/3823 * Automatic toolchain upgrade to nightly-2025-01-12 by @github-actions in https://github.com/model-checking/kani/pull/3831 * Automatic toolchain upgrade to nightly-2025-01-13 by @github-actions in https://github.com/model-checking/kani/pull/3833 * Upgrade toolchain to 2025-01-15 by @tautschnig in https://github.com/model-checking/kani/pull/3835 * Automatic toolchain upgrade to nightly-2025-01-16 by @github-actions in https://github.com/model-checking/kani/pull/3836 * Add a regression test for `no_std` feature by @carolynzech in https://github.com/model-checking/kani/pull/3837 * Use fully-qualified name for size_of by @zhassan-aws in https://github.com/model-checking/kani/pull/3838 * Automatic cargo update to 2025-01-20 by @github-actions in https://github.com/model-checking/kani/pull/3842 * Bump tests/perf/s2n-quic from `adc7ba9` to `f0649f9` by @dependabot in https://github.com/model-checking/kani/pull/3844 * Upgrade toolchain to nightly-2025-01-22 by @tautschnig in https://github.com/model-checking/kani/pull/3843 * Remove `DefKind::Ctor` from filtering crate items by @carolynzech in https://github.com/model-checking/kani/pull/3845 * Enable valid_ptr post_condition harnesses by @tautschnig in https://github.com/model-checking/kani/pull/3847 * Update build command in docs to use release mode by @zhassan-aws in https://github.com/model-checking/kani/pull/3846 * Automatic toolchain upgrade to nightly-2025-01-23 by @github-actions in https://github.com/model-checking/kani/pull/3848 * Automatic toolchain upgrade to nightly-2025-01-24 by @github-actions in https://github.com/model-checking/kani/pull/3850 * Remove the openssl-devel package from dependencies by @zhassan-aws in https://github.com/model-checking/kani/pull/3852 * Fix validity checks for `char` by @celinval in https://github.com/model-checking/kani/pull/3853 * Bump tests/perf/s2n-quic from `f0649f9` to `4500593` by @dependabot in https://github.com/model-checking/kani/pull/3857 * Automatic cargo update to 2025-01-27 by @github-actions in https://github.com/model-checking/kani/pull/3856 * Deprecate `--enable-unstable` and `--restrict-vtable` by @celinval in https://github.com/model-checking/kani/pull/3859 * Stub linker to avoid missing symbols errors by @celinval in https://github.com/model-checking/kani/pull/3858 * Toolchain upgrade to nightly-2025-01-28 by @feliperodri in https://github.com/model-checking/kani/pull/3855 * Allow multiple annotations, but check for duplicate targets. by @remi-delmas-3000 in https://github.com/model-checking/kani/pull/3808 * Move documentation of kani_core modules to right places by @qinheping in https://github.com/model-checking/kani/pull/3851 * Fix missing function declaration issue by @celinval in https://github.com/model-checking/kani/pull/3862 * Fix transmute codegen when sizes are different by @celinval in https://github.com/model-checking/kani/pull/3861 * Remove symtab2gb from bundle by @zhassan-aws in https://github.com/model-checking/kani/pull/3865 * Update the rustc hack for CLion / RustRover by @celinval in https://github.com/model-checking/kani/pull/3868 * Bump tests/perf/s2n-quic from `4500593` to `82dd0b5` by @dependabot in https://github.com/model-checking/kani/pull/3872 * Automatic cargo update to 2025-02-03 by @github-actions in https://github.com/model-checking/kani/pull/3869 * Add reference for loop contracts by @qinheping in https://github.com/model-checking/kani/pull/3849 * remove flag float-overflow-check by @rajath-mk in https://github.com/model-checking/kani/pull/3873 ## New Contributors * @rajath-mk made their first contribution in https://github.com/model-checking/kani/pull/3873 **Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.58.0...kani-0.59.0 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> --- CHANGELOG.md | 15 +++++++++++++++ Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- cprover_bindings/Cargo.toml | 2 +- kani-compiler/Cargo.toml | 2 +- kani-driver/Cargo.toml | 2 +- kani_metadata/Cargo.toml | 2 +- library/kani/Cargo.toml | 2 +- library/kani_core/Cargo.toml | 2 +- library/kani_macros/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- tools/build-kani/Cargo.toml | 2 +- 12 files changed, 35 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f873f2a42755..0941843ee413 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ This file contains notable changes (e.g. breaking changes, major changes, etc.) This file was introduced starting Kani 0.23.0, so it only contains changes from version 0.23.0 onwards. +## Breaking Changes +* Deprecate `--enable-unstable` and `--restrict-vtable` by @celinval in https://github.com/model-checking/kani/pull/3859 +* Do not report arithmetic overflow for floating point operations that produce +/-Inf by @rajath-mk in https://github.com/model-checking/kani/pull/3873 + +## What's Changed +* Fix validity checks for `char` by @celinval in https://github.com/model-checking/kani/pull/3853 +* Support verifying contracts/stubs for generic types with multiple inherent implementations by @carolynzech in https://github.com/model-checking/kani/pull/3829 +* Allow multiple stub_verified annotations, but check for duplicate targets by @remi-delmas-3000 in https://github.com/model-checking/kani/pull/3808 +* Fix crash if a function pointer is created but never used by @celinval in https://github.com/model-checking/kani/pull/3862 +* Fix transmute codegen when sizes are different by @celinval in https://github.com/model-checking/kani/pull/3861 +* Stub linker to avoid missing symbols errors by @celinval in https://github.com/model-checking/kani/pull/3858 +* Toolchain upgrade to nightly-2025-01-28 by @feliperodri @tautschnig + +**Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.58.0...kani-0.59.0 + ## [0.58.0] ### Major/Breaking Changes diff --git a/Cargo.lock b/Cargo.lock index a009b26b7303..e2ce5c6e576b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -176,7 +176,7 @@ dependencies = [ [[package]] name = "build-kani" -version = "0.58.0" +version = "0.59.0" dependencies = [ "anyhow", "cargo_metadata", @@ -398,7 +398,7 @@ dependencies = [ [[package]] name = "cprover_bindings" -version = "0.58.0" +version = "0.59.0" dependencies = [ "lazy_static", "linear-map", @@ -809,7 +809,7 @@ checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5" [[package]] name = "kani" -version = "0.58.0" +version = "0.59.0" dependencies = [ "kani_core", "kani_macros", @@ -817,7 +817,7 @@ dependencies = [ [[package]] name = "kani-compiler" -version = "0.58.0" +version = "0.59.0" dependencies = [ "charon", "clap", @@ -856,7 +856,7 @@ dependencies = [ [[package]] name = "kani-driver" -version = "0.58.0" +version = "0.59.0" dependencies = [ "anyhow", "cargo_metadata", @@ -888,7 +888,7 @@ dependencies = [ [[package]] name = "kani-verifier" -version = "0.58.0" +version = "0.59.0" dependencies = [ "anyhow", "home", @@ -897,14 +897,14 @@ dependencies = [ [[package]] name = "kani_core" -version = "0.58.0" +version = "0.59.0" dependencies = [ "kani_macros", ] [[package]] name = "kani_macros" -version = "0.58.0" +version = "0.59.0" dependencies = [ "proc-macro-error2", "proc-macro2", @@ -914,7 +914,7 @@ dependencies = [ [[package]] name = "kani_metadata" -version = "0.58.0" +version = "0.59.0" dependencies = [ "clap", "cprover_bindings", @@ -1633,7 +1633,7 @@ dependencies = [ [[package]] name = "std" -version = "0.58.0" +version = "0.59.0" dependencies = [ "kani", ] diff --git a/Cargo.toml b/Cargo.toml index a83963ad986c..4948246d5110 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-verifier" -version = "0.58.0" +version = "0.59.0" edition = "2021" description = "A bit-precise model checker for Rust." readme = "README.md" diff --git a/cprover_bindings/Cargo.toml b/cprover_bindings/Cargo.toml index a96660df3d72..b35626e3d5f4 100644 --- a/cprover_bindings/Cargo.toml +++ b/cprover_bindings/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "cprover_bindings" -version = "0.58.0" +version = "0.59.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index a007742e5faa..e2636f5fcd64 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-compiler" -version = "0.58.0" +version = "0.59.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index a2378c407fcc..ec39fbf67dec 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-driver" -version = "0.58.0" +version = "0.59.0" edition = "2021" description = "Build a project with Kani and run all proof harnesses" license = "MIT OR Apache-2.0" diff --git a/kani_metadata/Cargo.toml b/kani_metadata/Cargo.toml index 35239b0242e6..b4db86ebed1e 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_metadata" -version = "0.58.0" +version = "0.59.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index ca039ebdaa39..d559be65a88f 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.58.0" +version = "0.59.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani_core/Cargo.toml b/library/kani_core/Cargo.toml index 89d57047a77c..b474e15d7d0c 100644 --- a/library/kani_core/Cargo.toml +++ b/library/kani_core/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_core" -version = "0.58.0" +version = "0.59.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani_macros/Cargo.toml b/library/kani_macros/Cargo.toml index 5d8ef49541cc..85e3ee2ed48c 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_macros" -version = "0.58.0" +version = "0.59.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index ec29489d9aec..223310d52726 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -5,7 +5,7 @@ # Note: this package is intentionally named std to make sure the names of # standard library symbols are preserved name = "std" -version = "0.58.0" +version = "0.59.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/tools/build-kani/Cargo.toml b/tools/build-kani/Cargo.toml index fb862e3d8507..8d837a9b0e3f 100644 --- a/tools/build-kani/Cargo.toml +++ b/tools/build-kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "build-kani" -version = "0.58.0" +version = "0.59.0" edition = "2021" description = "Builds Kani, Sysroot and release bundle." license = "MIT OR Apache-2.0" From 94ed3f71792b0bbf439654eebbcda1a5a7a89765 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Thu, 6 Feb 2025 14:11:09 -0500 Subject: [PATCH 013/161] Add missing version number to changelog (#3877) By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0941843ee413..7ae95fe7f52c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,13 @@ This file contains notable changes (e.g. breaking changes, major changes, etc.) This file was introduced starting Kani 0.23.0, so it only contains changes from version 0.23.0 onwards. -## Breaking Changes +## [0.59.0] + +### Breaking Changes * Deprecate `--enable-unstable` and `--restrict-vtable` by @celinval in https://github.com/model-checking/kani/pull/3859 * Do not report arithmetic overflow for floating point operations that produce +/-Inf by @rajath-mk in https://github.com/model-checking/kani/pull/3873 -## What's Changed +### What's Changed * Fix validity checks for `char` by @celinval in https://github.com/model-checking/kani/pull/3853 * Support verifying contracts/stubs for generic types with multiple inherent implementations by @carolynzech in https://github.com/model-checking/kani/pull/3829 * Allow multiple stub_verified annotations, but check for duplicate targets by @remi-delmas-3000 in https://github.com/model-checking/kani/pull/3808 From a66bb06a50e9b4cfa67a4db44a2882313570bf9f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 07:30:08 +0000 Subject: [PATCH 014/161] Automatic cargo update to 2025-02-10 (#3880) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e2ce5c6e576b..3b776124d1bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -192,9 +192,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" [[package]] name = "camino" @@ -230,9 +230,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.11" +version = "1.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf" +checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" dependencies = [ "shlex", ] @@ -282,9 +282,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.27" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" +checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" dependencies = [ "clap_builder", "clap_derive", @@ -304,9 +304,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.24" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" dependencies = [ "heck", "proc-macro2", @@ -347,13 +347,12 @@ dependencies = [ [[package]] name = "comfy-table" -version = "7.1.3" +version = "7.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f165e7b643266ea80cb858aed492ad9280e3e05ce24d4a99d7d7b889b6a4d9" +checksum = "4a65ebfec4fb190b6f90e944a817d60499ee0744e582530e2c9900a22e591d9a" dependencies = [ "crossterm", - "strum", - "strum_macros", + "unicode-segmentation", "unicode-width 0.2.0", ] @@ -1164,15 +1163,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "os_info" -version = "3.9.2" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e6520c8cc998c5741ee68ec1dc369fc47e5f0ea5320018ecf2a1ccd6328f48b" +checksum = "2a604e53c24761286860eba4e2c8b23a0161526476b1de520139d69cdb85a6b5" dependencies = [ "log", "windows-sys 0.52.0", @@ -1825,9 +1824,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" dependencies = [ "serde", "serde_spanned", @@ -2035,9 +2034,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wait-timeout" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" dependencies = [ "libc", ] @@ -2069,9 +2068,9 @@ dependencies = [ [[package]] name = "which" -version = "7.0.1" +version = "7.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a9e33648339dc1642b0e36e21b3385e6148e289226f657c809dee59df5028" +checksum = "2774c861e1f072b3aadc02f8ba886c26ad6321567ecc294c935434cad06f1283" dependencies = [ "either", "env_home", @@ -2194,9 +2193,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e49d2d35d3fad69b39b94139037ecfb4f359f08958b9c11e7315ce770462419" +checksum = "86e376c75f4f43f44db463cf729e0d3acbf954d13e22c51e26e4c264b4ab545f" dependencies = [ "memchr", ] From 759e2c5c93fc59c3ad9ca9b8163ee4c4745c9c17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 14:08:54 -0500 Subject: [PATCH 015/161] Bump tests/perf/s2n-quic from `82dd0b5` to `a5d8422` (#3882) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `82dd0b5` to `a5d8422`.
Commits
  • a5d8422 feat(s2n-quic-dc): Replace requested_handshakes map with immediate request (#...
  • 21464fc build(deps): update lru requirement from 0.12 to 0.13 (#2467)
  • b871ad0 chore: release 1.53.0 (#2465)
  • e8df067 fix(s2n-quic-dc): Replace shared map with larger bitset (#2464)
  • See full diff in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 82dd0b595e7e..a5d84229dae9 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 82dd0b595e7e9e58531ff26b908fd0f6bbc36e45 +Subproject commit a5d84229dae984a40ece44add3f418b00342fa01 From 4f78926bf46891a5bcd6ed94a6ce1550640a9dc9 Mon Sep 17 00:00:00 2001 From: rajath-mk Date: Mon, 10 Feb 2025 17:10:37 -0800 Subject: [PATCH 016/161] Fast fail feature - Stops verification process as soon as one failure is observed - Use case : CI speed up (#3879) Resolves #3458 Added the option `--fail-fast`, which would stop the verification process whenever any of the harnesses fails, and returns the failed harness as an error. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> --- kani-driver/src/args/mod.rs | 4 ++ kani-driver/src/harness_runner.rs | 46 +++++++++++++-- .../fail_fast_test.expected | 1 + .../stop_at_single_fail/fail_fast_test.rs | 26 +++++++++ .../fail_fast_test_parallel.expected | 1 + .../fail_fast_test_parallel.rs | 57 +++++++++++++++++++ 6 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test.expected create mode 100644 tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test.rs create mode 100644 tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test_parallel.expected create mode 100644 tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test_parallel.rs diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index 9ba06826d200..b8b1ba60a50a 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -334,6 +334,10 @@ pub struct VerificationArgs { #[arg(long)] pub harness_timeout: Option, + /// Stop the verification process as soon as one of the harnesses fails. + #[arg(long)] + pub fail_fast: bool, + /// Arguments to pass down to Cargo #[command(flatten)] pub cargo: CargoCommonArgs, diff --git a/kani-driver/src/harness_runner.rs b/kani-driver/src/harness_runner.rs index b78e1dc9d80b..96b2e732f5ba 100644 --- a/kani-driver/src/harness_runner.rs +++ b/kani-driver/src/harness_runner.rs @@ -1,7 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -use anyhow::{Result, bail}; +use anyhow::{Error, Result, bail}; use kani_metadata::{ArtifactType, HarnessMetadata}; use rayon::prelude::*; use std::fs::File; @@ -34,6 +34,20 @@ pub(crate) struct HarnessResult<'pr> { pub result: VerificationResult, } +#[derive(Debug)] +struct FailFastHarnessInfo { + pub index_to_failing_harness: usize, + pub result: VerificationResult, +} + +impl std::error::Error for FailFastHarnessInfo {} + +impl std::fmt::Display for FailFastHarnessInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "harness failed") + } +} + impl<'pr> HarnessRunner<'_, 'pr> { /// Given a [`HarnessRunner`] (to abstract over how these harnesses were generated), this runs /// the proof-checking process for each harness in `harnesses`. @@ -55,7 +69,8 @@ impl<'pr> HarnessRunner<'_, 'pr> { let results = pool.install(|| -> Result>> { sorted_harnesses .par_iter() - .map(|harness| -> Result> { + .enumerate() + .map(|(idx, harness)| -> Result> { let goto_file = self.project.get_harness_artifact(&harness, ArtifactType::Goto).unwrap(); @@ -66,12 +81,31 @@ impl<'pr> HarnessRunner<'_, 'pr> { } let result = self.sess.check_harness(goto_file, harness)?; - Ok(HarnessResult { harness, result }) + if self.sess.args.fail_fast && result.status == VerificationStatus::Failure { + Err(Error::new(FailFastHarnessInfo { + index_to_failing_harness: idx, + result, + })) + } else { + Ok(HarnessResult { harness, result }) + } }) .collect::>>() - })?; - - Ok(results) + }); + match results { + Ok(results) => Ok(results), + Err(err) => { + if err.is::() { + let failed = err.downcast::().unwrap(); + Ok(vec![HarnessResult { + harness: sorted_harnesses[failed.index_to_failing_harness], + result: failed.result, + }]) + } else { + Err(err) + } + } + } } /// Return an error if the user is trying to verify a harness with stubs without enabling the diff --git a/tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test.expected b/tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test.expected new file mode 100644 index 000000000000..4bb591a9a72d --- /dev/null +++ b/tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test.expected @@ -0,0 +1 @@ +Complete - 0 successfully verified harnesses, 1 failures, 1 total. \ No newline at end of file diff --git a/tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test.rs b/tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test.rs new file mode 100644 index 000000000000..9ac9a22204b5 --- /dev/null +++ b/tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test.rs @@ -0,0 +1,26 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: --fail-fast +//! Ensure that the verification process stops as soon as one of the harnesses fails. + +mod tests { + #[kani::proof] + fn test_01_fail() { + assert!(false, "First failure"); + } + + #[kani::proof] + fn test_02_fail() { + assert!(false, "Second failure"); + } + + #[kani::proof] + fn test_03_fail() { + assert!(false, "Third failure"); + } + + #[kani::proof] + fn test_04_fail() { + assert!(false, "Fourth failure"); + } +} diff --git a/tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test_parallel.expected b/tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test_parallel.expected new file mode 100644 index 000000000000..4bb591a9a72d --- /dev/null +++ b/tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test_parallel.expected @@ -0,0 +1 @@ +Complete - 0 successfully verified harnesses, 1 failures, 1 total. \ No newline at end of file diff --git a/tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test_parallel.rs b/tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test_parallel.rs new file mode 100644 index 000000000000..db2535777a26 --- /dev/null +++ b/tests/ui/multiple-harnesses/stop_at_single_fail/fail_fast_test_parallel.rs @@ -0,0 +1,57 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: --fail-fast -Z unstable-options --jobs 4 --output-format=terse +//! Ensure that the verification process stops as soon as one of the harnesses fails. +//! This test runs on 4 parallel threads. Stops verification as soon as a harness on any of the threads fails. + +mod tests { + #[kani::proof] + fn test_01_fail() { + assert!(false, "First failure"); + } + + #[kani::proof] + fn test_02_fail() { + assert!(false, "Second failure"); + } + + #[kani::proof] + fn test_03_fail() { + assert!(false, "Third failure"); + } + + #[kani::proof] + fn test_04_fail() { + assert!(false, "Fourth failure"); + } + + #[kani::proof] + fn test_05_fail() { + assert!(false, "Fifth failure"); + } + + #[kani::proof] + fn test_06_fail() { + assert!(false, "Sixth failure"); + } + + #[kani::proof] + fn test_07_fail() { + assert!(false, "Seventh failure"); + } + + #[kani::proof] + fn test_08_fail() { + assert!(false, "Eighth failure"); + } + + #[kani::proof] + fn test_09_fail() { + assert!(false, "Ninth failure"); + } + + #[kani::proof] + fn test_10_fail() { + assert!(false, "Tenth failure"); + } +} From 8b2ec7737b8f5153841b216b553cb69250809124 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Tue, 11 Feb 2025 10:56:22 -0500 Subject: [PATCH 017/161] Autoharness Subcommand (#3874) This PR introduces an initial implementation for the `autoharness` subcommand and a book chapter describing the feature. I recommend reading the book chapter before reviewing the code (or proceeding further in this PR description). ## Callouts `--harness` is to manual harnesses what `--include-function` and `--exclude-function` are to autoharness; both allow the user to filter which harnesses/functions get verified. Their implementation is different, however--`--harness` is a driver-only flag, i.e., we still compile every harness, but then only call CBMC on the ones specified in `--harness`. `--include-function` and `--exclude-function` get passed to the compiler. I made this design choice to try to be more aggressive about improving compiler performance--if a user only asks to verify one function and we go try to codegen a thousand, the compiler will take much longer than it needs to. I thought this more aggressive optimization was warranted given that crates are likely to have far many more functions eligible for autoverification than manual Kani harnesses. (See also the limitations in the book chapter). ## Testing Besides the new tests in this PR, I also ran against [s2n-quic](https://github.com/aws/s2n-quic) to confirm that the subcommand works on larger crates. Towards #3832 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- docs/src/SUMMARY.md | 1 + .../src/reference/experimental/autoharness.md | 84 +++++++ kani-compiler/src/args.rs | 9 + .../codegen_aeneas_llbc/compiler_interface.rs | 2 +- .../compiler_interface.rs | 6 +- .../src/kani_middle/codegen_units.rs | 185 +++++++++++++-- .../src/kani_middle/kani_functions.rs | 2 + kani-compiler/src/kani_middle/metadata.rs | 44 ++++ .../src/kani_middle/transform/automatic.rs | 129 +++++++++++ .../src/kani_middle/transform/contracts.rs | 36 ++- .../kani_middle/transform/kani_intrinsics.rs | 6 +- .../src/kani_middle/transform/mod.rs | 4 + kani-driver/src/args/autoharness_args.rs | 136 ++++++++++++ kani-driver/src/args/mod.rs | 8 + kani-driver/src/assess/mod.rs | 2 +- kani-driver/src/autoharness/mod.rs | 73 ++++++ kani-driver/src/call_cargo.rs | 11 +- kani-driver/src/harness_runner.rs | 114 ++++++---- kani-driver/src/list/collect_metadata.rs | 4 +- kani-driver/src/main.rs | 47 +++- kani-driver/src/metadata.rs | 5 + kani-driver/src/project.rs | 2 +- kani-driver/src/session.rs | 20 +- kani_metadata/src/harness.rs | 2 + library/kani_core/src/lib.rs | 6 + .../cargo_autoharness_contracts/Cargo.toml | 10 + .../cargo_autoharness_contracts/config.yml | 4 + .../contracts.expected | 28 +++ .../cargo_autoharness_contracts/contracts.sh | 9 + .../cargo_autoharness_contracts/src/lib.rs | 56 +++++ .../cargo_autoharness_exclude/Cargo.toml | 10 + .../cargo_autoharness_exclude/config.yml | 4 + .../exclude.expected | 4 + .../cargo_autoharness_exclude/exclude.sh | 5 + .../cargo_autoharness_exclude/src/lib.rs | 23 ++ .../cargo_autoharness_filter/Cargo.toml | 10 + .../cargo_autoharness_filter/config.yml | 4 + .../cargo_autoharness_filter/filter.expected | 43 ++++ .../cargo_autoharness_filter/filter.sh | 5 + .../cargo_autoharness_filter/src/lib.rs | 210 ++++++++++++++++++ .../Cargo.toml | 10 + .../config.yml | 4 + .../harnesses_fail.expected | 57 +++++ .../harnesses_fail.sh | 9 + .../src/lib.rs | 68 ++++++ .../cargo_autoharness_include/Cargo.toml | 10 + .../cargo_autoharness_include/config.yml | 4 + .../include.expected | 4 + .../cargo_autoharness_include/include.sh | 5 + .../cargo_autoharness_include/src/lib.rs | 24 ++ .../cargo_autoharness_loops_fixme/Cargo.toml | 10 + .../cargo_autoharness_loops_fixme/config.yml | 4 + .../loops.expected | 1 + .../cargo_autoharness_loops_fixme/loops.sh | 5 + .../cargo_autoharness_loops_fixme/src/lib.rs | 39 ++++ 55 files changed, 1516 insertions(+), 101 deletions(-) create mode 100644 docs/src/reference/experimental/autoharness.md create mode 100644 kani-compiler/src/kani_middle/transform/automatic.rs create mode 100644 kani-driver/src/args/autoharness_args.rs create mode 100644 kani-driver/src/autoharness/mod.rs create mode 100644 tests/script-based-pre/cargo_autoharness_contracts/Cargo.toml create mode 100644 tests/script-based-pre/cargo_autoharness_contracts/config.yml create mode 100644 tests/script-based-pre/cargo_autoharness_contracts/contracts.expected create mode 100755 tests/script-based-pre/cargo_autoharness_contracts/contracts.sh create mode 100644 tests/script-based-pre/cargo_autoharness_contracts/src/lib.rs create mode 100644 tests/script-based-pre/cargo_autoharness_exclude/Cargo.toml create mode 100644 tests/script-based-pre/cargo_autoharness_exclude/config.yml create mode 100644 tests/script-based-pre/cargo_autoharness_exclude/exclude.expected create mode 100755 tests/script-based-pre/cargo_autoharness_exclude/exclude.sh create mode 100644 tests/script-based-pre/cargo_autoharness_exclude/src/lib.rs create mode 100644 tests/script-based-pre/cargo_autoharness_filter/Cargo.toml create mode 100644 tests/script-based-pre/cargo_autoharness_filter/config.yml create mode 100644 tests/script-based-pre/cargo_autoharness_filter/filter.expected create mode 100755 tests/script-based-pre/cargo_autoharness_filter/filter.sh create mode 100644 tests/script-based-pre/cargo_autoharness_filter/src/lib.rs create mode 100644 tests/script-based-pre/cargo_autoharness_harnesses_fail/Cargo.toml create mode 100644 tests/script-based-pre/cargo_autoharness_harnesses_fail/config.yml create mode 100644 tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected create mode 100755 tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.sh create mode 100644 tests/script-based-pre/cargo_autoharness_harnesses_fail/src/lib.rs create mode 100644 tests/script-based-pre/cargo_autoharness_include/Cargo.toml create mode 100644 tests/script-based-pre/cargo_autoharness_include/config.yml create mode 100644 tests/script-based-pre/cargo_autoharness_include/include.expected create mode 100755 tests/script-based-pre/cargo_autoharness_include/include.sh create mode 100644 tests/script-based-pre/cargo_autoharness_include/src/lib.rs create mode 100644 tests/script-based-pre/cargo_autoharness_loops_fixme/Cargo.toml create mode 100644 tests/script-based-pre/cargo_autoharness_loops_fixme/config.yml create mode 100644 tests/script-based-pre/cargo_autoharness_loops_fixme/loops.expected create mode 100755 tests/script-based-pre/cargo_autoharness_loops_fixme/loops.sh create mode 100644 tests/script-based-pre/cargo_autoharness_loops_fixme/src/lib.rs diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index d534cf1f7f22..11d5318a0e2c 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -18,6 +18,7 @@ - [Reference](./reference.md) - [Attributes](./reference/attributes.md) - [Experimental features](./reference/experimental/experimental-features.md) + - [Automatic Harness Generation](./reference/experimental/autoharness.md) - [Coverage](./reference/experimental/coverage.md) - [Stubbing](./reference/experimental/stubbing.md) - [Contracts](./reference/experimental/contracts.md) diff --git a/docs/src/reference/experimental/autoharness.md b/docs/src/reference/experimental/autoharness.md new file mode 100644 index 000000000000..022d6471ea0e --- /dev/null +++ b/docs/src/reference/experimental/autoharness.md @@ -0,0 +1,84 @@ +# Automatic Harness Generation + +Recall the harness for `estimate_size` that we wrote in [First Steps](../../tutorial-first-steps.md): +```rust +{{#include ../../tutorial/first-steps-v1/src/lib.rs:kani}} +``` + +This harness first declares a local variable `x` using `kani::any()`, then calls `estimate_size` with argument `x`. +Many proof harnesses follow this predictable format—to verify a function `foo`, we create arbitrary values for each of `foo`'s arguments, then call `foo` on those arguments. + +The `autoharness` subcommand leverages this observation to automatically generate and run harnesses. +Kani scans the crate for functions whose arguments all implement the `kani::Arbitrary` trait, generates harnesses for them, then runs them. +These harnesses are internal to Kani--i.e., Kani does not make any changes to your source code. + +## Usage +Run either: +``` +# cargo kani autoharness -Z unstable-options +``` +or +``` +# kani autoharness -Z unstable-options +``` + +If Kani detects that all of a function `foo`'s arguments implement `kani::Arbitrary`, it will generate and run a `#[kani::proof]` harness, which prints: + +``` +Autoharness: Checking function foo against all possible inputs... + +``` + +However, if Kani detects that `foo` has a [contract](./contracts.md), it will instead generate a `#[kani::proof_for_contract]` harness and verify the contract: +``` +Autoharness: Checking function foo's contract against all possible inputs... + +``` + +Kani generates and runs these harnesses internally—the user only sees the verification results. + +The `autoharness` subcommand has options `--include-function` and `--exclude-function` to include and exclude particular functions. +These flags look for partial matches against the fully qualified name of a function. + +For example, if a module `my_module` has many functions, but we are only interested in `my_module::foo` and `my_module::bar`, we can run: +``` +cargo run autoharness -Z unstable-options --include-function foo include-function bar +``` +To exclude `my_module` entirely, run: +``` +cargo run autoharness -Z unstable-options --exclude-function my_module +``` + +## Example +Using the `estimate_size` example from [First Steps](../../tutorial-first-steps.md) again: +```rust +{{#include ../../tutorial/first-steps-v1/src/lib.rs:code}} +``` + +We get: + +``` +# cargo kani autoharness -Z unstable-options +Autoharness: Checking function estimate_size against all possible inputs... +RESULTS: +Check 3: estimate_size.assertion.1 + - Status: FAILURE + - Description: "Oh no, a failing corner case!" +[...] + +Verification failed for - estimate_size +Complete - 0 successfully verified functions, 1 failures, 1 total. +``` + +## Request for comments +This feature is experimental and is therefore subject to change. +If you have ideas for improving the user experience of this feature, +please add them to [this GitHub issue](https://github.com/model-checking/kani/issues/3832). + +## Limitations +Kani will only generate an automatic harness for a function if it can determine that all of the function's arguments implement Arbitrary. +It does not attempt to derive/implement Arbitrary for any types, even if those types could implement Arbitrary. + +If a function contains a loop with a loop contract, Kani will detect the presence of a loop contract and verify that contract. +If, however, the loop does not have a contract, then there is currently no way to specify an unwinding bound for the function, meaning that Kani may hang as it tries to unwind the loop. +We recommend using the `--exclude-function` option to exclude any functions that have this issue (or `--harness-timeout` to bail after attempting verification for some amount of time). \ No newline at end of file diff --git a/kani-compiler/src/args.rs b/kani-compiler/src/args.rs index 99254a08423a..e524b6e5ba2a 100644 --- a/kani-compiler/src/args.rs +++ b/kani-compiler/src/args.rs @@ -30,6 +30,9 @@ pub enum ReachabilityType { PubFns, /// Start the cross-crate reachability analysis from all *test* (i.e. `#[test]`) harnesses in the local crate. Tests, + /// Start the cross-crate reachability analysis from all functions in the local crate. + /// Currently, this mode is only used for automatic harness generation. + AllFns, } /// Command line arguments that this instance of the compiler run was called @@ -88,6 +91,12 @@ pub struct Arguments { /// Print the final LLBC file to stdout. #[clap(long)] pub print_llbc: bool, + /// If we are running the autoharness subcommand, the functions to autoharness + #[arg(long = "autoharness-include-function", num_args(1))] + pub autoharness_included_functions: Vec, + /// If we are running the autoharness subcommand, the functions to exclude from autoverification + #[arg(long = "autoharness-exclude-function", num_args(1))] + pub autoharness_excluded_functions: Vec, } #[derive(Debug, Clone, Copy, AsRefStr, EnumString, VariantNames, PartialEq, Eq)] diff --git a/kani-compiler/src/codegen_aeneas_llbc/compiler_interface.rs b/kani-compiler/src/codegen_aeneas_llbc/compiler_interface.rs index e87bed0ea388..f577d2886e6c 100644 --- a/kani-compiler/src/codegen_aeneas_llbc/compiler_interface.rs +++ b/kani-compiler/src/codegen_aeneas_llbc/compiler_interface.rs @@ -253,7 +253,7 @@ impl CodegenBackend for LlbcCodegenBackend { units.store_modifies(&modifies_instances); units.write_metadata(&queries, tcx); } - ReachabilityType::Tests => todo!(), + ReachabilityType::Tests | ReachabilityType::AllFns => todo!(), ReachabilityType::None => {} ReachabilityType::PubFns => { let unit = CodegenUnit::default(); diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index a0ae5ae99eff..80137db06a6e 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -264,7 +264,7 @@ impl CodegenBackend for GotocCodegenBackend { let reachability = queries.args().reachability_analysis; let mut results = GotoCodegenResults::new(tcx, reachability); match reachability { - ReachabilityType::Harnesses => { + ReachabilityType::AllFns | ReachabilityType::Harnesses => { let mut units = CodegenUnits::new(&queries, tcx); let mut modifies_instances = vec![]; let mut loop_contracts_instances = vec![]; @@ -375,7 +375,9 @@ impl CodegenBackend for GotocCodegenBackend { // Print compilation report. results.print_report(tcx); - if reachability != ReachabilityType::Harnesses { + if reachability != ReachabilityType::Harnesses + && reachability != ReachabilityType::AllFns + { // In a workspace, cargo seems to be using the same file prefix to build a crate that is // a package lib and also a dependency of another package. // To avoid overriding the metadata for its verification, we skip this step when diff --git a/kani-compiler/src/kani_middle/codegen_units.rs b/kani-compiler/src/kani_middle/codegen_units.rs index 072528f9a765..1fc659ea2f80 100644 --- a/kani-compiler/src/kani_middle/codegen_units.rs +++ b/kani-compiler/src/kani_middle/codegen_units.rs @@ -8,8 +8,11 @@ //! according to their stub configuration. use crate::args::ReachabilityType; -use crate::kani_middle::attributes::is_proof_harness; -use crate::kani_middle::metadata::{gen_contracts_metadata, gen_proof_metadata}; +use crate::kani_middle::attributes::{KaniAttributes, is_proof_harness}; +use crate::kani_middle::kani_functions::{KaniIntrinsic, KaniModel}; +use crate::kani_middle::metadata::{ + gen_automatic_proof_metadata, gen_contracts_metadata, gen_proof_metadata, +}; use crate::kani_middle::reachability::filter_crate_items; use crate::kani_middle::resolve::expect_resolve_fn; use crate::kani_middle::stubbing::{check_compatibility, harness_stub_map}; @@ -20,8 +23,8 @@ use rustc_middle::ty::TyCtxt; use rustc_session::config::OutputType; use rustc_smir::rustc_internal; use stable_mir::CrateDef; -use stable_mir::mir::mono::Instance; -use stable_mir::ty::{FnDef, IndexedVal, RigidTy, TyKind}; +use stable_mir::mir::{TerminatorKind, mono::Instance}; +use stable_mir::ty::{FnDef, GenericArgKind, GenericArgs, IndexedVal, RigidTy, TyKind}; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::fs::File; use std::io::BufWriter; @@ -57,26 +60,66 @@ pub struct CodegenUnit { impl CodegenUnits { pub fn new(queries: &QueryDb, tcx: TyCtxt) -> Self { let crate_info = CrateInfo { name: stable_mir::local_crate().name.as_str().into() }; - if queries.args().reachability_analysis == ReachabilityType::Harnesses { - let base_filepath = tcx.output_filenames(()).path(OutputType::Object); - let base_filename = base_filepath.as_path(); - let harnesses = filter_crate_items(tcx, |_, instance| is_proof_harness(tcx, instance)); - let all_harnesses = harnesses - .into_iter() - .map(|harness| { - let metadata = gen_proof_metadata(tcx, harness, &base_filename); - (harness, metadata) - }) - .collect::>(); + let base_filepath = tcx.output_filenames(()).path(OutputType::Object); + let base_filename = base_filepath.as_path(); + let args = queries.args(); + match args.reachability_analysis { + ReachabilityType::Harnesses => { + let all_harnesses = get_all_manual_harnesses(tcx, base_filename); + // Even if no_stubs is empty we still need to store rustc metadata. + let units = group_by_stubs(tcx, &all_harnesses); + validate_units(tcx, &units); + debug!(?units, "CodegenUnits::new"); + CodegenUnits { units, harness_info: all_harnesses, crate_info } + } + ReachabilityType::AllFns => { + let mut all_harnesses = get_all_manual_harnesses(tcx, base_filename); + let mut units = group_by_stubs(tcx, &all_harnesses); + validate_units(tcx, &units); - // Even if no_stubs is empty we still need to store rustc metadata. - let units = group_by_stubs(tcx, &all_harnesses); - validate_units(tcx, &units); - debug!(?units, "CodegenUnits::new"); - CodegenUnits { units, harness_info: all_harnesses, crate_info } - } else { - // Leave other reachability type handling as is for now. - CodegenUnits { units: vec![], harness_info: HashMap::default(), crate_info } + let kani_fns = queries.kani_functions(); + let kani_harness_intrinsic = + kani_fns.get(&KaniIntrinsic::AutomaticHarness.into()).unwrap(); + let kani_any_inst = kani_fns.get(&KaniModel::Any.into()).unwrap(); + + let verifiable_fns = filter_crate_items(tcx, |_, instance: Instance| -> bool { + // If the user specified functions to include or exclude, only allow instances matching those filters. + let user_included = if !args.autoharness_included_functions.is_empty() { + fn_list_contains_instance(&instance, &args.autoharness_included_functions) + } else if !args.autoharness_excluded_functions.is_empty() { + !fn_list_contains_instance(&instance, &args.autoharness_excluded_functions) + } else { + true + }; + user_included + && is_eligible_for_automatic_harness(tcx, instance, *kani_any_inst) + }); + let automatic_harnesses = get_all_automatic_harnesses( + tcx, + verifiable_fns, + *kani_harness_intrinsic, + base_filename, + ); + // We generate one contract harness per function under contract, so each harness is in its own unit, + // and these harnesses have no stubs. + units.extend( + automatic_harnesses + .keys() + .map(|harness| CodegenUnit { + harnesses: vec![*harness], + stubs: HashMap::default(), + }) + .collect::>(), + ); + all_harnesses.extend(automatic_harnesses); + // No need to validate the units again because validation only checks stubs, and we haven't added any stubs. + debug!(?units, "CodegenUnits::new"); + CodegenUnits { units, harness_info: all_harnesses, crate_info } + } + _ => { + // Leave other reachability type handling as is for now. + CodegenUnits { units: vec![], harness_info: HashMap::default(), crate_info } + } } } @@ -94,7 +137,15 @@ impl CodegenUnits { /// We flag that the harness contains usage of loop contracts. pub fn store_loop_contracts(&mut self, harnesses: &[Harness]) { for harness in harnesses { - self.harness_info.get_mut(harness).unwrap().has_loop_contracts = true; + let metadata = self.harness_info.get_mut(harness).unwrap(); + metadata.has_loop_contracts = true; + // If we're generating this harness automatically and we encounter a loop contract, + // ensure that the HarnessKind is updated to be a contract harness + // targeting the function to verify. + if metadata.is_automatically_generated { + metadata.attributes.kind = + HarnessKind::ProofForContract { target_fn: metadata.pretty_name.clone() } + } } } @@ -252,3 +303,89 @@ fn apply_transitivity(tcx: TyCtxt, harness: Harness, stubs: Stubs) -> Stubs { } new_stubs } + +/// Fetch all manual harnesses (i.e., functions provided by the user) and generate their metadata +fn get_all_manual_harnesses( + tcx: TyCtxt, + base_filename: &Path, +) -> HashMap { + let harnesses = filter_crate_items(tcx, |_, instance| is_proof_harness(tcx, instance)); + harnesses + .into_iter() + .map(|harness| { + let metadata = gen_proof_metadata(tcx, harness, &base_filename); + (harness, metadata) + }) + .collect::>() +} + +/// For each function eligible for automatic verification, +/// generate a harness Instance for it, then generate its metadata. +/// Note that the body of each harness instance is still the dummy body of `kani_harness_intrinsic`; +/// the AutomaticHarnessPass will later transform the bodies of these instances to actually verify the function. +fn get_all_automatic_harnesses( + tcx: TyCtxt, + verifiable_fns: Vec, + kani_harness_intrinsic: FnDef, + base_filename: &Path, +) -> HashMap { + verifiable_fns + .into_iter() + .map(|fn_to_verify| { + // Set the generic arguments of the harness to be the function it is verifying + // so that later, in AutomaticHarnessPass, we can retrieve the function to verify + // and generate the harness body accordingly. + let harness = Instance::resolve( + kani_harness_intrinsic, + &GenericArgs(vec![GenericArgKind::Type(fn_to_verify.ty())]), + ) + .unwrap(); + let metadata = gen_automatic_proof_metadata(tcx, &base_filename, &fn_to_verify); + (harness, metadata) + }) + .collect::>() +} + +/// Determine whether `instance` is eligible for automatic verification. +fn is_eligible_for_automatic_harness(tcx: TyCtxt, instance: Instance, any_inst: FnDef) -> bool { + // `instance` is ineligble if it is a harness or has an nonexistent/empty body + if is_proof_harness(tcx, instance) || !instance.has_body() { + return false; + } + let body = instance.body().unwrap(); + + // `instance` is ineligble if it is an associated item of a Kani trait implementation, + // or part of Kani contract instrumentation. + // FIXME -- find a less hardcoded way of checking the former condition (perhaps upstream PR to StableMIR). + if instance.name().contains("kani::Arbitrary") + || instance.name().contains("kani::Invariant") + || KaniAttributes::for_instance(tcx, instance) + .fn_marker() + .is_some_and(|m| m.as_str() == "kani_contract_mode") + { + return false; + } + + // Each non-generic argument of `instance`` must implement Arbitrary. + body.arg_locals().iter().all(|arg| { + let kani_any_body = + Instance::resolve(any_inst, &GenericArgs(vec![GenericArgKind::Type(arg.ty)])) + .unwrap() + .body() + .unwrap(); + if let TerminatorKind::Call { func, .. } = &kani_any_body.blocks[0].terminator.kind { + if let Some((def, args)) = func.ty(body.arg_locals()).unwrap().kind().fn_def() { + return Instance::resolve(def, &args).is_ok(); + } + } + false + }) +} + +/// Return whether the name of `instance` is included in `fn_list`. +/// If `exact = true`, then `instance`'s exact, fully-qualified name must be in `fn_list`; otherwise, `fn_list` +/// can have a substring of `instance`'s name. +fn fn_list_contains_instance(instance: &Instance, fn_list: &[String]) -> bool { + let pretty_name = instance.name(); + fn_list.contains(&pretty_name) || fn_list.iter().any(|fn_name| pretty_name.contains(fn_name)) +} diff --git a/kani-compiler/src/kani_middle/kani_functions.rs b/kani-compiler/src/kani_middle/kani_functions.rs index 8d1ab5ef939d..3182ced8fa61 100644 --- a/kani-compiler/src/kani_middle/kani_functions.rs +++ b/kani-compiler/src/kani_middle/kani_functions.rs @@ -48,6 +48,8 @@ pub enum KaniIntrinsic { CheckedAlignOf, #[strum(serialize = "CheckedSizeOfIntrinsic")] CheckedSizeOf, + #[strum(serialize = "AutomaticHarnessIntrinsic")] + AutomaticHarness, #[strum(serialize = "IsInitializedIntrinsic")] IsInitialized, #[strum(serialize = "ValidValueIntrinsic")] diff --git a/kani-compiler/src/kani_middle/metadata.rs b/kani-compiler/src/kani_middle/metadata.rs index 67d32b0079c4..449548f27045 100644 --- a/kani-compiler/src/kani_middle/metadata.rs +++ b/kani-compiler/src/kani_middle/metadata.rs @@ -39,6 +39,7 @@ pub fn gen_proof_metadata(tcx: TyCtxt, instance: Instance, base_name: &Path) -> goto_file: Some(model_file), contract: Default::default(), has_loop_contracts: false, + is_automatically_generated: false, } } @@ -84,6 +85,48 @@ pub fn gen_contracts_metadata(tcx: TyCtxt) -> Vec { fn_to_data.into_values().collect() } +/// Generate metadata for automatically generated harnesses. +/// For now, we just use the data from the function we are verifying; since we only generate one automatic harness per function, +/// the metdata from that function uniquely identifies the harness. +/// In future iterations of this feature, we will likely have multiple harnesses for a single function (e.g., for generic functions), +/// in which case HarnessMetadata will need to change further to differentiate between those harnesses. +pub fn gen_automatic_proof_metadata( + tcx: TyCtxt, + base_name: &Path, + fn_to_verify: &Instance, +) -> HarnessMetadata { + let def = fn_to_verify.def; + let pretty_name = fn_to_verify.name(); + let mangled_name = fn_to_verify.mangled_name(); + + // Leave the concrete playback instrumentation for now, but this feature does not actually support concrete playback. + let loc = SourceLocation::new(fn_to_verify.body().unwrap().span); + let file_stem = format!("{}_{mangled_name}", base_name.file_stem().unwrap().to_str().unwrap()); + let model_file = base_name.with_file_name(file_stem).with_extension(ArtifactType::SymTabGoto); + + let kani_attributes = KaniAttributes::for_instance(tcx, *fn_to_verify); + let harness_kind = if kani_attributes.has_contract() { + HarnessKind::ProofForContract { target_fn: pretty_name.clone() } + } else { + HarnessKind::Proof + }; + + HarnessMetadata { + pretty_name, + mangled_name, + crate_name: def.krate().name, + original_file: loc.filename, + original_start_line: loc.start_line, + original_end_line: loc.end_line, + attributes: HarnessAttributes::new(harness_kind), + // TODO: This no longer needs to be an Option. + goto_file: Some(model_file), + contract: Default::default(), + has_loop_contracts: false, + is_automatically_generated: true, + } +} + /// Create the harness metadata for a test description. #[allow(dead_code)] pub fn gen_test_metadata( @@ -110,5 +153,6 @@ pub fn gen_test_metadata( goto_file: Some(model_file), contract: Default::default(), has_loop_contracts: false, + is_automatically_generated: false, } } diff --git a/kani-compiler/src/kani_middle/transform/automatic.rs b/kani-compiler/src/kani_middle/transform/automatic.rs new file mode 100644 index 000000000000..705a8ac26a88 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/automatic.rs @@ -0,0 +1,129 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This module transforms the body of an automatic harness to verify a function. +//! Upon entry to this module, a harness has the dummy body of the automatic_harness Kani intrinsic. +//! We obtain the function its meant to verify by inspecting its generic arguments, +//! then transform its body to be a harness for that function. + +use crate::args::ReachabilityType; +use crate::kani_middle::codegen_units::CodegenUnit; +use crate::kani_middle::kani_functions::{KaniIntrinsic, KaniModel}; +use crate::kani_middle::transform::body::{InsertPosition, MutableBody, SourceInstruction}; +use crate::kani_middle::transform::{TransformPass, TransformationType}; +use crate::kani_queries::QueryDb; +use rustc_middle::ty::TyCtxt; +use stable_mir::mir::mono::Instance; +use stable_mir::mir::{Body, Operand, Place, TerminatorKind}; +use stable_mir::ty::{FnDef, GenericArgKind, GenericArgs}; +use tracing::debug; + +#[derive(Debug)] +pub struct AutomaticHarnessPass { + /// The FnDef of KaniModel::Any + kani_any: FnDef, + /// All of the automatic harness Instances that we generated in the CodegenUnits constructor + automatic_harnesses: Vec, +} + +impl AutomaticHarnessPass { + // FIXME: this is a bit clunky. + // Historically, in codegen_crate, we reset the BodyTransformation cache on a per-unit basis, + // so the BodyTransformation constructor only accepts a CodegenUnit and thus this constructor can only accept a unit. + // Later, we changed codegen to reset the cache on a per-harness basis (for uninitialized memory instrumentation). + // So BodyTransformation should really be changed to reflect that, so that this constructor can just save the one automatic harness it should transform + // and not all of the possibilities. + pub fn new(unit: &CodegenUnit, query_db: &QueryDb) -> Self { + let kani_fns = query_db.kani_functions(); + let harness_intrinsic = *kani_fns.get(&KaniIntrinsic::AutomaticHarness.into()).unwrap(); + let kani_any = *kani_fns.get(&KaniModel::Any.into()).unwrap(); + let automatic_harnesses = unit + .harnesses + .iter() + .cloned() + .filter(|harness| { + let (def, _) = harness.ty().kind().fn_def().unwrap(); + def == harness_intrinsic + }) + .collect::>(); + Self { kani_any, automatic_harnesses } + } +} + +impl TransformPass for AutomaticHarnessPass { + fn transformation_type() -> TransformationType + where + Self: Sized, + { + TransformationType::Stubbing + } + + fn is_enabled(&self, query_db: &QueryDb) -> bool + where + Self: Sized, + { + matches!(query_db.args().reachability_analysis, ReachabilityType::AllFns) + } + + fn transform(&mut self, _tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + debug!(function=?instance.name(), "AutomaticHarnessPass::transform"); + + if !self.automatic_harnesses.contains(&instance) { + return (false, body); + } + + // Retrieve the generic arguments of the harness, which is the type of the function it is verifying, + // and then resolve `fn_to_verify`. + let kind = instance.args().0[0].expect_ty().kind(); + let (def, args) = kind.fn_def().unwrap(); + let fn_to_verify = Instance::resolve(def, &args).unwrap(); + let fn_to_verify_body = fn_to_verify.body().unwrap(); + + let mut harness_body = MutableBody::from(body); + harness_body.clear_body(TerminatorKind::Return); + let mut source = SourceInstruction::Terminator { bb: 0 }; + + let mut arg_locals = vec![]; + + // For each argument of `fn_to_verify`, create a nondeterministic value of its type + // by generating a kani::any() call and saving the result in `arg_local`. + for local_decl in fn_to_verify_body.arg_locals().iter() { + let arg_local = harness_body.new_local( + local_decl.ty, + source.span(harness_body.blocks()), + local_decl.mutability, + ); + let kani_any_inst = Instance::resolve( + self.kani_any, + &GenericArgs(vec![GenericArgKind::Type(local_decl.ty)]), + ) + .unwrap(); + harness_body.insert_call( + &kani_any_inst, + &mut source, + InsertPosition::Before, + vec![], + Place::from(arg_local), + ); + arg_locals.push(arg_local); + } + + let func_to_verify_ret = fn_to_verify_body.ret_local(); + let ret_place = Place::from(harness_body.new_local( + func_to_verify_ret.ty, + source.span(harness_body.blocks()), + func_to_verify_ret.mutability, + )); + + // Call `fn_to_verify` on the nondeterministic arguments generated above. + harness_body.insert_call( + &fn_to_verify, + &mut source, + InsertPosition::Before, + arg_locals.iter().map(|lcl| Operand::Copy(Place::from(*lcl))).collect::>(), + ret_place, + ); + + (true, harness_body.into()) + } +} diff --git a/kani-compiler/src/kani_middle/transform/contracts.rs b/kani-compiler/src/kani_middle/transform/contracts.rs index fea8a8a1bc3c..bd613b3d6663 100644 --- a/kani-compiler/src/kani_middle/transform/contracts.rs +++ b/kani-compiler/src/kani_middle/transform/contracts.rs @@ -1,6 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT //! This module contains code related to the MIR-to-MIR pass to enable contracts. +use crate::args::ReachabilityType; use crate::kani_middle::attributes::KaniAttributes; use crate::kani_middle::codegen_units::CodegenUnit; use crate::kani_middle::kani_functions::{KaniIntrinsic, KaniModel}; @@ -341,13 +342,34 @@ impl FunctionWithContractPass { /// verifying. pub fn new(tcx: TyCtxt, queries: &QueryDb, unit: &CodegenUnit) -> FunctionWithContractPass { if let Some(harness) = unit.harnesses.first() { - let attrs = KaniAttributes::for_instance(tcx, *harness); - let check_fn = attrs.interpret_for_contract_attribute().map(|(_, def_id, _)| def_id); - let replace_fns: HashSet<_> = attrs - .interpret_stub_verified_attribute() - .iter() - .map(|(_, def_id, _)| *def_id) - .collect(); + let (check_fn, replace_fns) = { + let harness_generic_args = harness.args().0; + // Manual harnesses have no arguments, so if there are generic arguments, + // we know this is an automatic harness + if matches!(queries.args().reachability_analysis, ReachabilityType::AllFns) + && !harness_generic_args.is_empty() + { + let kind = harness.args().0[0].expect_ty().kind(); + let (def, args) = kind.fn_def().unwrap(); + let fn_to_verify = Instance::resolve(def, &args).unwrap(); + // For automatic harnesses, the target is the function to verify, + // and stubs are empty. + ( + Some(rustc_internal::internal(tcx, fn_to_verify.def.def_id())), + HashSet::default(), + ) + } else { + let attrs = KaniAttributes::for_instance(tcx, *harness); + let check_fn = + attrs.interpret_for_contract_attribute().map(|(_, def_id, _)| def_id); + let replace_fns: HashSet<_> = attrs + .interpret_stub_verified_attribute() + .iter() + .map(|(_, def_id, _)| *def_id) + .collect(); + (check_fn, replace_fns) + } + }; let run_contract_fn = queries.kani_functions().get(&KaniModel::RunContract.into()).copied(); assert!(run_contract_fn.is_some(), "Failed to find Kani run contract function"); diff --git a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs index 206c0160a80d..d18b4b059063 100644 --- a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs +++ b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs @@ -74,8 +74,10 @@ impl TransformPass for IntrinsicGeneratorPass { KaniIntrinsic::CheckedSizeOf => (true, self.checked_size_of(body, instance)), KaniIntrinsic::IsInitialized => (true, self.is_initialized_body(body)), KaniIntrinsic::ValidValue => (true, self.valid_value_body(body)), - // This is handled in contracts pass for now. - KaniIntrinsic::WriteAny | KaniIntrinsic::AnyModifies => (false, body), + // The former two are handled in contracts pass for now, while the latter is handled in the the automatic harness pass. + KaniIntrinsic::WriteAny + | KaniIntrinsic::AnyModifies + | KaniIntrinsic::AutomaticHarness => (false, body), } } else { (false, body) diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index 8001b1f1d359..49e36461081c 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -26,6 +26,7 @@ use crate::kani_middle::transform::kani_intrinsics::IntrinsicGeneratorPass; use crate::kani_middle::transform::loop_contracts::LoopContractPass; use crate::kani_middle::transform::stubs::{ExternFnStubPass, FnStubPass}; use crate::kani_queries::QueryDb; +use automatic::AutomaticHarnessPass; use dump_mir_pass::DumpMirPass; use rustc_middle::ty::TyCtxt; use stable_mir::mir::Body; @@ -36,6 +37,7 @@ use std::fmt::Debug; use crate::kani_middle::transform::rustc_intrinsics::RustcIntrinsicsPass; pub use internal_mir::RustcInternalMir; +mod automatic; pub(crate) mod body; mod check_uninit; mod check_values; @@ -71,6 +73,8 @@ impl BodyTransformation { cache: Default::default(), }; let check_type = CheckType::new_assert_assume(queries); + // This has to come first, since creating harnesses affects later stubbing and contract passes. + transformer.add_pass(queries, AutomaticHarnessPass::new(unit, queries)); transformer.add_pass(queries, FnStubPass::new(&unit.stubs)); transformer.add_pass(queries, ExternFnStubPass::new(&unit.stubs)); transformer.add_pass(queries, FunctionWithContractPass::new(tcx, queries, &unit)); diff --git a/kani-driver/src/args/autoharness_args.rs b/kani-driver/src/args/autoharness_args.rs new file mode 100644 index 000000000000..d7a69070bf41 --- /dev/null +++ b/kani-driver/src/args/autoharness_args.rs @@ -0,0 +1,136 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +//! Implements the subcommand handling of the autoharness subcommand + +use std::path::PathBuf; + +use crate::args::{ValidateArgs, VerificationArgs}; +use clap::{Error, Parser, error::ErrorKind}; +use kani_metadata::UnstableFeature; + +#[derive(Debug, Parser)] +pub struct CommonAutoharnessArgs { + /// If specified, only autoharness functions that match this filter. This option can be provided + /// multiple times, which will verify all functions matching any of the filters. + /// Note that this filter will match against partial names, i.e., providing the name of a module will include all functions from that module. + /// Also note that if the function specified is unable to be automatically verified, this flag will have no effect. + #[arg( + long = "include-function", + num_args(1), + value_name = "FUNCTION", + conflicts_with = "exclude_function" + )] + pub include_function: Vec, + + /// If specified, only autoharness functions that do not match this filter. This option can be provided + /// multiple times, which will verify all functions that do not match any of the filters. + /// Note that this filter will match against partial names, i.e., providing the name of a module will exclude all functions from that module. + #[arg(long = "exclude-function", num_args(1), value_name = "FUNCTION")] + pub exclude_function: Vec, + // TODO: It would be nice if we could borrow --exact here from VerificationArgs to differentiate between partial/exact matches, + // like --harnesses does. Sharing arguments with VerificationArgs doesn't work with our current structure, though. +} + +/// Automatically verify functions in a crate. +#[derive(Debug, Parser)] +pub struct CargoAutoharnessArgs { + #[command(flatten)] + pub common_autoharness_args: CommonAutoharnessArgs, + + #[command(flatten)] + pub verify_opts: VerificationArgs, +} + +/// Automatically verify functions in a file. +#[derive(Debug, Parser)] +pub struct StandaloneAutoharnessArgs { + /// Rust crate's top file location. + #[arg(required = true)] + pub input: PathBuf, + + #[arg(long, hide = true)] + pub crate_name: Option, + + #[command(flatten)] + pub common_autoharness_args: CommonAutoharnessArgs, + + #[command(flatten)] + pub verify_opts: VerificationArgs, +} + +impl ValidateArgs for CargoAutoharnessArgs { + fn validate(&self) -> Result<(), Error> { + self.verify_opts.validate()?; + if !self + .verify_opts + .common_args + .unstable_features + .contains(UnstableFeature::UnstableOptions) + { + return Err(Error::raw( + ErrorKind::MissingRequiredArgument, + format!( + "The `autoharness` subcommand is unstable and requires -Z {}", + UnstableFeature::UnstableOptions + ), + )); + } + + if self + .verify_opts + .common_args + .unstable_features + .contains(UnstableFeature::ConcretePlayback) + { + return Err(Error::raw( + ErrorKind::ArgumentConflict, + "The autoharness subcommand does not support concrete playback", + )); + } + + Ok(()) + } +} + +impl ValidateArgs for StandaloneAutoharnessArgs { + fn validate(&self) -> Result<(), Error> { + self.verify_opts.validate()?; + if !self + .verify_opts + .common_args + .unstable_features + .contains(UnstableFeature::UnstableOptions) + { + return Err(Error::raw( + ErrorKind::MissingRequiredArgument, + format!( + "The `autoharness` subcommand is unstable and requires -Z {}", + UnstableFeature::UnstableOptions + ), + )); + } + if !self.input.is_file() { + return Err(Error::raw( + ErrorKind::InvalidValue, + format!( + "Invalid argument: Input invalid. `{}` is not a regular file.", + self.input.display() + ), + )); + } + + if self + .verify_opts + .common_args + .unstable_features + .contains(UnstableFeature::ConcretePlayback) + { + return Err(Error::raw( + ErrorKind::ArgumentConflict, + "The autoharness subcommand does not support concrete playback", + )); + } + + Ok(()) + } +} diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index b8b1ba60a50a..26e9927cf67c 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -3,6 +3,7 @@ //! Module that define Kani's command line interface. This includes all subcommands. pub mod assess_args; +pub mod autoharness_args; pub mod cargo; pub mod common; pub mod list_args; @@ -145,6 +146,8 @@ pub enum StandaloneSubcommand { VerifyStd(Box), /// List contracts and harnesses. List(Box), + /// Scan the input file for functions eligible for automatic (i.e., harness-free) verification and verify them. + Autoharness(Box), } #[derive(Debug, clap::Parser)] @@ -173,6 +176,9 @@ pub enum CargoKaniSubcommand { /// List contracts and harnesses. List(Box), + + /// Scan the crate for functions eligible for automatic (i.e., harness-free) verification and verify them. + Autoharness(Box), } // Common arguments for invoking Kani for verification purpose. This gets put into KaniContext, @@ -487,6 +493,7 @@ impl ValidateArgs for StandaloneArgs { match &self.command { Some(StandaloneSubcommand::VerifyStd(args)) => args.validate()?, Some(StandaloneSubcommand::List(args)) => args.validate()?, + Some(StandaloneSubcommand::Autoharness(args)) => args.validate()?, // TODO: Invoke PlaybackArgs::validate() None | Some(StandaloneSubcommand::Playback(..)) => {} }; @@ -532,6 +539,7 @@ impl ValidateArgs for CargoKaniSubcommand { match self { // Assess doesn't implement validation yet. CargoKaniSubcommand::Assess(_) => Ok(()), + CargoKaniSubcommand::Autoharness(autoharness) => autoharness.validate(), CargoKaniSubcommand::Playback(playback) => playback.validate(), CargoKaniSubcommand::List(list) => list.validate(), } diff --git a/kani-driver/src/assess/mod.rs b/kani-driver/src/assess/mod.rs index ec91918ac8b5..02b3da0974f8 100644 --- a/kani-driver/src/assess/mod.rs +++ b/kani-driver/src/assess/mod.rs @@ -53,7 +53,7 @@ fn assess_project(mut session: KaniSession) -> Result { session.args.jobs = Some(None); // -j, num_cpu } - let project = project::cargo_project(&session, true)?; + let project = project::cargo_project(&mut session, true)?; let cargo_metadata = project.cargo_metadata.as_ref().expect("built with cargo"); let packages_metadata = diff --git a/kani-driver/src/autoharness/mod.rs b/kani-driver/src/autoharness/mod.rs new file mode 100644 index 000000000000..69c49580cd77 --- /dev/null +++ b/kani-driver/src/autoharness/mod.rs @@ -0,0 +1,73 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use crate::call_cbmc::VerificationStatus; +use crate::call_single_file::to_rustc_arg; +use crate::harness_runner::HarnessResult; +use crate::session::KaniSession; +use anyhow::Result; + +impl KaniSession { + /// Enable autoharness mode. + pub fn enable_autoharness(&mut self) { + self.auto_harness = true; + } + + /// Add the compiler arguments specific to the `autoharness` subcommand. + pub fn add_auto_harness_args(&mut self, included: Vec, excluded: Vec) { + for func in included { + self.pkg_args + .push(to_rustc_arg(vec![format!("--autoharness-include-function {}", func)])); + } + for func in excluded { + self.pkg_args + .push(to_rustc_arg(vec![format!("--autoharness-exclude-function {}", func)])); + } + } + + /// Prints the results from running the `autoharness` subcommand. + pub fn print_autoharness_summary(&self, automatic: Vec<&HarnessResult<'_>>) -> Result<()> { + let (successes, failures): (Vec<_>, Vec<_>) = + automatic.into_iter().partition(|r| r.result.status == VerificationStatus::Success); + + let succeeding = successes.len(); + let failing = failures.len(); + let total = succeeding + failing; + + // TODO: it would be nice if we had access to which functions the user included/excluded here + // so that we could print a comparison for them of any of the included functions that we skipped. + println!("Autoharness Summary:"); + println!( + "Note that Kani will only generate an automatic harness for a function if it determines that each of its arguments implement the Arbitrary trait." + ); + println!( + "Examine the summary closely to determine which functions were automatically verified." + ); + + // Since autoverification skips over some functions, print the successes to make it easier to see what we verified in one place. + for success in successes { + println!("Verification succeeded for - {}", success.harness.pretty_name); + } + + for failure in failures { + println!("Verification failed for - {}", failure.harness.pretty_name); + } + + if total > 0 { + println!( + "Complete - {succeeding} successfully verified functions, {failing} failures, {total} total." + ); + } else { + println!( + "No functions were eligible for automatic verification. Functions can only be automatically verified if each of their arguments implement kani::Arbitrary." + ); + println!( + "If you specified --include-function or --exclude-function, make sure that your filters were not overly restrictive." + ); + } + + // Manual harness summary may come afterward, so separate them with a new line. + println!(); + Ok(()) + } +} diff --git a/kani-driver/src/call_cargo.rs b/kani-driver/src/call_cargo.rs index 0bc716115989..4c39b03183b6 100644 --- a/kani-driver/src/call_cargo.rs +++ b/kani-driver/src/call_cargo.rs @@ -126,7 +126,7 @@ crate-type = ["lib"] } /// Calls `cargo_build` to generate `*.symtab.json` files in `target_dir` - pub fn cargo_build(&self, keep_going: bool) -> Result { + pub fn cargo_build(&mut self, keep_going: bool) -> Result { let build_target = env!("TARGET"); // see build.rs let metadata = self.cargo_metadata(build_target)?; let target_dir = self @@ -183,13 +183,12 @@ crate-type = ["lib"] } // Arguments that will only be passed to the target package. - let mut pkg_args: Vec = vec![]; - pkg_args.extend(["--".to_string(), self.reachability_arg()]); + self.pkg_args.push(self.reachability_arg()); if let Some(backend_arg) = self.backend_arg() { - pkg_args.push(backend_arg); + self.pkg_args.push(backend_arg); } if self.args.no_assert_contracts { - pkg_args.push("--no-assert-contracts".into()); + self.pkg_args.push("--no-assert-contracts".into()); } let mut found_target = false; @@ -202,7 +201,7 @@ crate-type = ["lib"] cmd.args(&cargo_args) .args(vec!["-p", &package.id.to_string()]) .args(verification_target.to_args()) - .args(&pkg_args) + .args(&self.pkg_args) .env("RUSTC", &self.kani_compiler) // Use CARGO_ENCODED_RUSTFLAGS instead of RUSTFLAGS is preferred. See // https://doc.rust-lang.org/cargo/reference/environment-variables.html diff --git a/kani-driver/src/harness_runner.rs b/kani-driver/src/harness_runner.rs index 96b2e732f5ba..56e3f4c09ed2 100644 --- a/kani-driver/src/harness_runner.rs +++ b/kani-driver/src/harness_runner.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use anyhow::{Error, Result, bail}; -use kani_metadata::{ArtifactType, HarnessMetadata}; +use kani_metadata::{ArtifactType, HarnessKind, HarnessMetadata}; use rayon::prelude::*; use std::fs::File; use std::io::Write; @@ -11,7 +11,7 @@ use std::path::Path; use crate::args::OutputFormat; use crate::call_cbmc::{VerificationResult, VerificationStatus}; use crate::project::Project; -use crate::session::KaniSession; +use crate::session::{BUG_REPORT_URL, KaniSession}; use std::env::current_dir; use std::path::PathBuf; @@ -202,11 +202,28 @@ impl KaniSession { ) -> Result { let thread_index = rayon::current_thread_index().unwrap_or_default(); if !self.args.common_args.quiet { - if rayon::current_num_threads() > 1 { - println!("Thread {thread_index}: Checking harness {}...", harness.pretty_name); + // If the harness is automatically generated, pretty_name refers to the function under verification. + let mut msg = if harness.is_automatically_generated { + if matches!(harness.attributes.kind, HarnessKind::Proof) { + format!( + "Autoharness: Checking function {} against all possible inputs...", + harness.pretty_name + ) + } else { + format!( + "Autoharness: Checking function {}'s contract against all possible inputs...", + harness.pretty_name + ) + } } else { - println!("Checking harness {}...", harness.pretty_name); + format!("Checking harness {}...", harness.pretty_name) + }; + + if rayon::current_num_threads() > 1 { + msg = format!("Thread {thread_index}: {msg}"); } + + println!("{msg}"); } let mut result = self.with_timer(|| self.run_cbmc(binary, harness), "run_cbmc")?; @@ -222,54 +239,65 @@ impl KaniSession { /// Note: Takes `self` "by ownership". This function wants to be able to drop before /// exiting with an error code, if needed. pub(crate) fn print_final_summary(self, results: &[HarnessResult<'_>]) -> Result<()> { + if self.args.common_args.quiet { + return Ok(()); + } + + let (automatic, manual): (Vec<_>, Vec<_>) = + results.iter().partition(|r| r.harness.is_automatically_generated); + + if self.auto_harness { + self.print_autoharness_summary(automatic)?; + } + let (successes, failures): (Vec<_>, Vec<_>) = - results.iter().partition(|r| r.result.status == VerificationStatus::Success); + manual.into_iter().partition(|r| r.result.status == VerificationStatus::Success); let succeeding = successes.len(); let failing = failures.len(); let total = succeeding + failing; - if self.args.concrete_playback.is_some() - && !self.args.common_args.quiet - && results.iter().all(|r| !r.result.generated_concrete_test) - { - println!( - "INFO: The concrete playback feature never generated unit tests because there were no failing harnesses." - ) + if self.args.concrete_playback.is_some() { + if failures.is_empty() { + println!( + "INFO: The concrete playback feature never generated unit tests because there were no failing harnesses." + ) + } else if failures.iter().all(|r| !r.result.generated_concrete_test) { + eprintln!( + "The concrete playback feature did not generate unit tests, but there were failing harnesses. Please file a bug report at {BUG_REPORT_URL}" + ) + } } // We currently omit a summary if there was just 1 harness - if !self.args.common_args.quiet { - if failing > 0 { - println!("Summary:"); - } - for failure in failures.iter() { - println!("Verification failed for - {}", failure.harness.pretty_name); - } + if failing > 0 { + println!("Manual Harness Summary:"); + } + for failure in failures.iter() { + println!("Verification failed for - {}", failure.harness.pretty_name); + } - if total > 0 { - println!( - "Complete - {succeeding} successfully verified harnesses, {failing} failures, {total} total." - ); - } else { - match self.args.harnesses.as_slice() { - [] => - // TODO: This could use a better message, possibly with links to Kani documentation. - // New users may encounter this and could use a pointer to how to write proof harnesses. - { - println!( - "No proof harnesses (functions with #[kani::proof]) were found to verify." - ) - } - [harness] => { - bail!("no harnesses matched the harness filter: `{harness}`") - } - harnesses => bail!( - "no harnesses matched the harness filters: `{}`", - harnesses.join("`, `") - ), - }; - } + if total > 0 { + println!( + "Complete - {succeeding} successfully verified harnesses, {failing} failures, {total} total." + ); + } else { + match self.args.harnesses.as_slice() { + [] => + // TODO: This could use a better message, possibly with links to Kani documentation. + // New users may encounter this and could use a pointer to how to write proof harnesses. + { + println!( + "No proof harnesses (functions with #[kani::proof]) were found to verify." + ) + } + [harness] => { + bail!("no harnesses matched the harness filter: `{harness}`") + } + harnesses => { + bail!("no harnesses matched the harness filters: `{}`", harnesses.join("`, `")) + } + }; } if self.args.coverage { diff --git a/kani-driver/src/list/collect_metadata.rs b/kani-driver/src/list/collect_metadata.rs index a7901e997ab1..588cab0b63af 100644 --- a/kani-driver/src/list/collect_metadata.rs +++ b/kani-driver/src/list/collect_metadata.rs @@ -78,12 +78,12 @@ fn process_metadata(metadata: Vec) -> ListMetadata { pub fn list_cargo(args: CargoListArgs, mut verify_opts: VerificationArgs) -> Result<()> { let quiet = args.common_args.quiet; verify_opts.common_args = args.common_args; - let session = KaniSession::new(verify_opts)?; + let mut session = KaniSession::new(verify_opts)?; if !quiet { print_kani_version(InvocationType::CargoKani(vec![])); } - let project = cargo_project(&session, false)?; + let project = cargo_project(&mut session, false)?; let list_metadata = process_metadata(project.metadata); output_list_results(list_metadata, args.format, quiet) diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 07405a30a307..9b82c8467e26 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -22,6 +22,7 @@ use tracing::debug; mod args; mod args_toml; mod assess; +mod autoharness; mod call_cargo; mod call_cbmc; mod call_goto_cc; @@ -72,28 +73,37 @@ fn cargokani_main(input_args: Vec) -> Result<()> { return list_cargo(*list_args, args.verify_opts); } - let session = session::KaniSession::new(args.verify_opts)?; - - if !session.args.common_args.quiet { - print_kani_version(InvocationType::CargoKani(input_args)); - } - - match args.command { - Some(CargoKaniSubcommand::Assess(args)) => { - return assess::run_assess(session, *args); + let mut session = match args.command { + Some(CargoKaniSubcommand::Assess(assess_args)) => { + let sess = session::KaniSession::new(args.verify_opts)?; + return assess::run_assess(sess, *assess_args); + } + Some(CargoKaniSubcommand::Autoharness(args)) => { + let mut sess = session::KaniSession::new(args.verify_opts)?; + sess.enable_autoharness(); + sess.add_auto_harness_args( + args.common_autoharness_args.include_function, + args.common_autoharness_args.exclude_function, + ); + + sess } Some(CargoKaniSubcommand::Playback(args)) => { return playback_cargo(*args); } Some(CargoKaniSubcommand::List(_)) => unreachable!(), - None => {} + None => session::KaniSession::new(args.verify_opts)?, + }; + + if !session.args.common_args.quiet { + print_kani_version(InvocationType::CargoKani(input_args)); } if session.args.assess { return assess::run_assess(session, assess::AssessArgs::default()); } - let project = project::cargo_project(&session, false)?; + let project = project::cargo_project(&mut session, false)?; if session.args.only_codegen { Ok(()) } else { verify_project(project, session) } } @@ -103,6 +113,21 @@ fn standalone_main() -> Result<()> { check_is_valid(&args); let (session, project) = match args.command { + Some(StandaloneSubcommand::Autoharness(args)) => { + let mut session = KaniSession::new(args.verify_opts)?; + session.enable_autoharness(); + session.add_auto_harness_args( + args.common_autoharness_args.include_function, + args.common_autoharness_args.exclude_function, + ); + + if !session.args.common_args.quiet { + print_kani_version(InvocationType::Standalone); + } + + let project = project::standalone_project(&args.input, args.crate_name, &session)?; + (session, project) + } Some(StandaloneSubcommand::Playback(args)) => return playback_standalone(*args), Some(StandaloneSubcommand::List(list_args)) => { return list_standalone(*list_args, args.verify_opts); diff --git a/kani-driver/src/metadata.rs b/kani-driver/src/metadata.rs index 94a5393408d3..d112591c6532 100644 --- a/kani-driver/src/metadata.rs +++ b/kani-driver/src/metadata.rs @@ -176,6 +176,10 @@ fn find_proof_harnesses<'a>( debug!(?targets, "find_proof_harness"); let mut result = vec![]; for md in all_harnesses.iter() { + // --harnesses should not select automatic harnesses + if md.is_automatically_generated { + continue; + } if exact_filter { // Check for exact match only if targets.contains(&md.pretty_name) { @@ -224,6 +228,7 @@ pub mod tests { goto_file: model_file, contract: Default::default(), has_loop_contracts: false, + is_automatically_generated: false, } } diff --git a/kani-driver/src/project.rs b/kani-driver/src/project.rs index da04cc0517ba..2a1aad2c1eff 100644 --- a/kani-driver/src/project.rs +++ b/kani-driver/src/project.rs @@ -175,7 +175,7 @@ impl Artifact { /// Generate a project using `cargo`. /// Accept a boolean to build as many targets as possible. The number of failures in that case can /// be collected from the project. -pub fn cargo_project(session: &KaniSession, keep_going: bool) -> Result { +pub fn cargo_project(session: &mut KaniSession, keep_going: bool) -> Result { let outputs = session.cargo_build(keep_going)?; let outdir = outputs.outdir.canonicalize()?; // For the MIR Linker we know there is only one metadata per crate. Use that in our favor. diff --git a/kani-driver/src/session.rs b/kani-driver/src/session.rs index 187a17ae6981..6645db06e45e 100644 --- a/kani-driver/src/session.rs +++ b/kani-driver/src/session.rs @@ -17,6 +17,9 @@ use tokio::process::Command as TokioCommand; use tracing::level_filters::LevelFilter; use tracing_subscriber::{EnvFilter, Registry, layer::SubscriberExt}; +pub const BUG_REPORT_URL: &str = + "https://github.com/model-checking/kani/issues/new?labels=bug&template=bug_report.md"; + /// Environment variable used to control this session log tracing. /// This is the same variable used to control `kani-compiler` logs. Note that you can still control /// the driver logs separately, by using the logger directives to select the kani-driver crate. @@ -28,6 +31,12 @@ pub struct KaniSession { /// The common command-line arguments pub args: VerificationArgs, + /// Automatically verify functions in the crate, in addition to running manual harnesses. + pub auto_harness: bool, + + /// The arguments that will be passed to the target package, i.e. kani_compiler. + pub pkg_args: Vec, + /// Include all publicly-visible symbols in the generated goto binary, not just those reachable from /// a proof harness. Useful when attempting to verify things that were not annotated with kani /// proof attributes. @@ -62,6 +71,8 @@ impl KaniSession { Ok(KaniSession { args, + auto_harness: false, + pkg_args: vec!["--".to_string()], codegen_tests: false, kani_compiler: install.kani_compiler()?, kani_lib_c: install.kani_lib_c()?, @@ -88,13 +99,20 @@ impl KaniSession { /// Determine which symbols Kani should codegen (i.e. by slicing away symbols /// that are considered unreachable.) pub fn reachability_mode(&self) -> ReachabilityMode { - if self.codegen_tests { ReachabilityMode::Tests } else { ReachabilityMode::ProofHarnesses } + if self.codegen_tests { + ReachabilityMode::Tests + } else if self.auto_harness { + ReachabilityMode::AllFns + } else { + ReachabilityMode::ProofHarnesses + } } } #[derive(Debug, Copy, Clone, Display)] #[strum(serialize_all = "snake_case")] pub enum ReachabilityMode { + AllFns, #[strum(to_string = "harnesses")] ProofHarnesses, Tests, diff --git a/kani_metadata/src/harness.rs b/kani_metadata/src/harness.rs index 6932b15dc1a7..227fe8df09d4 100644 --- a/kani_metadata/src/harness.rs +++ b/kani_metadata/src/harness.rs @@ -38,6 +38,8 @@ pub struct HarnessMetadata { pub contract: Option, /// If the harness contains some usage of loop contracts. pub has_loop_contracts: bool, + /// If the harness was automatically generated or manually written. + pub is_automatically_generated: bool, } /// The attributes added by the user to control how a harness is executed. diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index 37a05821bd33..d3940160a91a 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -434,6 +434,12 @@ macro_rules! kani_intrinsics { } } + /// Used to hold the bodies of automatically generated harnesses. + #[kanitool::fn_marker = "AutomaticHarnessIntrinsic"] + pub fn automatic_harness() { + super::kani_intrinsic() + } + /// A way to break the ownerhip rules. Only used by contracts where we can /// guarantee it is done safely. #[inline(never)] diff --git a/tests/script-based-pre/cargo_autoharness_contracts/Cargo.toml b/tests/script-based-pre/cargo_autoharness_contracts/Cargo.toml new file mode 100644 index 000000000000..10a47f48262b --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_contracts/Cargo.toml @@ -0,0 +1,10 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +[package] +name = "cargo_autoharness_contracts" +version = "0.1.0" +edition = "2024" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tests/script-based-pre/cargo_autoharness_contracts/config.yml b/tests/script-based-pre/cargo_autoharness_contracts/config.yml new file mode 100644 index 000000000000..38bf8ee6c38c --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_contracts/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: contracts.sh +expected: contracts.expected diff --git a/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected b/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected new file mode 100644 index 000000000000..1e25913b6765 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected @@ -0,0 +1,28 @@ +Autoharness: Checking function should_fail::max's contract against all possible inputs... +assertion\ + - Status: FAILURE\ + - Description: "|result : &u32| *result == x" + +Autoharness: Checking function should_pass::has_loop_contract's contract against all possible inputs... +should_pass::has_loop_contract.assertion\ + - Status: SUCCESS\ + - Description: "assertion failed: x == 2" + +Autoharness: Checking function should_pass::has_recursion_gcd's contract against all possible inputs... +assertion\ + - Status: SUCCESS\ + - Description: "|result : &u8| *result != 0 && x % *result == 0 && y % *result == 0" + +Autoharness: Checking function should_pass::div's contract against all possible inputs... + +Autoharness: Checking function should_pass::unchecked_mul's contract against all possible inputs... +arithmetic_overflow\ + - Status: SUCCESS\ + - Description: "attempt to compute `unchecked_mul` which would overflow" + +Verification succeeded for - should_pass::unchecked_mul +Verification succeeded for - should_pass::has_loop_contract +Verification succeeded for - should_pass::has_recursion_gcd +Verification succeeded for - should_pass::div +Verification failed for - should_fail::max +Complete - 4 successfully verified functions, 1 failures, 5 total. diff --git a/tests/script-based-pre/cargo_autoharness_contracts/contracts.sh b/tests/script-based-pre/cargo_autoharness_contracts/contracts.sh new file mode 100755 index 000000000000..caad53eb8ac4 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_contracts/contracts.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +cargo kani autoharness -Z unstable-options -Z function-contracts -Z loop-contracts +# We expect verification to fail, so the above command will produce an exit status of 1 +# However, we don't want the test to fail because of that exit status; we only want it to fail if the expected file doesn't match +# So, exit with a status code of 0 explicitly. +exit 0; diff --git a/tests/script-based-pre/cargo_autoharness_contracts/src/lib.rs b/tests/script-based-pre/cargo_autoharness_contracts/src/lib.rs new file mode 100644 index 000000000000..c9f9dd3fd1b0 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_contracts/src/lib.rs @@ -0,0 +1,56 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Test that the autoharness subcommand correctly verifies contracts, +// i.e., that it detects the presence of a contract and generates a contract +// harness instead of a standard harness. + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + +mod should_pass { + #[kani::requires(divisor != 0)] + fn div(dividend: u32, divisor: u32) -> u32 { + dividend / divisor + } + + #[kani::requires(x != 0 && y != 0)] + #[kani::ensures(|result : &u8| *result != 0 && x % *result == 0 && y % *result == 0)] + #[kani::recursion] + fn has_recursion_gcd(x: u8, y: u8) -> u8 { + let mut max = x; + let mut min = y; + if min > max { + let val = max; + max = min; + min = val; + } + + let res = max % min; + if res == 0 { min } else { has_recursion_gcd(min, res) } + } + + fn has_loop_contract() { + let mut x: u8 = kani::any_where(|i| *i >= 2); + + #[kani::loop_invariant(x >= 2)] + while x > 2 { + x = x - 1; + } + + assert!(x == 2); + } + + // Test that we can autoharness functions for unsafe functions with contracts + #[kani::requires(!left.overflowing_mul(rhs).1)] + unsafe fn unchecked_mul(left: u8, rhs: u8) -> u8 { + left.unchecked_mul(rhs) + } +} + +mod should_fail { + #[kani::ensures(|result : &u32| *result == x)] + fn max(x: u32, y: u32) -> u32 { + if x > y { x } else { y } + } +} diff --git a/tests/script-based-pre/cargo_autoharness_exclude/Cargo.toml b/tests/script-based-pre/cargo_autoharness_exclude/Cargo.toml new file mode 100644 index 000000000000..cceaa1bb7651 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_exclude/Cargo.toml @@ -0,0 +1,10 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +[package] +name = "cargo_autoharness_include" +version = "0.1.0" +edition = "2024" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tests/script-based-pre/cargo_autoharness_exclude/config.yml b/tests/script-based-pre/cargo_autoharness_exclude/config.yml new file mode 100644 index 000000000000..d3c26a0301f4 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_exclude/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: exclude.sh +expected: exclude.expected diff --git a/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected b/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected new file mode 100644 index 000000000000..91d9eb4c399a --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected @@ -0,0 +1,4 @@ +Autoharness: Checking function include::simple against all possible inputs... +VERIFICATION:- SUCCESSFUL +Verification succeeded for - include::simple +Complete - 1 successfully verified functions, 0 failures, 1 total. \ No newline at end of file diff --git a/tests/script-based-pre/cargo_autoharness_exclude/exclude.sh b/tests/script-based-pre/cargo_autoharness_exclude/exclude.sh new file mode 100755 index 000000000000..492325dc5c81 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_exclude/exclude.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +cargo kani autoharness -Z unstable-options --exclude-function exclude diff --git a/tests/script-based-pre/cargo_autoharness_exclude/src/lib.rs b/tests/script-based-pre/cargo_autoharness_exclude/src/lib.rs new file mode 100644 index 000000000000..7757239126c0 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_exclude/src/lib.rs @@ -0,0 +1,23 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Test that the automatic harness generation feature selects functions correctly +// when --exclude-function is provided. + +mod include { + fn simple(x: u8, _y: u16) -> u8 { + x + } + + // Doesn't implement Arbitrary, so still should not be included. + fn generic(x: u32, _y: T) -> u32 { + x + } +} + +// These functions are eligible for autoverification, but are excluded. +mod excluded { + fn simple(x: u8, _y: u16) -> u8 { + x + } +} diff --git a/tests/script-based-pre/cargo_autoharness_filter/Cargo.toml b/tests/script-based-pre/cargo_autoharness_filter/Cargo.toml new file mode 100644 index 000000000000..41f0d6133106 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_filter/Cargo.toml @@ -0,0 +1,10 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +[package] +name = "cargo_autoharness_filter" +version = "0.1.0" +edition = "2024" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tests/script-based-pre/cargo_autoharness_filter/config.yml b/tests/script-based-pre/cargo_autoharness_filter/config.yml new file mode 100644 index 000000000000..90ee1970e96c --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_filter/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: filter.sh +expected: filter.expected diff --git a/tests/script-based-pre/cargo_autoharness_filter/filter.expected b/tests/script-based-pre/cargo_autoharness_filter/filter.expected new file mode 100644 index 000000000000..9db37edf8232 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_filter/filter.expected @@ -0,0 +1,43 @@ +Autoharness: Checking function yes_harness::f_tuple against all possible inputs... +Autoharness: Checking function yes_harness::f_maybe_uninit against all possible inputs... +Autoharness: Checking function yes_harness::f_result against all possible inputs... +Autoharness: Checking function yes_harness::f_option against all possible inputs... +Autoharness: Checking function yes_harness::f_array against all possible inputs... +Autoharness: Checking function yes_harness::f_nonzero_isize against all possible inputs... +Autoharness: Checking function yes_harness::f_nonzero_i128 against all possible inputs... +Autoharness: Checking function yes_harness::f_nonzero_i64 against all possible inputs... +Autoharness: Checking function yes_harness::f_nonzero_i32 against all possible inputs... +Autoharness: Checking function yes_harness::f_nonzero_i16 against all possible inputs... +Autoharness: Checking function yes_harness::f_nonzero_i8 against all possible inputs... +Autoharness: Checking function yes_harness::f_nonzero_usize against all possible inputs... +Autoharness: Checking function yes_harness::f_nonzero_u128 against all possible inputs... +Autoharness: Checking function yes_harness::f_nonzero_u64 against all possible inputs... +Autoharness: Checking function yes_harness::f_nonzero_u32 against all possible inputs... +Autoharness: Checking function yes_harness::f_nonzero_u16 against all possible inputs... +Autoharness: Checking function yes_harness::f_nonzero_u8 against all possible inputs... +Autoharness: Checking function yes_harness::f_f128 against all possible inputs... +Autoharness: Checking function yes_harness::f_f16 against all possible inputs... +Autoharness: Checking function yes_harness::f_f64 against all possible inputs... +Autoharness: Checking function yes_harness::f_f32 against all possible inputs... +Autoharness: Checking function yes_harness::f_char against all possible inputs... +Autoharness: Checking function yes_harness::f_bool against all possible inputs... +Autoharness: Checking function yes_harness::f_isize against all possible inputs... +Autoharness: Checking function yes_harness::f_i128 against all possible inputs... +Autoharness: Checking function yes_harness::f_i64 against all possible inputs... +Autoharness: Checking function yes_harness::f_i32 against all possible inputs... +Autoharness: Checking function yes_harness::f_i16 against all possible inputs... +Autoharness: Checking function yes_harness::f_i8 against all possible inputs... +Autoharness: Checking function yes_harness::f_usize against all possible inputs... +Autoharness: Checking function yes_harness::f_u128 against all possible inputs... +Autoharness: Checking function yes_harness::f_u64 against all possible inputs... +Autoharness: Checking function yes_harness::f_u32 against all possible inputs... +Autoharness: Checking function yes_harness::f_u16 against all possible inputs... +Autoharness: Checking function yes_harness::f_u8 against all possible inputs... +Autoharness: Checking function yes_harness::f_unsupported_return_type against all possible inputs... +Autoharness: Checking function yes_harness::f_multiple_args against all possible inputs... +Autoharness: Checking function yes_harness::f_derives_arbitrary against all possible inputs... +Autoharness: Checking function yes_harness::f_manually_implements_arbitrary against all possible inputs... +Autoharness: Checking function yes_harness::f_phantom_data against all possible inputs... +Autoharness: Checking function yes_harness::f_phantom_pinned against all possible inputs... +Autoharness: Checking function yes_harness::empty_body against all possible inputs... +Complete - 42 successfully verified functions, 0 failures, 42 total. \ No newline at end of file diff --git a/tests/script-based-pre/cargo_autoharness_filter/filter.sh b/tests/script-based-pre/cargo_autoharness_filter/filter.sh new file mode 100755 index 000000000000..cec2f2e2a10b --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_filter/filter.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +cargo kani autoharness -Z unstable-options diff --git a/tests/script-based-pre/cargo_autoharness_filter/src/lib.rs b/tests/script-based-pre/cargo_autoharness_filter/src/lib.rs new file mode 100644 index 000000000000..bb278c73a2e7 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_filter/src/lib.rs @@ -0,0 +1,210 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Test that the automatic harness generation feature filters functions correctly, +// i.e., that it generates harnesses for a function iff: +// - It is not itself a harness +// - All of its arguments implement Arbitrary, either trivially or through a user-provided implementation +// The bodies of these functions are purposefully left as simple as possible; +// the point is not to test the generated harnesses themselves, +// but only that we generate the harnesses in the first place. + +#![feature(f16)] +#![feature(f128)] + +extern crate kani; +use kani::Arbitrary; + +#[derive(Arbitrary)] +struct DerivesArbitrary { + x: u8, + y: u32, +} + +struct ManuallyImplementsArbitrary { + x: u8, + y: u32, +} + +impl Arbitrary for ManuallyImplementsArbitrary { + fn any() -> Self { + Self { x: kani::any(), y: kani::any() } + } +} + +struct DoesntImplementArbitrary { + x: u8, + y: u32, +} + +mod yes_harness { + use crate::{DerivesArbitrary, ManuallyImplementsArbitrary}; + use std::marker::{PhantomData, PhantomPinned}; + use std::mem::MaybeUninit; + use std::num::NonZero; + + // Kani-provided Arbitrary implementations + fn f_u8(x: u8) -> u8 { + x + } + fn f_u16(x: u16) -> u16 { + x + } + fn f_u32(x: u32) -> u32 { + x + } + fn f_u64(x: u64) -> u64 { + x + } + fn f_u128(x: u128) -> u128 { + x + } + fn f_usize(x: usize) -> usize { + x + } + fn f_i8(x: i8) -> i8 { + x + } + fn f_i16(x: i16) -> i16 { + x + } + fn f_i32(x: i32) -> i32 { + x + } + fn f_i64(x: i64) -> i64 { + x + } + fn f_i128(x: i128) -> i128 { + x + } + fn f_isize(x: isize) -> isize { + x + } + fn f_bool(x: bool) -> bool { + x + } + fn f_char(x: char) -> char { + x + } + fn f_f32(x: f32) -> f32 { + x + } + fn f_f64(x: f64) -> f64 { + x + } + fn f_f16(x: f16) -> f16 { + x + } + fn f_f128(x: f128) -> f128 { + x + } + fn f_nonzero_u8(x: NonZero) -> NonZero { + x + } + fn f_nonzero_u16(x: NonZero) -> NonZero { + x + } + fn f_nonzero_u32(x: NonZero) -> NonZero { + x + } + fn f_nonzero_u64(x: NonZero) -> NonZero { + x + } + fn f_nonzero_u128(x: NonZero) -> NonZero { + x + } + fn f_nonzero_usize(x: NonZero) -> NonZero { + x + } + fn f_nonzero_i8(x: NonZero) -> NonZero { + x + } + fn f_nonzero_i16(x: NonZero) -> NonZero { + x + } + fn f_nonzero_i32(x: NonZero) -> NonZero { + x + } + fn f_nonzero_i64(x: NonZero) -> NonZero { + x + } + fn f_nonzero_i128(x: NonZero) -> NonZero { + x + } + fn f_nonzero_isize(x: NonZero) -> NonZero { + x + } + fn f_array(x: [u8; 4]) -> [u8; 4] { + x + } + fn f_option(x: Option) -> Option { + x + } + fn f_result(x: Result) -> Result { + x + } + fn f_maybe_uninit(x: MaybeUninit) -> MaybeUninit { + x + } + fn f_tuple(x: (u8, u16, u32)) -> (u8, u16, u32) { + x + } + + // The return type doesn't implement Arbitrary, but that shouldn't matter + fn f_unsupported_return_type(x: u8) -> Vec { + vec![x] + } + + // Multiple arguments of different types, all of which implement Arbitrary + fn f_multiple_args(x: u8, y: u16, z: u32) -> (u8, u16, u32) { + (x, y, z) + } + + // User-defined types that implement Arbitrary + fn f_derives_arbitrary(x: DerivesArbitrary) -> DerivesArbitrary { + x + } + fn f_manually_implements_arbitrary( + x: ManuallyImplementsArbitrary, + ) -> ManuallyImplementsArbitrary { + x + } + + fn f_phantom_data(x: PhantomData) -> PhantomData { + x + } + + fn f_phantom_pinned(x: PhantomPinned) -> PhantomPinned { + x + } + + fn empty_body(_x: u8, _y: u16) {} +} + +mod no_harness { + use crate::{DerivesArbitrary, DoesntImplementArbitrary}; + fn unsupported_generic(x: u32, _y: T) -> u32 { + x + } + fn unsupported_ref(x: u32, _y: &i32) -> u32 { + x + } + fn unsupported_const_pointer(x: u32, _y: *const i32) -> u32 { + x + } + fn unsupported_mut_pointer(x: u32, _y: *mut i32) -> u32 { + x + } + fn unsupported_vec(x: u32, _y: Vec) -> u32 { + x + } + fn unsupported_slice(x: u32, _y: &[u8]) -> u32 { + x + } + fn doesnt_implement_arbitrary( + x: DoesntImplementArbitrary, + _y: DerivesArbitrary, + ) -> DoesntImplementArbitrary { + x + } +} diff --git a/tests/script-based-pre/cargo_autoharness_harnesses_fail/Cargo.toml b/tests/script-based-pre/cargo_autoharness_harnesses_fail/Cargo.toml new file mode 100644 index 000000000000..948b9e1666a4 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_harnesses_fail/Cargo.toml @@ -0,0 +1,10 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +[package] +name = "cargo_autoharness_harnesses_fail" +version = "0.1.0" +edition = "2024" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tests/script-based-pre/cargo_autoharness_harnesses_fail/config.yml b/tests/script-based-pre/cargo_autoharness_harnesses_fail/config.yml new file mode 100644 index 000000000000..f5b3400d03bf --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_harnesses_fail/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: harnesses_fail.sh +expected: harnesses_fail.expected diff --git a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected new file mode 100644 index 000000000000..7ceed3cc019a --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected @@ -0,0 +1,57 @@ +Autoharness: Checking function panic against all possible inputs... +panic.assertion\ + - Status: FAILURE\ + - Description: "explicit panic" + +Autoharness: Checking function integer_overflow against all possible inputs... +assertion\ + - Status: FAILURE + - Description: "attempt to add with overflow" + +Autoharness: Checking function oob_unsafe_array_access against all possible inputs... +oob_unsafe_array_access.pointer_dereference\ + - Status: FAILURE\ + - Description: "dereference failure: pointer outside object bounds" + +Autoharness: Checking function oob_safe_array_access against all possible inputs... +assertion\ + - Status: FAILURE\ + - Description: "index out of bounds: the length is less than or equal to the given index" + +Autoharness: Checking function unchecked_mul against all possible inputs... +arithmetic_overflow\ + - Status: FAILURE\ + - Description: "attempt to compute `unchecked_mul` which would overflow" + +Verification failed for - panic +Verification failed for - integer_overflow +Verification failed for - oob_unsafe_array_access +Verification failed for - oob_safe_array_access +Verification failed for - unchecked_mul +Complete - 0 successfully verified functions, 5 failures, 5 total. + +Checking harness panic_harness... +panic.assertion\ + - Status: FAILURE\ + - Description: "explicit panic" + +Checking harness integer_overflow_harness... +assertion\ + - Status: FAILURE\ + - Description: "attempt to add with overflow" + +Checking harness oob_unsafe_array_access_harness... +oob_unsafe_array_access.pointer_dereference\ + - Status: FAILURE\ + +Checking harness oob_safe_array_access_harness... +assertion\ + - Status: FAILURE\ + - Description: "index out of bounds: the length is less than or equal to the given index" + +Checking harness unchecked_mul_harness... +arithmetic_overflow\ + - Status: FAILURE\ + - Description: "attempt to compute `unchecked_mul` which would overflow" + +Complete - 0 successfully verified harnesses, 5 failures, 5 total. diff --git a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.sh b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.sh new file mode 100755 index 000000000000..b34d3203311f --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +cargo kani autoharness -Z unstable-options +# We expect verification to fail, so the above command will produce an exit status of 1 +# However, we don't want the test to fail because of that exit status; we only want it to fail if the expected file doesn't match +# So, exit with a status code of 0 explicitly. +exit 0; diff --git a/tests/script-based-pre/cargo_autoharness_harnesses_fail/src/lib.rs b/tests/script-based-pre/cargo_autoharness_harnesses_fail/src/lib.rs new file mode 100644 index 000000000000..591c6b92ee5e --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_harnesses_fail/src/lib.rs @@ -0,0 +1,68 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Test the bodies of the automatically generated harnesses: +// do they catch the same failures as manual ones? +// Note that this also indirectly tests that the automatic harness +// subcommand also runs the manual harnesses in the crate. + +fn oob_safe_array_access(idx: usize) { + let v = vec![1, 2, 3]; + v[idx]; +} + +fn oob_unsafe_array_access(i: usize) -> u32 { + let a: &[u32] = &[1, 2, 3]; + if a.len() == 0 { + return 0; + } + return unsafe { *a.as_ptr().add(i % a.len() + 1) }; +} + +fn integer_overflow(x: i32) -> i32 { + if x <= i32::MAX - 100 { + let add_num = |mut x: i32, z: i64| x += z as i32; + add_num(x, 1); + // overflow + add_num(x, 101); + } + x +} + +fn panic() { + if kani::any() { + panic!(); + } +} + +// Test that we can autoharness functions for unsafe functions +unsafe fn unchecked_mul(left: u8, rhs: u8) -> u8 { + left.unchecked_mul(rhs) +} + +#[kani::proof] +fn oob_safe_array_access_harness() { + oob_safe_array_access(kani::any()); +} + +#[kani::proof] +fn oob_unsafe_array_access_harness() { + oob_unsafe_array_access(kani::any()); +} + +#[kani::proof] +fn integer_overflow_harness() { + integer_overflow(kani::any()); +} + +#[kani::proof] +fn panic_harness() { + panic(); +} + +#[kani::proof] +fn unchecked_mul_harness() { + unsafe { + unchecked_mul(kani::any(), kani::any()); + } +} diff --git a/tests/script-based-pre/cargo_autoharness_include/Cargo.toml b/tests/script-based-pre/cargo_autoharness_include/Cargo.toml new file mode 100644 index 000000000000..cceaa1bb7651 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_include/Cargo.toml @@ -0,0 +1,10 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +[package] +name = "cargo_autoharness_include" +version = "0.1.0" +edition = "2024" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tests/script-based-pre/cargo_autoharness_include/config.yml b/tests/script-based-pre/cargo_autoharness_include/config.yml new file mode 100644 index 000000000000..8df48ec69865 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_include/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: include.sh +expected: include.expected diff --git a/tests/script-based-pre/cargo_autoharness_include/include.expected b/tests/script-based-pre/cargo_autoharness_include/include.expected new file mode 100644 index 000000000000..91d9eb4c399a --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_include/include.expected @@ -0,0 +1,4 @@ +Autoharness: Checking function include::simple against all possible inputs... +VERIFICATION:- SUCCESSFUL +Verification succeeded for - include::simple +Complete - 1 successfully verified functions, 0 failures, 1 total. \ No newline at end of file diff --git a/tests/script-based-pre/cargo_autoharness_include/include.sh b/tests/script-based-pre/cargo_autoharness_include/include.sh new file mode 100755 index 000000000000..1b3fcc285c88 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_include/include.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +cargo kani autoharness -Z unstable-options --include-function include diff --git a/tests/script-based-pre/cargo_autoharness_include/src/lib.rs b/tests/script-based-pre/cargo_autoharness_include/src/lib.rs new file mode 100644 index 000000000000..68b30bf7afe8 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_include/src/lib.rs @@ -0,0 +1,24 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Test that the automatic harness generation feature selects functions correctly +// when --include-function is provided. + +// Each function inside this module matches the filter. +mod include { + fn simple(x: u8, _y: u16) -> u8 { + x + } + + // Doesn't implement Arbitrary, so still should not be included. + fn generic(x: u32, _y: T) -> u32 { + x + } +} + +// These functions are eligible for autoverification, but do not match the filter. +mod excluded { + fn simple(x: u8, _y: u16) -> u8 { + x + } +} diff --git a/tests/script-based-pre/cargo_autoharness_loops_fixme/Cargo.toml b/tests/script-based-pre/cargo_autoharness_loops_fixme/Cargo.toml new file mode 100644 index 000000000000..57d8cd0f0541 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_loops_fixme/Cargo.toml @@ -0,0 +1,10 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +[package] +name = "cargo_autoharness_loops_fixme" +version = "0.1.0" +edition = "2024" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tests/script-based-pre/cargo_autoharness_loops_fixme/config.yml b/tests/script-based-pre/cargo_autoharness_loops_fixme/config.yml new file mode 100644 index 000000000000..fefb50875c7a --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_loops_fixme/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: loops.sh +expected: loops.expected diff --git a/tests/script-based-pre/cargo_autoharness_loops_fixme/loops.expected b/tests/script-based-pre/cargo_autoharness_loops_fixme/loops.expected new file mode 100644 index 000000000000..4c43332f6db0 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_loops_fixme/loops.expected @@ -0,0 +1 @@ +It's not yet clear what the ideal output is for this test, so this expected file is just a placeholder. \ No newline at end of file diff --git a/tests/script-based-pre/cargo_autoharness_loops_fixme/loops.sh b/tests/script-based-pre/cargo_autoharness_loops_fixme/loops.sh new file mode 100755 index 000000000000..cec2f2e2a10b --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_loops_fixme/loops.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +cargo kani autoharness -Z unstable-options diff --git a/tests/script-based-pre/cargo_autoharness_loops_fixme/src/lib.rs b/tests/script-based-pre/cargo_autoharness_loops_fixme/src/lib.rs new file mode 100644 index 000000000000..99ba72445b6d --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_loops_fixme/src/lib.rs @@ -0,0 +1,39 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Test that automatic harnesses terminate on functions with loops. + +// Since foo()'s arguments implement Arbitrary, we will attempt to verify it, +// and enter an infinite loop. +// Unclear what the best solution to this problem is; perhaps this is just a known limitation +// and the user needs to specify some command line flag to skip this function, +// or we can conservatively skip functions with loops that don't have loop contracts. +fn infinite_loop() { + loop {} +} + +/// Euclid's algorithm for calculating the GCD of two numbers +#[kani::requires(x != 0 && y != 0)] +#[kani::ensures(|result : &u8| *result != 0 && x % *result == 0 && y % *result == 0)] +fn gcd(mut x: u8, mut y: u8) -> u8 { + (x, y) = (if x > y { x } else { y }, if x > y { y } else { x }); + loop { + let res = x % y; + if res == 0 { + return y; + } + + x = y; + y = res; + } +} + +// Since we can specify an unwinding bound in a manual harness, +// the proof will terminate. +// Automatic harnesses, however, do not support unwinding bounds, +// so the proof does not terminate. +#[kani::proof_for_contract(gcd)] +#[kani::unwind(12)] +fn gcd_harness() { + gcd(kani::any(), kani::any()); +} From 81e9aa3063251cbe2474f18281c7292ebd41e667 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Tue, 11 Feb 2025 13:21:26 -0500 Subject: [PATCH 018/161] Upgrade toolchain to 2/10 (#3883) Upgrade toolchain to 2/10. I **highly recommend** reviewing this PR commit-by-commit. The description in each commit message links to the upstream PRs that prompted those particular changes. ## Callouts - 2/1 had a lot of formatting changes. I split the commits for that day into formatting changes and functionality changes accordingly. - 2/5 introduced a regression in our delayed UB instrumentation, so I made a new fixme test. See #3881 for details. ## Culprit PRs: https://github.com/rust-lang/rust/pull/134424 https://github.com/rust-lang/rust/pull/130514 https://github.com/rust-lang/rust/pull/135748 https://github.com/rust-lang/rust/pull/136590 https://github.com/rust-lang/rust/pull/135318 https://github.com/rust-lang/rust/pull/135265 https://github.com/rust-lang/rust/pull/128045/commits/bcb8565f301b579dee60fffe87d5a329cc69fefa https://github.com/rust-lang/rust/pull/136471 https://github.com/rust-lang/rust/pull/136645 Resolves #3863 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- cprover_bindings/src/goto_program/expr.rs | 11 +- cprover_bindings/src/goto_program/typ.rs | 8 +- cprover_bindings/src/irep/serialize.rs | 224 +++++++++--------- cprover_bindings/src/irep/to_irep.rs | 67 +++--- .../codegen_aeneas_llbc/mir_to_ullbc/mod.rs | 20 +- .../codegen_cprover_gotoc/codegen/place.rs | 2 +- .../codegen_cprover_gotoc/codegen/rvalue.rs | 18 +- .../codegen/statement.rs | 4 +- .../src/codegen_cprover_gotoc/codegen/typ.rs | 9 +- .../compiler_interface.rs | 2 +- .../codegen_cprover_gotoc/context/goto_ctx.rs | 4 +- kani-compiler/src/kani_middle/analysis.rs | 2 +- kani-compiler/src/kani_middle/metadata.rs | 20 +- .../points_to/points_to_analysis.rs | 41 ++-- .../kani_middle/points_to/points_to_graph.rs | 3 +- kani-compiler/src/kani_middle/reachability.rs | 8 +- .../check_uninit/relevant_instruction.rs | 8 +- .../src/kani_middle/transform/check_values.rs | 6 +- .../src/kani_middle/transform/contracts.rs | 8 +- .../src/kani_middle/transform/internal_mir.rs | 12 +- .../src/kani_middle/transform/mod.rs | 13 +- kani-compiler/src/session.rs | 4 +- kani-driver/src/call_cbmc.rs | 4 +- kani-driver/src/cbmc_property_renderer.rs | 208 +++++++++------- .../src/concrete_playback/test_generator.rs | 18 +- library/kani/src/models/mod.rs | 2 +- rust-toolchain.toml | 2 +- .../{expected => delayed-ub.expected} | 7 +- .../expected/uninit/delayed-ub/delayed-ub.rs | 13 - .../uninit/delayed-ub/slices_fixme.expected | 5 + .../uninit/delayed-ub/slices_fixme.rs | 21 ++ .../uninit/multiple-instrumentations.expected | 8 +- tests/expected/zst/expected | 4 +- tools/kani-cov/src/report.rs | 3 +- tools/kani-cov/src/summary.rs | 6 +- 35 files changed, 431 insertions(+), 364 deletions(-) rename tests/expected/uninit/delayed-ub/{expected => delayed-ub.expected} (88%) create mode 100644 tests/expected/uninit/delayed-ub/slices_fixme.expected create mode 100644 tests/expected/uninit/delayed-ub/slices_fixme.rs diff --git a/cprover_bindings/src/goto_program/expr.rs b/cprover_bindings/src/goto_program/expr.rs index 2a29985cb229..5805dc4f7280 100644 --- a/cprover_bindings/src/goto_program/expr.rs +++ b/cprover_bindings/src/goto_program/expr.rs @@ -285,10 +285,13 @@ pub fn arithmetic_overflow_result_type(operand_type: Type) -> Type { // give the struct the name "overflow_result_", e.g. // "overflow_result_Unsignedbv" let name: InternedString = format!("overflow_result_{operand_type:?}").into(); - Type::struct_type(name, vec![ - DatatypeComponent::field(ARITH_OVERFLOW_RESULT_FIELD, operand_type), - DatatypeComponent::field(ARITH_OVERFLOW_OVERFLOWED_FIELD, Type::bool()), - ]) + Type::struct_type( + name, + vec![ + DatatypeComponent::field(ARITH_OVERFLOW_RESULT_FIELD, operand_type), + DatatypeComponent::field(ARITH_OVERFLOW_OVERFLOWED_FIELD, Type::bool()), + ], + ) } /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/cprover_bindings/src/goto_program/typ.rs b/cprover_bindings/src/goto_program/typ.rs index b3e29d8b5c65..5a0ad76b81c6 100644 --- a/cprover_bindings/src/goto_program/typ.rs +++ b/cprover_bindings/src/goto_program/typ.rs @@ -1594,10 +1594,10 @@ mod type_tests { fn check_typedef_struct_properties() { // Create a struct with a random field. let struct_name: InternedString = "MyStruct".into(); - let struct_type = Type::struct_type(struct_name, vec![DatatypeComponent::Field { - name: "field".into(), - typ: Double, - }]); + let struct_type = Type::struct_type( + struct_name, + vec![DatatypeComponent::Field { name: "field".into(), typ: Double }], + ); // Insert a field to the sym table to represent the struct field. let mut sym_table = SymbolTable::new(machine_model_test_stub()); sym_table.ensure(struct_type.type_name().unwrap(), |_, name| { diff --git a/cprover_bindings/src/irep/serialize.rs b/cprover_bindings/src/irep/serialize.rs index 6985d4a60dee..51a2b06fd874 100644 --- a/cprover_bindings/src/irep/serialize.rs +++ b/cprover_bindings/src/irep/serialize.rs @@ -149,12 +149,10 @@ mod test { #[test] fn serialize_irep() { let irep = Irep::empty(); - assert_ser_tokens(&irep, &[ - Token::Map { len: None }, - Token::String("id"), - Token::String("empty"), - Token::MapEnd, - ]); + assert_ser_tokens( + &irep, + &[Token::Map { len: None }, Token::String("id"), Token::String("empty"), Token::MapEnd], + ); } #[test] @@ -189,77 +187,80 @@ mod test { is_weak: false, }; sym_table.insert(symbol); - assert_ser_tokens(&sym_table, &[ - Token::Map { len: None }, - Token::String("symbolTable"), - Token::Map { len: Some(1) }, - Token::String("my_name"), - // symbol start - Token::Map { len: None }, - // type irep - Token::String("type"), - Token::Map { len: None }, - Token::String("id"), - Token::String("empty"), - Token::MapEnd, - // value irep - Token::String("value"), - Token::Map { len: None }, - Token::String("id"), - Token::String("empty"), - Token::MapEnd, - // value locaton - Token::String("location"), - Token::Map { len: None }, - Token::String("id"), - Token::String("empty"), - Token::MapEnd, - Token::String("name"), - Token::String("my_name"), - Token::String("module"), - Token::String(""), - Token::String("baseName"), - Token::String(""), - Token::String("prettyName"), - Token::String(""), - Token::String("mode"), - Token::String(""), - Token::String("isType"), - Token::Bool(false), - Token::String("isMacro"), - Token::Bool(false), - Token::String("isExported"), - Token::Bool(false), - Token::String("isInput"), - Token::Bool(false), - Token::String("isOutput"), - Token::Bool(false), - Token::String("isStateVar"), - Token::Bool(false), - Token::String("isProperty"), - Token::Bool(false), - Token::String("isStaticLifetime"), - Token::Bool(false), - Token::String("isThreadLocal"), - Token::Bool(false), - Token::String("isLvalue"), - Token::Bool(false), - Token::String("isFileLocal"), - Token::Bool(false), - Token::String("isExtern"), - Token::Bool(false), - Token::String("isVolatile"), - Token::Bool(false), - Token::String("isParameter"), - Token::Bool(false), - Token::String("isAuxiliary"), - Token::Bool(false), - Token::String("isWeak"), - Token::Bool(false), - Token::MapEnd, - Token::MapEnd, - Token::MapEnd, - ]); + assert_ser_tokens( + &sym_table, + &[ + Token::Map { len: None }, + Token::String("symbolTable"), + Token::Map { len: Some(1) }, + Token::String("my_name"), + // symbol start + Token::Map { len: None }, + // type irep + Token::String("type"), + Token::Map { len: None }, + Token::String("id"), + Token::String("empty"), + Token::MapEnd, + // value irep + Token::String("value"), + Token::Map { len: None }, + Token::String("id"), + Token::String("empty"), + Token::MapEnd, + // value locaton + Token::String("location"), + Token::Map { len: None }, + Token::String("id"), + Token::String("empty"), + Token::MapEnd, + Token::String("name"), + Token::String("my_name"), + Token::String("module"), + Token::String(""), + Token::String("baseName"), + Token::String(""), + Token::String("prettyName"), + Token::String(""), + Token::String("mode"), + Token::String(""), + Token::String("isType"), + Token::Bool(false), + Token::String("isMacro"), + Token::Bool(false), + Token::String("isExported"), + Token::Bool(false), + Token::String("isInput"), + Token::Bool(false), + Token::String("isOutput"), + Token::Bool(false), + Token::String("isStateVar"), + Token::Bool(false), + Token::String("isProperty"), + Token::Bool(false), + Token::String("isStaticLifetime"), + Token::Bool(false), + Token::String("isThreadLocal"), + Token::Bool(false), + Token::String("isLvalue"), + Token::Bool(false), + Token::String("isFileLocal"), + Token::Bool(false), + Token::String("isExtern"), + Token::Bool(false), + Token::String("isVolatile"), + Token::Bool(false), + Token::String("isParameter"), + Token::Bool(false), + Token::String("isAuxiliary"), + Token::Bool(false), + Token::String("isWeak"), + Token::Bool(false), + Token::MapEnd, + Token::MapEnd, + Token::MapEnd, + ], + ); } #[test] @@ -268,38 +269,41 @@ mod test { let one_irep = Irep::one(); let sub_irep = Irep::just_sub(vec![empty_irep.clone(), one_irep]); let top_irep = Irep::just_sub(vec![sub_irep, empty_irep]); - assert_ser_tokens(&top_irep, &[ - // top_irep - Token::Map { len: None }, - Token::String("id"), - Token::String(""), - Token::String("sub"), - Token::Seq { len: Some(2) }, - // sub_irep - Token::Map { len: None }, - Token::String("id"), - Token::String(""), - Token::String("sub"), - Token::Seq { len: Some(2) }, - // empty_irep - Token::Map { len: None }, - Token::String("id"), - Token::String("empty"), - Token::MapEnd, - // one_irep - Token::Map { len: None }, - Token::String("id"), - Token::String("1"), - Token::MapEnd, - Token::SeqEnd, - Token::MapEnd, - // empty_irep - Token::Map { len: None }, - Token::String("id"), - Token::String("empty"), - Token::MapEnd, - Token::SeqEnd, - Token::MapEnd, - ]); + assert_ser_tokens( + &top_irep, + &[ + // top_irep + Token::Map { len: None }, + Token::String("id"), + Token::String(""), + Token::String("sub"), + Token::Seq { len: Some(2) }, + // sub_irep + Token::Map { len: None }, + Token::String("id"), + Token::String(""), + Token::String("sub"), + Token::Seq { len: Some(2) }, + // empty_irep + Token::Map { len: None }, + Token::String("id"), + Token::String("empty"), + Token::MapEnd, + // one_irep + Token::Map { len: None }, + Token::String("id"), + Token::String("1"), + Token::MapEnd, + Token::SeqEnd, + Token::MapEnd, + // empty_irep + Token::Map { len: None }, + Token::String("id"), + Token::String("empty"), + Token::MapEnd, + Token::SeqEnd, + Token::MapEnd, + ], + ); } } diff --git a/cprover_bindings/src/irep/to_irep.rs b/cprover_bindings/src/irep/to_irep.rs index e007652a04a9..7fd364e5d725 100644 --- a/cprover_bindings/src/irep/to_irep.rs +++ b/cprover_bindings/src/irep/to_irep.rs @@ -273,12 +273,10 @@ impl ToIrep for ExprValue { )], } } - ExprValue::FunctionCall { function, arguments } => { - side_effect_irep(IrepId::FunctionCall, vec![ - function.to_irep(mm), - arguments_irep(arguments.iter(), mm), - ]) - } + ExprValue::FunctionCall { function, arguments } => side_effect_irep( + IrepId::FunctionCall, + vec![function.to_irep(mm), arguments_irep(arguments.iter(), mm)], + ), ExprValue::If { c, t, e } => Irep { id: IrepId::If, sub: vec![c.to_irep(mm), t.to_irep(mm), e.to_irep(mm)], @@ -320,11 +318,10 @@ impl ToIrep for ExprValue { named_sub: linear_map![], }, ExprValue::SelfOp { op, e } => side_effect_irep(op.to_irep_id(), vec![e.to_irep(mm)]), - ExprValue::StatementExpression { statements: ops, location: loc } => { - side_effect_irep(IrepId::StatementExpression, vec![ - Stmt::block(ops.to_vec(), *loc).to_irep(mm), - ]) - } + ExprValue::StatementExpression { statements: ops, location: loc } => side_effect_irep( + IrepId::StatementExpression, + vec![Stmt::block(ops.to_vec(), *loc).to_irep(mm)], + ), ExprValue::StringConstant { s } => Irep { id: IrepId::StringConstant, sub: vec![], @@ -491,10 +488,10 @@ impl ToIrep for StmtBody { StmtBody::Dead(symbol) => code_irep(IrepId::Dead, vec![symbol.to_irep(mm)]), StmtBody::Decl { lhs, value } => { if value.is_some() { - code_irep(IrepId::Decl, vec![ - lhs.to_irep(mm), - value.as_ref().unwrap().to_irep(mm), - ]) + code_irep( + IrepId::Decl, + vec![lhs.to_irep(mm), value.as_ref().unwrap().to_irep(mm)], + ) } else { code_irep(IrepId::Decl, vec![lhs.to_irep(mm)]) } @@ -507,19 +504,18 @@ impl ToIrep for StmtBody { .with_comment("deinit") } StmtBody::Expression(e) => code_irep(IrepId::Expression, vec![e.to_irep(mm)]), - StmtBody::For { init, cond, update, body } => code_irep(IrepId::For, vec![ - init.to_irep(mm), - cond.to_irep(mm), - update.to_irep(mm), - body.to_irep(mm), - ]), - StmtBody::FunctionCall { lhs, function, arguments } => { - code_irep(IrepId::FunctionCall, vec![ + StmtBody::For { init, cond, update, body } => code_irep( + IrepId::For, + vec![init.to_irep(mm), cond.to_irep(mm), update.to_irep(mm), body.to_irep(mm)], + ), + StmtBody::FunctionCall { lhs, function, arguments } => code_irep( + IrepId::FunctionCall, + vec![ lhs.as_ref().map_or(Irep::nil(), |x| x.to_irep(mm)), function.to_irep(mm), arguments_irep(arguments.iter(), mm), - ]) - } + ], + ), StmtBody::Goto { dest, loop_invariants } => { let stmt_goto = code_irep(IrepId::Goto, vec![]) .with_named_sub(IrepId::Destination, Irep::just_string_id(dest.to_string())); @@ -532,11 +528,14 @@ impl ToIrep for StmtBody { stmt_goto } } - StmtBody::Ifthenelse { i, t, e } => code_irep(IrepId::Ifthenelse, vec![ - i.to_irep(mm), - t.to_irep(mm), - e.as_ref().map_or(Irep::nil(), |x| x.to_irep(mm)), - ]), + StmtBody::Ifthenelse { i, t, e } => code_irep( + IrepId::Ifthenelse, + vec![ + i.to_irep(mm), + t.to_irep(mm), + e.as_ref().map_or(Irep::nil(), |x| x.to_irep(mm)), + ], + ), StmtBody::Label { label, body } => code_irep(IrepId::Label, vec![body.to_irep(mm)]) .with_named_sub(IrepId::Label, Irep::just_string_id(label.to_string())), StmtBody::Return(e) => { @@ -548,10 +547,10 @@ impl ToIrep for StmtBody { if default.is_some() { switch_arms.push(switch_default_irep(default.as_ref().unwrap(), mm)); } - code_irep(IrepId::Switch, vec![ - control.to_irep(mm), - code_irep(IrepId::Block, switch_arms), - ]) + code_irep( + IrepId::Switch, + vec![control.to_irep(mm), code_irep(IrepId::Block, switch_arms)], + ) } StmtBody::While { cond, body } => { code_irep(IrepId::While, vec![cond.to_irep(mm), body.to_irep(mm)]) diff --git a/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs b/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs index 4780b4023a02..546a3aa4f127 100644 --- a/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs +++ b/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs @@ -1416,10 +1416,13 @@ impl<'a, 'tcx> Context<'a, 'tcx> { } RigidTy::RawPtr(ty, mutability) => { let c_ty = self.translate_ty(ty); - CharonTy::new(CharonTyKind::RawPtr(c_ty, match mutability { - Mutability::Mut => CharonRefKind::Mut, - Mutability::Not => CharonRefKind::Shared, - })) + CharonTy::new(CharonTyKind::RawPtr( + c_ty, + match mutability { + Mutability::Mut => CharonRefKind::Mut, + Mutability::Not => CharonRefKind::Shared, + }, + )) } RigidTy::FnPtr(polyfunsig) => { let value = polyfunsig.value; @@ -1539,9 +1542,12 @@ impl<'a, 'tcx> Context<'a, 'tcx> { args: args.iter().map(|arg| self.translate_operand(arg)).collect(), dest: self.translate_place(destination), }; - (Some(CharonRawStatement::Call(call)), CharonRawTerminator::Goto { - target: CharonBlockId::from_usize(target.unwrap()), - }) + ( + Some(CharonRawStatement::Call(call)), + CharonRawTerminator::Goto { + target: CharonBlockId::from_usize(target.unwrap()), + }, + ) } TerminatorKind::Assert { cond, expected, msg: _, target, .. } => ( Some(CharonRawStatement::Assert(CharonAssert { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs index 8cabd10c87ad..01796a832b78 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs @@ -12,9 +12,9 @@ use crate::codegen_cprover_gotoc::codegen::typ::std_pointee_type; use crate::codegen_cprover_gotoc::utils::{dynamic_fat_ptr, slice_fat_ptr}; use crate::unwrap_or_return_codegen_unimplemented; use cbmc::goto_program::{Expr, ExprValue, Location, Stmt, Type}; +use rustc_abi::{TagEncoding, Variants}; use rustc_middle::ty::layout::LayoutOf; use rustc_smir::rustc_internal; -use rustc_target::abi::{TagEncoding, Variants}; use stable_mir::mir::{FieldIdx, Local, Mutability, Place, ProjectionElem}; use stable_mir::ty::{RigidTy, Ty, TyKind, VariantDef, VariantIdx}; use tracing::{debug, trace, warn}; diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index 7f5568ccf152..860619668c9d 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -18,9 +18,9 @@ use cbmc::goto_program::{ }; use cbmc::{InternString, InternedString, btree_string_map}; use num::bigint::BigInt; +use rustc_abi::{FieldsShape, TagEncoding, Variants}; use rustc_middle::ty::{TyCtxt, TypingEnv, VtblEntry}; use rustc_smir::rustc_internal; -use rustc_target::abi::{FieldsShape, TagEncoding, Variants}; use stable_mir::abi::{Primitive, Scalar, ValueAbi}; use stable_mir::mir::mono::Instance; use stable_mir::mir::{ @@ -851,7 +851,7 @@ impl GotocCtx<'_> { .bytes(), Type::size_t(), ), - NullOp::UbChecks => Expr::c_false(), + NullOp::ContractChecks | NullOp::UbChecks => Expr::c_false(), } } Rvalue::ShallowInitBox(ref operand, content_ty) => { @@ -964,10 +964,10 @@ impl GotocCtx<'_> { pub fn codegen_discriminant_field(&self, place: Expr, ty: Ty) -> Expr { let layout = self.layout_of_stable(ty); assert!( - matches!(&layout.variants, Variants::Multiple { - tag_encoding: TagEncoding::Direct, - .. - }), + matches!( + &layout.variants, + Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } + ), "discriminant field (`case`) only exists for multiple variants and direct encoding" ); let expr = if ty.kind().is_coroutine() { @@ -1505,8 +1505,10 @@ impl GotocCtx<'_> { |ctx, var| { // Build the vtable, using Rust's vtable_entries to determine field order let vtable_entries = if let Some(principal) = trait_type.kind().trait_principal() { - let trait_ref_binder = principal.with_self_ty(src_mir_type); - ctx.tcx.vtable_entries(rustc_internal::internal(ctx.tcx, trait_ref_binder)) + let trait_ref = + rustc_internal::internal(ctx.tcx, principal.with_self_ty(src_mir_type)); + let trait_ref = ctx.tcx.instantiate_bound_regions_with_erased(trait_ref); + ctx.tcx.vtable_entries(trait_ref) } else { TyCtxt::COMMON_VTABLE_ENTRIES }; diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 90635a71f2e8..2d876899f235 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -7,10 +7,10 @@ use crate::codegen_cprover_gotoc::codegen::function::rustc_smir::region_from_cov use crate::codegen_cprover_gotoc::{GotocCtx, VtableCtx}; use crate::unwrap_or_return_codegen_unimplemented_stmt; use cbmc::goto_program::{Expr, Location, Stmt, Type}; +use rustc_abi::{FieldsShape, Primitive, TagEncoding, Variants}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{List, TypingEnv}; use rustc_smir::rustc_internal; -use rustc_target::abi::{FieldsShape, Primitive, TagEncoding, Variants}; use stable_mir::abi::{ArgAbi, FnAbi, PassMode}; use stable_mir::mir::mono::{Instance, InstanceKind}; use stable_mir::mir::{ @@ -552,7 +552,7 @@ impl GotocCtx<'_> { let ty = self.operand_ty_stable(op); if ty.kind().is_bool() { Some(self.codegen_operand_stable(op).cast_to(Type::c_bool())) - } else if arg_abi.map_or(true, |abi| abi.mode != PassMode::Ignore) { + } else if arg_abi.is_none_or(|abi| abi.mode != PassMode::Ignore) { Some(self.codegen_operand_stable(op)) } else { None diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index 18c121694223..a22528d55407 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -4,6 +4,10 @@ use crate::codegen_cprover_gotoc::GotocCtx; use cbmc::goto_program::{DatatypeComponent, Expr, Location, Parameter, Symbol, SymbolTable, Type}; use cbmc::utils::aggr_tag; use cbmc::{InternString, InternedString}; +use rustc_abi::{ + BackendRepr::Vector, FieldIdx, FieldsShape, Float, Integer, LayoutData, Primitive, Size, + TagEncoding, TyAndLayout, VariantIdx, Variants, +}; use rustc_ast::ast::Mutability; use rustc_index::IndexVec; use rustc_middle::ty::GenericArgsRef; @@ -17,10 +21,6 @@ use rustc_middle::ty::{ use rustc_middle::ty::{List, TypeFoldable}; use rustc_smir::rustc_internal; use rustc_span::def_id::DefId; -use rustc_target::abi::{ - BackendRepr::Vector, FieldIdx, FieldsShape, Float, Integer, LayoutData, Primitive, Size, - TagEncoding, TyAndLayout, VariantIdx, Variants, -}; use stable_mir::abi::{ArgAbi, FnAbi, PassMode}; use stable_mir::mir::Body; use stable_mir::mir::mono::Instance as InstanceStable; @@ -371,6 +371,7 @@ impl<'tcx> GotocCtx<'tcx> { // The virtual methods on the trait ref. Some auto traits have no methods. if let Some(principal) = binder.principal() { let poly = principal.with_self_ty(self.tcx, t); + let poly = self.tcx.instantiate_bound_regions_with_erased(poly); let poly = self.tcx.erase_regions(poly); let mut flds = self .tcx diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index 80137db06a6e..d6f353fe995d 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -23,6 +23,7 @@ use cbmc::{InternedString, MachineModel}; use kani_metadata::artifact::convert_type; use kani_metadata::{ArtifactType, HarnessMetadata, KaniMetadata, UnsupportedFeature}; use kani_metadata::{AssignsContract, CompilerArtifactStub}; +use rustc_abi::Endian; use rustc_codegen_ssa::back::archive::{ ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, }; @@ -40,7 +41,6 @@ use rustc_session::Session; use rustc_session::config::{CrateType, OutputFilenames, OutputType}; use rustc_session::output::out_filename; use rustc_smir::rustc_internal; -use rustc_target::abi::Endian; use rustc_target::spec::PanicStrategy; use stable_mir::mir::mono::{Instance, MonoItem}; use stable_mir::{CrateDef, DefId}; diff --git a/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs b/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs index 0e5e72af70b6..79410bd3373e 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs @@ -26,6 +26,7 @@ use cbmc::goto_program::{ }; use cbmc::utils::aggr_tag; use cbmc::{InternedString, MachineModel}; +use rustc_abi::{HasDataLayout, TargetDataLayout}; use rustc_data_structures::fx::FxHashMap; use rustc_middle::span_bug; use rustc_middle::ty::layout::{ @@ -35,8 +36,7 @@ use rustc_middle::ty::layout::{ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; use rustc_span::source_map::respan; -use rustc_target::abi::call::FnAbi; -use rustc_target::abi::{HasDataLayout, TargetDataLayout}; +use rustc_target::callconv::FnAbi; use stable_mir::mir::Body; use stable_mir::mir::mono::Instance; use stable_mir::ty::Allocation; diff --git a/kani-compiler/src/kani_middle/analysis.rs b/kani-compiler/src/kani_middle/analysis.rs index b232db8feecb..f0828dace441 100644 --- a/kani-compiler/src/kani_middle/analysis.rs +++ b/kani-compiler/src/kani_middle/analysis.rs @@ -149,7 +149,7 @@ impl From<&Terminator> for Key { TerminatorKind::Drop { .. } => Key("Drop"), TerminatorKind::Goto { .. } => Key("Goto"), TerminatorKind::InlineAsm { .. } => Key("InlineAsm"), - TerminatorKind::Resume { .. } => Key("Resume"), + TerminatorKind::Resume => Key("Resume"), TerminatorKind::Return => Key("Return"), TerminatorKind::SwitchInt { .. } => Key("SwitchInt"), TerminatorKind::Unreachable => Key("Unreachable"), diff --git a/kani-compiler/src/kani_middle/metadata.rs b/kani-compiler/src/kani_middle/metadata.rs index 449548f27045..f8de975802e1 100644 --- a/kani-compiler/src/kani_middle/metadata.rs +++ b/kani-compiler/src/kani_middle/metadata.rs @@ -59,11 +59,8 @@ pub fn gen_contracts_metadata(tcx: TyCtxt) -> Vec { let attributes = KaniAttributes::for_def_id(tcx, item.def_id()); if attributes.has_contract() { - fn_to_data.insert(item.def_id(), ContractedFunction { - function, - file, - harnesses: vec![], - }); + fn_to_data + .insert(item.def_id(), ContractedFunction { function, file, harnesses: vec![] }); } else if let Some((target_name, internal_def_id, _)) = attributes.interpret_for_contract_attribute() { @@ -73,11 +70,14 @@ pub fn gen_contracts_metadata(tcx: TyCtxt) -> Vec { if let Some(cf) = fn_to_data.get_mut(&target_def_id) { cf.harnesses.push(function); } else { - fn_to_data.insert(target_def_id, ContractedFunction { - function: target_name.to_string(), - file, - harnesses: vec![function], - }); + fn_to_data.insert( + target_def_id, + ContractedFunction { + function: target_name.to_string(), + file, + harnesses: vec![function], + }, + ); } } } diff --git a/kani-compiler/src/kani_middle/points_to/points_to_analysis.rs b/kani-compiler/src/kani_middle/points_to/points_to_analysis.rs index 3d80397530b3..a89baf963e7f 100644 --- a/kani-compiler/src/kani_middle/points_to/points_to_analysis.rs +++ b/kani-compiler/src/kani_middle/points_to/points_to_analysis.rs @@ -463,19 +463,22 @@ impl<'tcx> PointsToAnalysis<'_, 'tcx> { // Sanity check. The first argument is the closure itself and the second argument is the tupled arguments from the caller. assert!(args.len() == 2); // First, connect all upvars. - let lvalue_set = HashSet::from([MemLoc::new_stack_allocation(instance, Place { - local: 1usize.into(), - projection: List::empty(), - })]); + let lvalue_set = HashSet::from([MemLoc::new_stack_allocation( + instance, + Place { local: 1usize.into(), projection: List::empty() }, + )]); let rvalue_set = self.successors_for_operand(state, args[0].node.clone()); initial_graph.extend(&lvalue_set, &rvalue_set); // Then, connect the argument tuple to each of the spread arguments. let spread_arg_operand = args[1].node.clone(); for i in 0..new_body.arg_count { - let lvalue_set = HashSet::from([MemLoc::new_stack_allocation(instance, Place { - local: (i + 1).into(), // Since arguments in the callee are starting with 1, account for that. - projection: List::empty(), - })]); + let lvalue_set = HashSet::from([MemLoc::new_stack_allocation( + instance, + Place { + local: (i + 1).into(), // Since arguments in the callee are starting with 1, account for that. + projection: List::empty(), + }, + )]); // This conservatively assumes all arguments alias to all parameters. let rvalue_set = self.successors_for_operand(state, spread_arg_operand.clone()); initial_graph.extend(&lvalue_set, &rvalue_set); @@ -483,10 +486,13 @@ impl<'tcx> PointsToAnalysis<'_, 'tcx> { } else { // Otherwise, simply connect all arguments to parameters. for (i, arg) in args.iter().enumerate() { - let lvalue_set = HashSet::from([MemLoc::new_stack_allocation(instance, Place { - local: (i + 1).into(), // Since arguments in the callee are starting with 1, account for that. - projection: List::empty(), - })]); + let lvalue_set = HashSet::from([MemLoc::new_stack_allocation( + instance, + Place { + local: (i + 1).into(), // Since arguments in the callee are starting with 1, account for that. + projection: List::empty(), + }, + )]); let rvalue_set = self.successors_for_operand(state, arg.node.clone()); initial_graph.extend(&lvalue_set, &rvalue_set); } @@ -500,10 +506,10 @@ impl<'tcx> PointsToAnalysis<'_, 'tcx> { // Connect the return value to the return destination. let lvalue_set = state.resolve_place(*destination, self.instance); - let rvalue_set = HashSet::from([MemLoc::new_stack_allocation(instance, Place { - local: 0usize.into(), - projection: List::empty(), - })]); + let rvalue_set = HashSet::from([MemLoc::new_stack_allocation( + instance, + Place { local: 0usize.into(), projection: List::empty() }, + )]); state.extend(&lvalue_set, &state.successors(&rvalue_set)); } @@ -519,7 +525,8 @@ impl<'tcx> PointsToAnalysis<'_, 'tcx> { Rvalue::Use(operand) | Rvalue::ShallowInitBox(operand, _) | Rvalue::Cast(_, operand, _) - | Rvalue::Repeat(operand, ..) => self.successors_for_operand(state, operand), + | Rvalue::Repeat(operand, ..) + | Rvalue::WrapUnsafeBinder(operand, _) => self.successors_for_operand(state, operand), Rvalue::Ref(_, _, ref_place) | Rvalue::RawPtr(_, ref_place) => { // Here, a reference to a place is created, which leaves the place // unchanged. diff --git a/kani-compiler/src/kani_middle/points_to/points_to_graph.rs b/kani-compiler/src/kani_middle/points_to/points_to_graph.rs index 0b52a84eaa59..6e473a84eecc 100644 --- a/kani-compiler/src/kani_middle/points_to/points_to_graph.rs +++ b/kani-compiler/src/kani_middle/points_to/points_to_graph.rs @@ -163,7 +163,8 @@ impl<'tcx> PointsToGraph<'tcx> { | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(..) | ProjectionElem::OpaqueCast(..) - | ProjectionElem::Subtype(..) => { + | ProjectionElem::Subtype(..) + | ProjectionElem::UnwrapUnsafeBinder(..) => { /* There operations are no-ops w.r.t aliasing since we are tracking it on per-object basis. */ } } diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 2267af56a684..fdc81e177d1f 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -256,11 +256,11 @@ impl MonoItemsFnCollector<'_, '_> { // A trait object type can have multiple trait bounds but up to one non-auto-trait // bound. This non-auto-trait, named principal, is the only one that can have methods. // https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits - let poly_trait_ref = principal.with_self_ty(concrete_ty); + let trait_ref = rustc_internal::internal(self.tcx, principal.with_self_ty(concrete_ty)); + let trait_ref = self.tcx.instantiate_bound_regions_with_erased(trait_ref); // Walk all methods of the trait, including those of its supertraits - let entries = - self.tcx.vtable_entries(rustc_internal::internal(self.tcx, &poly_trait_ref)); + let entries = self.tcx.vtable_entries(trait_ref); let methods = entries.iter().filter_map(|entry| match entry { VtblEntry::MetadataAlign | VtblEntry::MetadataDropInPlace @@ -458,7 +458,7 @@ impl MirVisitor for MonoItemsFnCollector<'_, '_> { // We don't support inline assembly. This shall be replaced by an unsupported // construct during codegen. } - TerminatorKind::Abort { .. } | TerminatorKind::Assert { .. } => { + TerminatorKind::Abort | TerminatorKind::Assert { .. } => { // We generate code for this without invoking any lang item. } TerminatorKind::Goto { .. } diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/relevant_instruction.rs b/kani-compiler/src/kani_middle/transform/check_uninit/relevant_instruction.rs index 9ad23071df4a..7469508e2c70 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/relevant_instruction.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/relevant_instruction.rs @@ -6,7 +6,7 @@ use crate::kani_middle::transform::body::{InsertPosition, MutableBody, SourceInstruction}; use stable_mir::{ - mir::{FieldIdx, Mutability, Operand, Place, Rvalue, Statement, StatementKind}, + mir::{FieldIdx, Mutability, Operand, Place, RawPtrKind, Rvalue, Statement, StatementKind}, ty::{RigidTy, Ty}, }; use strum_macros::AsRefStr; @@ -123,7 +123,7 @@ impl MemoryInitOp { Operand::Copy(place) | Operand::Move(place) => place, Operand::Constant(_) => unreachable!(), }; - let rvalue = Rvalue::AddressOf(Mutability::Not, place.clone()); + let rvalue = Rvalue::AddressOf(RawPtrKind::Const, place.clone()); rvalue.ty(body.locals()).unwrap() } MemoryInitOp::Unsupported { .. } | MemoryInitOp::TriviallyUnsafe { .. } => { @@ -147,7 +147,7 @@ impl MemoryInitOp { MemoryInitOp::AssignUnion { lvalue, .. } => { // It does not matter which operand to return for layout generation, since both of // them have the same pointee type. - let address_of = Rvalue::AddressOf(Mutability::Not, lvalue.clone()); + let address_of = Rvalue::AddressOf(RawPtrKind::Const, lvalue.clone()); address_of.ty(body.locals()).unwrap() } } @@ -271,7 +271,7 @@ fn mk_ref( Operand::Copy(place) | Operand::Move(place) => place, Operand::Constant(_) => unreachable!(), }; - let rvalue = Rvalue::AddressOf(Mutability::Not, place.clone()); + let rvalue = Rvalue::AddressOf(RawPtrKind::Const, place.clone()); let ret_ty = rvalue.ty(body.locals()).unwrap(); let result = body.new_local(ret_ty, span, Mutability::Not); let stmt = Statement { kind: StatementKind::Assign(Place::from(result), rvalue), span }; diff --git a/kani-compiler/src/kani_middle/transform/check_values.rs b/kani-compiler/src/kani_middle/transform/check_values.rs index eba23503dacb..e2c8a153c9a1 100644 --- a/kani-compiler/src/kani_middle/transform/check_values.rs +++ b/kani-compiler/src/kani_middle/transform/check_values.rs @@ -28,8 +28,8 @@ use stable_mir::mir::mono::Instance; use stable_mir::mir::visit::{Location, PlaceContext, PlaceRef}; use stable_mir::mir::{ AggregateKind, BasicBlockIdx, BinOp, Body, CastKind, ConstOperand, FieldIdx, Local, LocalDecl, - MirVisitor, Mutability, NonDivergingIntrinsic, Operand, Place, ProjectionElem, Rvalue, - Statement, StatementKind, Terminator, TerminatorKind, + MirVisitor, Mutability, NonDivergingIntrinsic, Operand, Place, ProjectionElem, RawPtrKind, + Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; use stable_mir::target::{MachineInfo, MachineSize}; use stable_mir::ty::{AdtKind, IndexedVal, MirConst, RigidTy, Span, Ty, TyKind, UintTy}; @@ -86,7 +86,7 @@ impl ValidValuePass { match operation { SourceOp::BytesValidity { ranges, target_ty, rvalue } => { let value = body.insert_assignment(rvalue, &mut source, InsertPosition::Before); - let rvalue_ptr = Rvalue::AddressOf(Mutability::Not, Place::from(value)); + let rvalue_ptr = Rvalue::AddressOf(RawPtrKind::Const, Place::from(value)); for range in ranges { let result = build_limits(body, &range, rvalue_ptr.clone(), &mut source); let msg = diff --git a/kani-compiler/src/kani_middle/transform/contracts.rs b/kani-compiler/src/kani_middle/transform/contracts.rs index bd613b3d6663..df5e4209e900 100644 --- a/kani-compiler/src/kani_middle/transform/contracts.rs +++ b/kani-compiler/src/kani_middle/transform/contracts.rs @@ -456,10 +456,10 @@ impl FunctionWithContractPass { &mut mode_call, InsertPosition::Before, ); - new_body.replace_terminator(&mode_call, Terminator { - kind: TerminatorKind::Goto { target }, - span, - }); + new_body.replace_terminator( + &mode_call, + Terminator { kind: TerminatorKind::Goto { target }, span }, + ); new_body.into() } diff --git a/kani-compiler/src/kani_middle/transform/internal_mir.rs b/kani-compiler/src/kani_middle/transform/internal_mir.rs index fd499db7c3c6..090c74311fa4 100644 --- a/kani-compiler/src/kani_middle/transform/internal_mir.rs +++ b/kani-compiler/src/kani_middle/transform/internal_mir.rs @@ -42,7 +42,7 @@ impl RustcInternalMir for AggregateKind { internal(tcx, generic_args), maybe_user_type_annotation_index .map(rustc_middle::ty::UserTypeAnnotationIndex::from_usize), - maybe_field_idx.map(rustc_target::abi::FieldIdx::from_usize), + maybe_field_idx.map(rustc_abi::FieldIdx::from_usize), ), AggregateKind::Closure(closure_def, generic_args) => { rustc_middle::mir::AggregateKind::Closure( @@ -207,7 +207,7 @@ impl RustcInternalMir for NullOp { .map(|(variant_idx, field_idx)| { ( internal(tcx, variant_idx), - rustc_target::abi::FieldIdx::from_usize(*field_idx), + rustc_abi::FieldIdx::from_usize(*field_idx), ) }) .collect::>() @@ -215,6 +215,7 @@ impl RustcInternalMir for NullOp { ), ), NullOp::UbChecks => rustc_middle::mir::NullOp::UbChecks, + NullOp::ContractChecks => rustc_middle::mir::NullOp::ContractChecks, } } } @@ -224,8 +225,8 @@ impl RustcInternalMir for Rvalue { fn internal_mir<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { match self { - Rvalue::AddressOf(mutability, place) => { - rustc_middle::mir::Rvalue::RawPtr(internal(tcx, mutability), internal(tcx, place)) + Rvalue::AddressOf(raw_ptr_kind, place) => { + rustc_middle::mir::Rvalue::RawPtr(internal(tcx, raw_ptr_kind), internal(tcx, place)) } Rvalue::Aggregate(aggregate_kind, operands) => rustc_middle::mir::Rvalue::Aggregate( Box::new(aggregate_kind.internal_mir(tcx)), @@ -545,6 +546,9 @@ impl RustcInternalMir for AssertMessage { found: found.internal_mir(tcx), } } + AssertMessage::NullPointerDereference => { + rustc_middle::mir::AssertMessage::NullPointerDereference + } } } } diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index 49e36461081c..d1eac474b47a 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -87,11 +87,14 @@ impl BodyTransformation { // would also make sense to check that the values are initialized before checking their // validity. In the future, it would be nice to have a mechanism to skip automatically // generated code for future instrumentation passes. - transformer.add_pass(queries, UninitPass { - // Since this uses demonic non-determinism under the hood, should not assume the assertion. - check_type: CheckType::new_assert(queries), - mem_init_fn_cache: queries.kani_functions().clone(), - }); + transformer.add_pass( + queries, + UninitPass { + // Since this uses demonic non-determinism under the hood, should not assume the assertion. + check_type: CheckType::new_assert(queries), + mem_init_fn_cache: queries.kani_functions().clone(), + }, + ); transformer.add_pass(queries, IntrinsicGeneratorPass::new(check_type, &queries)); transformer.add_pass(queries, LoopContractPass::new(tcx, queries, &unit)); transformer.add_pass(queries, RustcIntrinsicsPass::new(&queries)); diff --git a/kani-compiler/src/session.rs b/kani-compiler/src/session.rs index 5b4990790a10..8efabb2c9517 100644 --- a/kani-compiler/src/session.rs +++ b/kani-compiler/src/session.rs @@ -4,7 +4,6 @@ //! Module used to configure a compiler session. use crate::args::Arguments; -use rustc_data_structures::sync::Lrc; use rustc_errors::{ ColorConfig, DiagInner, emitter::Emitter, emitter::HumanReadableErrorType, fallback_fluent_bundle, json::JsonEmitter, registry::Registry as ErrorRegistry, @@ -16,6 +15,7 @@ use rustc_span::source_map::SourceMap; use std::io; use std::io::IsTerminal; use std::panic; +use std::sync::Arc; use std::sync::LazyLock; use tracing_subscriber::{EnvFilter, Registry, layer::SubscriberExt}; use tracing_tree::HierarchicalLayer; @@ -57,7 +57,7 @@ static JSON_PANIC_HOOK: LazyLock) + Sync + let mut emitter = JsonEmitter::new( Box::new(io::BufWriter::new(io::stderr())), #[allow(clippy::arc_with_non_send_sync)] - Some(Lrc::new(SourceMap::new(FilePathMapping::empty()))), + Some(Arc::new(SourceMap::new(FilePathMapping::empty()))), fallback_bundle, false, HumanReadableErrorType::Default, diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index b6050d915666..b26b84ca2632 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -309,9 +309,9 @@ impl VerificationResult { /// /// NOTE: We actually ignore the CBMC exit status, in favor of two checks: /// 1. Examining the actual results of CBMC properties. - /// (CBMC will regularly report "failure" but that's just our cover checks.) + /// (CBMC will regularly report "failure" but that's just our cover checks.) /// 2. Positively checking for the presence of results. - /// (Do not mistake lack of results for success: report it as failure.) + /// (Do not mistake lack of results for success: report it as failure.) fn from( output: VerificationOutput, should_panic: bool, diff --git a/kani-driver/src/cbmc_property_renderer.rs b/kani-driver/src/cbmc_property_renderer.rs index cc48449e01b2..95a2175a2eac 100644 --- a/kani-driver/src/cbmc_property_renderer.rs +++ b/kani-driver/src/cbmc_property_renderer.rs @@ -22,96 +22,126 @@ static CBMC_ALT_DESCRIPTIONS: Lazy = Lazy::new(|| { map.insert("error_label", vec![]); map.insert("division-by-zero", vec![("division by zero", None)]); map.insert("enum-range-check", vec![("enum range check", None)]); - map.insert("undefined-shift", vec![ - ("shift distance is negative", None), - ("shift distance too large", None), - ("shift operand is negative", None), - ("shift of non-integer type", None), - ]); - map.insert("overflow", vec![ - ("result of signed mod is not representable", None), - ("arithmetic overflow on signed type conversion", None), - ("arithmetic overflow on signed division", None), - ("arithmetic overflow on signed unary minus", None), - ("arithmetic overflow on signed shl", None), - ("arithmetic overflow on unsigned unary minus", None), - ("arithmetic overflow on signed +", Some("arithmetic overflow on signed addition")), - ("arithmetic overflow on signed -", Some("arithmetic overflow on signed subtraction")), - ("arithmetic overflow on signed *", Some("arithmetic overflow on signed multiplication")), - ("arithmetic overflow on unsigned +", Some("arithmetic overflow on unsigned addition")), - ("arithmetic overflow on unsigned -", Some("arithmetic overflow on unsigned subtraction")), - ( - "arithmetic overflow on unsigned *", - Some("arithmetic overflow on unsigned multiplication"), - ), - ("arithmetic overflow on floating-point typecast", None), - ("arithmetic overflow on floating-point division", None), - ("arithmetic overflow on floating-point addition", None), - ("arithmetic overflow on floating-point subtraction", None), - ("arithmetic overflow on floating-point multiplication", None), - ("arithmetic overflow on unsigned to signed type conversion", None), - ("arithmetic overflow on float to signed integer type conversion", None), - ("arithmetic overflow on signed to unsigned type conversion", None), - ("arithmetic overflow on unsigned to unsigned type conversion", None), - ("arithmetic overflow on float to unsigned integer type conversion", None), - ]); - map.insert("NaN", vec![ - ("NaN on +", Some("NaN on addition")), - ("NaN on -", Some("NaN on subtraction")), - ("NaN on /", Some("NaN on division")), - ("NaN on *", Some("NaN on multiplication")), - ]); - map.insert("pointer_arithmetic", vec![ - ("pointer relation: deallocated dynamic object", None), - ("pointer relation: dead object", None), - ("pointer relation: pointer NULL", None), - ("pointer relation: pointer invalid", None), - ("pointer relation: pointer outside dynamic object bounds", None), - ("pointer relation: pointer outside object bounds", None), - ("pointer relation: invalid integer address", None), - ("pointer arithmetic: deallocated dynamic object", None), - ("pointer arithmetic: dead object", None), - ("pointer arithmetic: pointer NULL", None), - ("pointer arithmetic: pointer invalid", None), - ("pointer arithmetic: pointer outside dynamic object bounds", None), - ("pointer arithmetic: pointer outside object bounds", None), - ("pointer arithmetic: invalid integer address", None), - ]); - map.insert("pointer_dereference", vec![ - ( - "dereferenced function pointer must be", - Some("dereference failure: invalid function pointer"), - ), - ("dereference failure: pointer NULL", None), - ("dereference failure: pointer invalid", None), - ("dereference failure: deallocated dynamic object", None), - ("dereference failure: dead object", None), - ("dereference failure: pointer outside dynamic object bounds", None), - ("dereference failure: pointer outside object bounds", None), - ("dereference failure: invalid integer address", None), - ]); + map.insert( + "undefined-shift", + vec![ + ("shift distance is negative", None), + ("shift distance too large", None), + ("shift operand is negative", None), + ("shift of non-integer type", None), + ], + ); + map.insert( + "overflow", + vec![ + ("result of signed mod is not representable", None), + ("arithmetic overflow on signed type conversion", None), + ("arithmetic overflow on signed division", None), + ("arithmetic overflow on signed unary minus", None), + ("arithmetic overflow on signed shl", None), + ("arithmetic overflow on unsigned unary minus", None), + ("arithmetic overflow on signed +", Some("arithmetic overflow on signed addition")), + ("arithmetic overflow on signed -", Some("arithmetic overflow on signed subtraction")), + ( + "arithmetic overflow on signed *", + Some("arithmetic overflow on signed multiplication"), + ), + ("arithmetic overflow on unsigned +", Some("arithmetic overflow on unsigned addition")), + ( + "arithmetic overflow on unsigned -", + Some("arithmetic overflow on unsigned subtraction"), + ), + ( + "arithmetic overflow on unsigned *", + Some("arithmetic overflow on unsigned multiplication"), + ), + ("arithmetic overflow on floating-point typecast", None), + ("arithmetic overflow on floating-point division", None), + ("arithmetic overflow on floating-point addition", None), + ("arithmetic overflow on floating-point subtraction", None), + ("arithmetic overflow on floating-point multiplication", None), + ("arithmetic overflow on unsigned to signed type conversion", None), + ("arithmetic overflow on float to signed integer type conversion", None), + ("arithmetic overflow on signed to unsigned type conversion", None), + ("arithmetic overflow on unsigned to unsigned type conversion", None), + ("arithmetic overflow on float to unsigned integer type conversion", None), + ], + ); + map.insert( + "NaN", + vec![ + ("NaN on +", Some("NaN on addition")), + ("NaN on -", Some("NaN on subtraction")), + ("NaN on /", Some("NaN on division")), + ("NaN on *", Some("NaN on multiplication")), + ], + ); + map.insert( + "pointer_arithmetic", + vec![ + ("pointer relation: deallocated dynamic object", None), + ("pointer relation: dead object", None), + ("pointer relation: pointer NULL", None), + ("pointer relation: pointer invalid", None), + ("pointer relation: pointer outside dynamic object bounds", None), + ("pointer relation: pointer outside object bounds", None), + ("pointer relation: invalid integer address", None), + ("pointer arithmetic: deallocated dynamic object", None), + ("pointer arithmetic: dead object", None), + ("pointer arithmetic: pointer NULL", None), + ("pointer arithmetic: pointer invalid", None), + ("pointer arithmetic: pointer outside dynamic object bounds", None), + ("pointer arithmetic: pointer outside object bounds", None), + ("pointer arithmetic: invalid integer address", None), + ], + ); + map.insert( + "pointer_dereference", + vec![ + ( + "dereferenced function pointer must be", + Some("dereference failure: invalid function pointer"), + ), + ("dereference failure: pointer NULL", None), + ("dereference failure: pointer invalid", None), + ("dereference failure: deallocated dynamic object", None), + ("dereference failure: dead object", None), + ("dereference failure: pointer outside dynamic object bounds", None), + ("dereference failure: pointer outside object bounds", None), + ("dereference failure: invalid integer address", None), + ], + ); // These are very hard to understand without more context. - map.insert("pointer_primitives", vec![ - ("pointer invalid", None), - ("deallocated dynamic object", Some("pointer to deallocated dynamic object")), - ("dead object", Some("pointer to dead object")), - ("pointer outside dynamic object bounds", None), - ("pointer outside object bounds", None), - ("invalid integer address", None), - ]); - map.insert("array_bounds", vec![ - ("lower bound", Some("index out of bounds")), - // This one is redundant: - // ("dynamic object upper bound", Some("access out of bounds")), - ( - "upper bound", - Some("index out of bounds: the length is less than or equal to the given index"), - ), - ]); - map.insert("bit_count", vec![ - ("count trailing zeros is undefined for value zero", None), - ("count leading zeros is undefined for value zero", None), - ]); + map.insert( + "pointer_primitives", + vec![ + ("pointer invalid", None), + ("deallocated dynamic object", Some("pointer to deallocated dynamic object")), + ("dead object", Some("pointer to dead object")), + ("pointer outside dynamic object bounds", None), + ("pointer outside object bounds", None), + ("invalid integer address", None), + ], + ); + map.insert( + "array_bounds", + vec![ + ("lower bound", Some("index out of bounds")), + // This one is redundant: + // ("dynamic object upper bound", Some("access out of bounds")), + ( + "upper bound", + Some("index out of bounds: the length is less than or equal to the given index"), + ), + ], + ); + map.insert( + "bit_count", + vec![ + ("count trailing zeros is undefined for value zero", None), + ("count leading zeros is undefined for value zero", None), + ], + ); map.insert("memory-leak", vec![("dynamically allocated memory never freed", None)]); // These pre-conditions should not print temporary variables since they are embedded in the libc implementation. // They are added via `__CPROVER_precondition`. diff --git a/kani-driver/src/concrete_playback/test_generator.rs b/kani-driver/src/concrete_playback/test_generator.rs index 5c4076f13591..282ae42a7c37 100644 --- a/kani-driver/src/concrete_playback/test_generator.rs +++ b/kani-driver/src/concrete_playback/test_generator.rs @@ -500,11 +500,10 @@ mod tests { /// Check that the generated unit tests have the right formatting and indentation #[test] fn format_two_concrete_vals() { - let concrete_vals = - [ConcreteVal { byte_arr: vec![0, 0], interp_val: "0".to_string() }, ConcreteVal { - byte_arr: vec![0, 0, 0, 0, 0, 0, 0, 0], - interp_val: "0l".to_string(), - }]; + let concrete_vals = [ + ConcreteVal { byte_arr: vec![0, 0], interp_val: "0".to_string() }, + ConcreteVal { byte_arr: vec![0, 0, 0, 0, 0, 0, 0, 0], interp_val: "0l".to_string() }, + ]; let actual: Vec<_> = format_concrete_vals(&concrete_vals).collect(); let expected = vec![ format!("{:<8}// 0", " "), @@ -598,11 +597,10 @@ mod tests { #[test] fn check_rustfmt_args_some_line_ranges() { - let file_line_ranges = - [FileLineRange { file: "file1".to_string(), line_range: None }, FileLineRange { - file: "path/to/file2".to_string(), - line_range: Some((1, 3)), - }]; + let file_line_ranges = [ + FileLineRange { file: "file1".to_string(), line_range: None }, + FileLineRange { file: "path/to/file2".to_string(), line_range: Some((1, 3)) }, + ]; let args = rustfmt_args(&file_line_ranges); let expected: Vec = [ "--unstable-features", diff --git a/library/kani/src/models/mod.rs b/library/kani/src/models/mod.rs index af5ac336e09e..3329b1d4b895 100644 --- a/library/kani/src/models/mod.rs +++ b/library/kani/src/models/mod.rs @@ -65,7 +65,7 @@ mod intrinsics { /// Logic similar to `bitmask_len` from `portable_simd`. /// pub(super) const fn mask_len(len: usize) -> usize { - (len + 7) / 8 + len.div_ceil(8) } #[cfg(target_endian = "little")] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 3741e551a5b8..307d7e8a6120 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-01-28" +channel = "nightly-2025-02-10" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/expected/uninit/delayed-ub/expected b/tests/expected/uninit/delayed-ub/delayed-ub.expected similarity index 88% rename from tests/expected/uninit/delayed-ub/expected rename to tests/expected/uninit/delayed-ub/delayed-ub.expected index b7c139c2d101..f062a30cf6b0 100644 --- a/tests/expected/uninit/delayed-ub/expected +++ b/tests/expected/uninit/delayed-ub/delayed-ub.expected @@ -2,10 +2,6 @@ delayed_ub_trigger_copy.assertion.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`"\ -delayed_ub_slices.assertion.\ - - Status: FAILURE\ - - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `[u128; 4]`" - delayed_ub_structs.assertion.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `U`" @@ -44,7 +40,6 @@ delayed_ub.assertion.\ Summary: Verification failed for - delayed_ub_trigger_copy -Verification failed for - delayed_ub_slices Verification failed for - delayed_ub_structs Verification failed for - delayed_ub_double_copy Verification failed for - delayed_ub_copy @@ -54,4 +49,4 @@ Verification failed for - delayed_ub_laundered Verification failed for - delayed_ub_static Verification failed for - delayed_ub_transmute Verification failed for - delayed_ub -Complete - 0 successfully verified harnesses, 11 failures, 11 total. +Complete - 0 successfully verified harnesses, 10 failures, 10 total. diff --git a/tests/expected/uninit/delayed-ub/delayed-ub.rs b/tests/expected/uninit/delayed-ub/delayed-ub.rs index d7f258c27ba5..17b654533406 100644 --- a/tests/expected/uninit/delayed-ub/delayed-ub.rs +++ b/tests/expected/uninit/delayed-ub/delayed-ub.rs @@ -170,19 +170,6 @@ fn delayed_ub_structs() { } } -/// Delayed UB via mutable pointer write into a slice element. -#[kani::proof] -fn delayed_ub_slices() { - unsafe { - // Create an array. - let mut arr = [0u128; 4]; - // Get a pointer to a part of the array. - let ptr = &mut arr[0..2][0..1][0] as *mut _ as *mut (u8, u32); - *ptr = (4, 4); - let arr_copy = arr; // UB: This reads a padding value inside the array! - } -} - /// Delayed UB via mutable pointer copy, which should be the only delayed UB trigger in this case. #[kani::proof] fn delayed_ub_trigger_copy() { diff --git a/tests/expected/uninit/delayed-ub/slices_fixme.expected b/tests/expected/uninit/delayed-ub/slices_fixme.expected new file mode 100644 index 000000000000..902980a2bf1c --- /dev/null +++ b/tests/expected/uninit/delayed-ub/slices_fixme.expected @@ -0,0 +1,5 @@ +delayed_ub_slices.assertion.\ + - Status: FAILURE\ + - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `[u128; 4]`" + +Verification failed for - delayed_ub_slices diff --git a/tests/expected/uninit/delayed-ub/slices_fixme.rs b/tests/expected/uninit/delayed-ub/slices_fixme.rs new file mode 100644 index 000000000000..c112a4638363 --- /dev/null +++ b/tests/expected/uninit/delayed-ub/slices_fixme.rs @@ -0,0 +1,21 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z ghost-state -Z uninit-checks + +//! Checks that Kani catches instances of delayed UB for slices. +//! This test used to live inside delayed-ub, but the 2/5/2025 toolchain upgrade introduced a regression for this test. +//! Once this test is fixed, move it back into delayed-ub.rs +//! See https://github.com/model-checking/kani/issues/3881 for details. + +/// Delayed UB via mutable pointer write into a slice element. +#[kani::proof] +fn delayed_ub_slices() { + unsafe { + // Create an array. + let mut arr = [0u128; 4]; + // Get a pointer to a part of the array. + let ptr = &mut arr[0..2][0..1][0] as *mut _ as *mut (u8, u32); + *ptr = (4, 4); + let arr_copy = arr; // UB: This reads a padding value inside the array! + } +} diff --git a/tests/expected/uninit/multiple-instrumentations.expected b/tests/expected/uninit/multiple-instrumentations.expected index 153024dc692b..0f503b624ca4 100644 --- a/tests/expected/uninit/multiple-instrumentations.expected +++ b/tests/expected/uninit/multiple-instrumentations.expected @@ -1,16 +1,16 @@ -multiple_instrumentations_different_vars.assertion.3\ +multiple_instrumentations_different_vars.assertion\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`" -multiple_instrumentations_different_vars.assertion.4\ +multiple_instrumentations_different_vars.assertion\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u64`" -multiple_instrumentations.assertion.2\ +multiple_instrumentations.assertion\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`" -multiple_instrumentations.assertion.3\ +multiple_instrumentations.assertion\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`" diff --git a/tests/expected/zst/expected b/tests/expected/zst/expected index bec891bea92c..3efe59d3445f 100644 --- a/tests/expected/zst/expected +++ b/tests/expected/zst/expected @@ -1,4 +1,4 @@ -Status: FAILURE\ -Description: "dereference failure: pointer NULL" +- Status: FAILURE\ +- Description: "null pointer dereference occurred" VERIFICATION:- FAILED diff --git a/tools/kani-cov/src/report.rs b/tools/kani-cov/src/report.rs index 95eba4d252ee..398f45fd47cb 100644 --- a/tools/kani-cov/src/report.rs +++ b/tools/kani-cov/src/report.rs @@ -261,7 +261,8 @@ fn insert_escapes(str: &str, markers: Vec<(ColumnNumber, bool)>, format: &Report escaped_str.insert_str(i + offset, b); // `offset` keeps track of the bytes we've already inserted so the original // index is shifted by the appropriate amount in subsequent insertions. - offset += b.bytes().len(); + // Note that b.len() returns the length of the string in bytes, c.f. https://doc.rust-lang.org/std/string/struct.String.html#method.len + offset += b.len(); } escaped_str } diff --git a/tools/kani-cov/src/summary.rs b/tools/kani-cov/src/summary.rs index e56008daade1..f07f4a83afdb 100644 --- a/tools/kani-cov/src/summary.rs +++ b/tools/kani-cov/src/summary.rs @@ -165,9 +165,9 @@ pub fn line_coverage_results( } } } else { - line_status = std::iter::repeat(Some((0, MarkerInfo::FullLine))) - .take(end_line - start_line + 1) - .collect(); + line_status = + std::iter::repeat_n(Some((0, MarkerInfo::FullLine)), end_line - start_line + 1) + .collect(); } line_status } From 475ea5e21f7e614c77e8f866366922786127079f Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Wed, 12 Feb 2025 11:24:21 -0800 Subject: [PATCH 019/161] Add loop-contracts doc to SUMMARY (#3886) Add documentation of loop contracts to `SUMMARY.md` so that it will show up in the book. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- docs/src/SUMMARY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 11d5318a0e2c..8cd6a1ebb7b0 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -22,6 +22,7 @@ - [Coverage](./reference/experimental/coverage.md) - [Stubbing](./reference/experimental/stubbing.md) - [Contracts](./reference/experimental/contracts.md) + - [Loop Contracts](./reference/experimental/loop-contracts.md) - [Concrete Playback](./reference/experimental/concrete-playback.md) - [Application](./application.md) - [Comparison with other tools](./tool-comparison.md) From ff3aea37216b84704b1525590c1519353ea3abcd Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Thu, 13 Feb 2025 19:01:17 -0500 Subject: [PATCH 020/161] Support concrete playback for arrays of length 65 or greater (#3888) ### Problem When CBMC generates a JSON trace for a nondeterministic array, it will output a key-value pair `elements: [array]`, where `array` contains concrete values for each element in the array. If the array is length 64 or shorter, it will _also_ output the elements of the array as separate values in the trace (so each element is in the trace twice). Prior to this PR, concrete playback relied on the latter output format to find elements of an array. However, when the array was length 65 or greater, those elements wouldn't be values in their own right, so we wouldn't find any values for the array and just output an empty array. ### Solution This PR changes our representation of concrete values to handle arrays explicitly; i.e., to look for the `elements` array and populate the concrete values of the array from that instead. ### Callouts For those wondering why the `playback_already_existed` test changed, it's because we're hashing `ConcreteItem` instead of `ConcreteValue`, so the hash changes. Resolves #3787 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- kani-driver/src/cbmc_output_parser.rs | 10 +- .../src/concrete_playback/test_generator.rs | 431 ++++++++++++++---- .../cargo_playback_array/config.yml | 4 + .../playback_target.expected | 9 + .../cargo_playback_array/playback_target.sh | 27 ++ .../sample_crate/Cargo.toml | 10 + .../sample_crate/src/lib.rs | 16 + .../playback_already_existing/original.rs | 2 +- 8 files changed, 422 insertions(+), 87 deletions(-) create mode 100644 tests/script-based-pre/cargo_playback_array/config.yml create mode 100644 tests/script-based-pre/cargo_playback_array/playback_target.expected create mode 100755 tests/script-based-pre/cargo_playback_array/playback_target.sh create mode 100644 tests/script-based-pre/cargo_playback_array/sample_crate/Cargo.toml create mode 100644 tests/script-based-pre/cargo_playback_array/sample_crate/src/lib.rs diff --git a/kani-driver/src/cbmc_output_parser.rs b/kani-driver/src/cbmc_output_parser.rs index bf86ef08c217..79a2c20732b4 100644 --- a/kani-driver/src/cbmc_output_parser.rs +++ b/kani-driver/src/cbmc_output_parser.rs @@ -296,12 +296,20 @@ pub struct TraceItem { /// Struct that represents a trace value. /// /// Note: this struct can have a lot of different fields depending on the value type. -/// The fields included right now are relevant to primitive types. +/// The fields included right now are relevant to primitive types and arrays. #[derive(Clone, Debug, Deserialize)] pub struct TraceValue { pub binary: Option, pub data: Option, pub width: Option, + // Invariant: elements is Some iff binary, data, and width are None. + pub elements: Option>, +} + +/// Struct that represents an element of an array in a trace. +#[derive(Clone, Debug, Deserialize)] +pub struct TraceArrayValue { + pub value: TraceValue, } /// Enum that represents a trace data item. diff --git a/kani-driver/src/concrete_playback/test_generator.rs b/kani-driver/src/concrete_playback/test_generator.rs index 282ae42a7c37..d397c5f00dff 100644 --- a/kani-driver/src/concrete_playback/test_generator.rs +++ b/kani-driver/src/concrete_playback/test_generator.rs @@ -9,7 +9,7 @@ use crate::call_cbmc::VerificationResult; use crate::cbmc_output_parser::Property; use crate::session::KaniSession; use anyhow::{Context, Result}; -use concrete_vals_extractor::{ConcreteVal, extract_harness_values}; +use concrete_vals_extractor::{ConcreteItem, PrimitiveConcreteVal, extract_harness_values}; use kani_metadata::{HarnessKind, HarnessMetadata}; use std::collections::hash_map::DefaultHasher; use std::ffi::OsString; @@ -44,9 +44,9 @@ impl KaniSession { } else { let mut unit_tests: Vec = harness_values .iter() - .map(|(prop, concrete_vals)| { + .map(|(prop, concrete_items)| { let pretty_name = harness.get_harness_name_unqualified(); - format_unit_test(&pretty_name, &concrete_vals, gen_test_doc(harness, prop)) + format_unit_test(&pretty_name, &concrete_items, gen_test_doc(harness, prop)) }) .collect(); unit_tests.dedup_by(|a, b| a.name == b.name); @@ -278,13 +278,13 @@ fn gen_test_doc(harness: &HarnessMetadata, property: &Property) -> String { /// Generate a formatted unit test from a list of concrete values. fn format_unit_test( harness_name: &str, - concrete_vals: &[ConcreteVal], + concrete_items: &[ConcreteItem], doc_str: String, ) -> UnitTest { // Hash the concrete values along with the proof harness name. let mut hasher = DefaultHasher::new(); harness_name.hash(&mut hasher); - concrete_vals.hash(&mut hasher); + concrete_items.hash(&mut hasher); let hash = hasher.finish(); let func_name = format!("kani_concrete_playback_{harness_name}_{hash}"); @@ -295,7 +295,7 @@ fn format_unit_test( format!("{:<4}let concrete_vals: Vec> = vec![", " "), ] .into_iter(); - let formatted_concrete_vals = format_concrete_vals(concrete_vals); + let formatted_concrete_items = format_concrete_items(concrete_items); let func_after_concrete_vals = [ format!("{:<4}];", " "), format!("{:<4}kani::concrete_playback_run(concrete_vals, {harness_name});", " "), @@ -304,15 +304,33 @@ fn format_unit_test( .into_iter(); let full_func: Vec<_> = func_before_concrete_vals - .chain(formatted_concrete_vals) + .chain(formatted_concrete_items) .chain(func_after_concrete_vals) .collect(); UnitTest { code: full_func, name: func_name } } +/// Format concrete items as strings--these make up the body of the concrete test. +fn format_concrete_items(concrete_items: &[ConcreteItem]) -> impl Iterator + '_ { + // Note that ConcreteItem::Arrays are flattened, e.g., given: concrete_items = [ConcreteItem::Array(val1, val2), ConcreteItem::Primitive(val3)], + // we output the formatted strings for val1, val2, and val3, with no grouping of val1 and val2 in an outer vector. + // library::concrete_playback::any_raw_array relies on this formatting assumption. + // This is not a fundamental limitation -- we could group the byte arrays for an array in an outer vector, + // but that could cause confusion if we don't group byte arrays for other types, e.g., a struct with multiple fields. + // So, we leave it flattened for now. + // See the tracking issue for improving this output format at https://github.com/model-checking/kani/issues/1527. + concrete_items.iter().flat_map(|item| match item { + ConcreteItem::Array(vals) => format_concrete_vals(vals), + ConcreteItem::Primitive(val) => format_concrete_vals(std::slice::from_ref(val)), + }) +} + /// Format an initializer expression for a number of concrete values. -fn format_concrete_vals(concrete_vals: &[ConcreteVal]) -> impl Iterator + '_ { +/// Each byte vector has a comment above it with its interpreted value, i.e., its decimal representation. +fn format_concrete_vals( + concrete_vals: &[PrimitiveConcreteVal], +) -> impl Iterator + '_ { /* Given a number of byte vectors, format them as: // interp_concrete_val_1 @@ -362,10 +380,18 @@ struct UnitTest { /// ..., ] } /// ``` mod concrete_vals_extractor { - use crate::cbmc_output_parser::{CheckStatus, Property, TraceItem}; + use crate::cbmc_output_parser::{CheckStatus, Property, TraceItem, TraceValue}; + + #[derive(Hash)] + pub enum ConcreteItem { + Primitive(PrimitiveConcreteVal), + Array(Vec), + } + /// Represents the concrete value of a primitive type--its byte representation and the intepreted value. + /// E.g., a u16 with decimal value 65280 would be PrimitiveConcreteVal { byte_arr: vec![0, 255], interp_val: "65280" }. #[derive(Hash)] - pub struct ConcreteVal { + pub struct PrimitiveConcreteVal { pub byte_arr: Vec, pub interp_val: String, } @@ -373,7 +399,9 @@ mod concrete_vals_extractor { /// Extract a set of concrete values that trigger one assertion /// failure. Each element of the outer vector corresponds to /// inputs triggering one assertion failure or cover statement. - pub fn extract_harness_values(result_items: &[Property]) -> Vec<(&Property, Vec)> { + pub fn extract_harness_values( + result_items: &[Property], + ) -> Vec<(&Property, Vec)> { result_items .iter() .filter(|prop| { @@ -386,70 +414,109 @@ mod concrete_vals_extractor { .trace .as_ref() .expect(&format!("Missing trace for {}", property.property_name())); - let concrete_vals: Vec = + let concrete_items: Vec = trace.iter().filter_map(&extract_from_trace_item).collect(); - (property, concrete_vals) + (property, concrete_items) }) .collect() } - /// Extracts individual bytes returned by kani::any() calls. - fn extract_from_trace_item(trace_item: &TraceItem) -> Option { - if let (Some(lhs), Some(source_location), Some(value)) = - (&trace_item.lhs, &trace_item.source_location, &trace_item.value) - { - if let ( - Some(func), - Some(width_u64), - Some(bit_concrete_val), - Some(interp_concrete_val), - ) = (&source_location.function, value.width, &value.binary, &value.data) - { - if trace_item.step_type == "assignment" - && lhs.starts_with("goto_symex$$return_value") - && func.starts_with("kani::any_raw_") - { - let declared_width = width_u64 as usize; - let actual_width = bit_concrete_val.len(); - assert_eq!( - declared_width, actual_width, - "Declared width of {declared_width} doesn't equal actual width of {actual_width}" - ); - let mut next_num: Vec = Vec::new(); - - // Reverse because of endianess of CBMC trace. - for i in (0..declared_width).step_by(8).rev() { - let str_chunk = &bit_concrete_val[i..i + 8]; - let str_chunk_len = str_chunk.len(); - assert_eq!( - str_chunk_len, 8, - "Tried to read a chunk of 8 bits of actually read {str_chunk_len} bits" - ); - let next_byte = u8::from_str_radix(str_chunk, 2).expect(&format!( - "Couldn't convert the string chunk `{str_chunk}` to u8" - )); - next_num.push(next_byte); - } + /// Extracts individual bytes from a TraceValue for a primitive type + /// to produce a PrimitiveConcreteVal representing that value. + fn extract_primitive_value(value: &TraceValue) -> Option { + assert!( + value.elements.is_none(), + "Expected no array elements for primitive value, but found: {:?}", + value.elements + ); + let (Some(width_u64), Some(bit_concrete_val), Some(interp_concrete_val)) = + (value.width, &value.binary, &value.data) + else { + return None; + }; - // In ARM64 Linux, CBMC will produce a character instead of a number for - // interpreted values because the char type is unsigned in that platform. - // For example, for the value `101` it will produce `'e'` instead of `101`. - // To correct this, we check if the value starts and ends with `'`, and - // convert the character into its ASCII value in that case. - let interp_val = { - let interp_val_str = interp_concrete_val.to_string(); - if interp_val_str.starts_with('\'') && interp_val_str.ends_with('\'') { - let interp_num = interp_val_str.chars().nth(1).unwrap() as u8; - interp_num.to_string() - } else { - interp_val_str - } - }; + let declared_width = width_u64 as usize; + let actual_width = bit_concrete_val.len(); + assert_eq!( + declared_width, actual_width, + "Declared width of {declared_width} doesn't equal actual width of {actual_width}" + ); + let mut next_num: Vec = Vec::new(); + + // Reverse because of endianess of CBMC trace. + for i in (0..declared_width).step_by(8).rev() { + let str_chunk = &bit_concrete_val[i..i + 8]; + let str_chunk_len = str_chunk.len(); + assert_eq!( + str_chunk_len, 8, + "Tried to read a chunk of 8 bits of actually read {str_chunk_len} bits" + ); + let next_byte = u8::from_str_radix(str_chunk, 2) + .expect(&format!("Couldn't convert the string chunk `{str_chunk}` to u8")); + next_num.push(next_byte); + } - return Some(ConcreteVal { byte_arr: next_num, interp_val }); - } + // In ARM64 Linux, CBMC will produce a character instead of a number for + // interpreted values because the char type is unsigned in that platform. + // For example, for the value `101` it will produce `'e'` instead of `101`. + // To correct this, we check if the value starts and ends with `'`, and + // convert the character into its ASCII value in that case. + let interp_val = { + let interp_val_str = interp_concrete_val.to_string(); + if interp_val_str.starts_with('\'') && interp_val_str.ends_with('\'') { + let interp_num = interp_val_str.chars().nth(1).unwrap() as u8; + interp_num.to_string() + } else { + interp_val_str } + }; + + Some(PrimitiveConcreteVal { byte_arr: next_num, interp_val }) + } + + /// Extracts individual bytes from a TraceItem corresponding to a kani::any() call + /// and returns a ConcreteItem representing it. + fn extract_from_trace_item(trace_item: &TraceItem) -> Option { + let (Some(lhs), Some(source_location), Some(value)) = + (&trace_item.lhs, &trace_item.source_location, &trace_item.value) + else { + return None; + }; + + let Some(func) = &source_location.function else { + return None; + }; + + if !(trace_item.step_type == "assignment" + && lhs.starts_with("goto_symex$$return_value") + && func.starts_with("kani::any_raw_")) + { + return None; + } + + if let Some(array_elements) = &value.elements { + let concrete_vals = array_elements + .iter() + .map(|array_value| { + let element_val = extract_primitive_value(&array_value.value); + if let Some(val) = element_val { + val + } else { + // We expect that if we have an array, every value in the array is representable as a primitive type. + // To avoid generating a test with a malformed (i.e., too short) array, we panic instead of returning None + // if converting an element fails. + panic!("Couldn't extract concrete value from {array_value:?}"); + } + }) + .collect::>(); + return Some(ConcreteItem::Array(concrete_vals)); + // At time of writing, if the array length is <= 64, CBMC's trace will include TraceItems for each primitive value of the array, + // as well as the `elements` field with the entire array (for arrays length > 65, it just has `elements`). + // So, filter out any instance of any_raw_array to avoid generating duplicate values for the primitive values that are separate from `elements`. + } else if !func.starts_with("kani::any_raw_array") { + let concrete_val = extract_primitive_value(value); + return concrete_val.map(ConcreteItem::Primitive); } None } @@ -460,7 +527,8 @@ mod tests { use super::concrete_vals_extractor::*; use super::*; use crate::cbmc_output_parser::{ - CheckStatus, Property, PropertyId, SourceLocation, TraceData, TraceItem, TraceValue, + CheckStatus, Property, PropertyId, SourceLocation, TraceArrayValue, TraceData, TraceItem, + TraceValue, }; /// util function for unit tests taht generates the rustfmt args used for formatting specific lines inside specific files. @@ -491,7 +559,7 @@ mod tests { #[test] fn format_zero_concrete_vals() { - let concrete_vals: [ConcreteVal; 0] = []; + let concrete_vals: [PrimitiveConcreteVal; 0] = []; let actual: Vec<_> = format_concrete_vals(&concrete_vals).collect(); let expected: Vec = Vec::new(); assert_eq!(actual, expected); @@ -501,8 +569,11 @@ mod tests { #[test] fn format_two_concrete_vals() { let concrete_vals = [ - ConcreteVal { byte_arr: vec![0, 0], interp_val: "0".to_string() }, - ConcreteVal { byte_arr: vec![0, 0, 0, 0, 0, 0, 0, 0], interp_val: "0l".to_string() }, + PrimitiveConcreteVal { byte_arr: vec![0, 0], interp_val: "0".to_string() }, + PrimitiveConcreteVal { + byte_arr: vec![0, 0, 0, 0, 0, 0, 0, 0], + interp_val: "0l".to_string(), + }, ]; let actual: Vec<_> = format_concrete_vals(&concrete_vals).collect(); let expected = vec![ @@ -535,7 +606,10 @@ mod tests { fn format_unit_test_full_func() { let doc_str = "/// Test documentation"; let harness_name = "test_proof_harness"; - let concrete_vals = [ConcreteVal { byte_arr: vec![0, 0], interp_val: "0".to_string() }]; + let concrete_vals = [ConcreteItem::Primitive(PrimitiveConcreteVal { + byte_arr: vec![0, 0], + interp_val: "0".to_string(), + })]; let unit_test = format_unit_test(harness_name, &concrete_vals, doc_str.to_string()); let full_func = unit_test.code; let split_unit_test_name = split_unit_test_name(&unit_test.name); @@ -559,10 +633,10 @@ mod tests { } /// Generates a unit test and returns its hash. - fn extract_hash_from_unit_test(harness_name: &str, concrete_vals: &[ConcreteVal]) -> String { + fn extract_hash_from_unit_test(harness_name: &str, concrete_items: &[ConcreteItem]) -> String { let unit_test = format_unit_test( harness_name, - concrete_vals, + concrete_items, "/// Harness created for unit test".to_string(), ); split_unit_test_name(&unit_test.name).hash @@ -573,14 +647,41 @@ mod tests { fn check_hashes_are_unique() { let harness_name_1 = "test_proof_harness1"; let harness_name_2 = "test_proof_harness2"; - let concrete_vals_1 = [ConcreteVal { byte_arr: vec![0, 0], interp_val: "0".to_string() }]; - let concrete_vals_2 = [ConcreteVal { byte_arr: vec![1, 0], interp_val: "0".to_string() }]; - let concrete_vals_3 = [ConcreteVal { byte_arr: vec![0, 0], interp_val: "1".to_string() }]; + let concrete_items_1 = [ + ConcreteItem::Primitive(PrimitiveConcreteVal { + byte_arr: vec![0, 0], + interp_val: "0".to_string(), + }), + ConcreteItem::Array(vec![PrimitiveConcreteVal { + byte_arr: vec![0, 0], + interp_val: "0".to_string(), + }]), + ]; + let concrete_items_2 = [ConcreteItem::Primitive(PrimitiveConcreteVal { + byte_arr: vec![1, 0], + interp_val: "0".to_string(), + })]; + let concrete_items_3 = [ + ConcreteItem::Array(vec![PrimitiveConcreteVal { + byte_arr: vec![0, 0], + interp_val: "0".to_string(), + }]), + ConcreteItem::Primitive(PrimitiveConcreteVal { + byte_arr: vec![0, 0], + interp_val: "1".to_string(), + }), + ConcreteItem::Array(vec![ + PrimitiveConcreteVal { byte_arr: vec![0, 0], interp_val: "0".to_string() }, + PrimitiveConcreteVal { byte_arr: vec![1, 0], interp_val: "0".to_string() }, + PrimitiveConcreteVal { byte_arr: vec![1, 0, 255], interp_val: "0".to_string() }, + ]), + ]; - let hash_base = extract_hash_from_unit_test(harness_name_1, &concrete_vals_1); - let hash_diff_harness_name = extract_hash_from_unit_test(harness_name_2, &concrete_vals_1); - let hash_diff_concrete_byte = extract_hash_from_unit_test(harness_name_1, &concrete_vals_2); - let hash_diff_interp_val = extract_hash_from_unit_test(harness_name_1, &concrete_vals_3); + let hash_base = extract_hash_from_unit_test(harness_name_1, &concrete_items_1); + let hash_diff_harness_name = extract_hash_from_unit_test(harness_name_2, &concrete_items_1); + let hash_diff_concrete_byte = + extract_hash_from_unit_test(harness_name_1, &concrete_items_2); + let hash_diff_interp_val = extract_hash_from_unit_test(harness_name_1, &concrete_items_3); assert_ne!(hash_base, hash_diff_harness_name); assert_ne!(hash_base, hash_diff_concrete_byte); @@ -626,7 +727,7 @@ mod tests { /// Test util functions which extract the counter example values from a property. #[test] - fn check_concrete_vals_extractor() { + fn check_concrete_vals_extractor_primitive() { let processed_items = [Property { description: "".to_string(), property_id: PropertyId { @@ -655,13 +756,173 @@ mod tests { binary: Some("0000001100000001".to_string()), data: Some(TraceData::NonBool("385".to_string())), width: Some(16), + elements: None, }), }]), }]; - let (_, concrete_vals) = extract_harness_values(&processed_items).pop().unwrap(); - let concrete_val = &concrete_vals[0]; + let (_, concrete_items) = extract_harness_values(&processed_items).pop().unwrap(); + let concrete_item = &concrete_items[0]; - assert_eq!(concrete_val.byte_arr, vec![1, 3]); - assert_eq!(concrete_val.interp_val, "385"); + assert!(matches!(concrete_item, ConcreteItem::Primitive(_))); + if let ConcreteItem::Primitive(concrete_val) = concrete_item { + assert_eq!(concrete_val.byte_arr, vec![1, 3]); + assert_eq!(concrete_val.interp_val, "385"); + } + } + + /// Test util functions which extract the counter example values from a property for arrays. + #[test] + fn check_concrete_vals_extractor_array() { + let processed_items = [Property { + description: "".to_string(), + property_id: PropertyId { + fn_name: Some("".to_string()), + class: "assertion".to_string(), + id: 1, + }, + status: CheckStatus::Failure, + reach: None, + source_location: SourceLocation { + column: None, + file: None, + function: None, + line: None, + }, + trace: Some(vec![ + TraceItem { + step_type: "assignment".to_string(), + lhs: Some("goto_symex$$return_value".to_string()), + source_location: Some(SourceLocation { + column: None, + file: None, + function: Some("kani::any_raw_array::".to_string()), + line: None, + }), + value: Some(TraceValue { + binary: None, + data: None, + width: None, + elements: Some(vec![ + TraceArrayValue { + value: TraceValue { + binary: Some("11111111111111111111111111111111".to_string()), + data: Some(TraceData::NonBool("4294967295".to_string())), + width: Some(32), + elements: None, + }, + }, + TraceArrayValue { + value: TraceValue { + binary: Some("10000000000000000000000000000000".to_string()), + data: Some(TraceData::NonBool("2147483648".to_string())), + width: Some(32), + elements: None, + }, + }, + TraceArrayValue { + value: TraceValue { + binary: Some("11111111111111111111111111111111".to_string()), + data: Some(TraceData::NonBool("4294967295".to_string())), + width: Some(32), + elements: None, + }, + }, + TraceArrayValue { + value: TraceValue { + binary: Some("00000000000000000000000000000111".to_string()), + data: Some(TraceData::NonBool("7".to_string())), + width: Some(32), + elements: None, + }, + }, + ]), + }), + }, + // Since the array is of size 4, there are also TraceItems for each element of the array, which extract_harness_value should ignore. + TraceItem { + step_type: "assignment".to_string(), + lhs: Some("goto_symex$$return_value".to_string()), + source_location: Some(SourceLocation { + column: None, + file: None, + function: Some("kani::any_raw_array::".to_string()), + line: None, + }), + value: Some(TraceValue { + binary: Some("11111111111111111111111111111111".to_string()), + data: Some(TraceData::NonBool("4294967295".to_string())), + width: Some(32), + elements: None, + }), + }, + TraceItem { + step_type: "assignment".to_string(), + lhs: Some("goto_symex$$return_value".to_string()), + source_location: Some(SourceLocation { + column: None, + file: None, + function: Some("kani::any_raw_array::".to_string()), + line: None, + }), + value: Some(TraceValue { + binary: Some("10000000000000000000000000000000".to_string()), + data: Some(TraceData::NonBool("2147483648".to_string())), + width: Some(32), + elements: None, + }), + }, + TraceItem { + step_type: "assignment".to_string(), + lhs: Some("goto_symex$$return_value".to_string()), + source_location: Some(SourceLocation { + column: None, + file: None, + function: Some("kani::any_raw_array::".to_string()), + line: None, + }), + value: Some(TraceValue { + binary: Some("11111111111111111111111111111111".to_string()), + data: Some(TraceData::NonBool("4294967295".to_string())), + width: Some(32), + elements: None, + }), + }, + TraceItem { + step_type: "assignment".to_string(), + lhs: Some("goto_symex$$return_value".to_string()), + source_location: Some(SourceLocation { + column: None, + file: None, + function: Some("kani::any_raw_array::".to_string()), + line: None, + }), + value: Some(TraceValue { + binary: Some("00000000000000000000000000000111".to_string()), + data: Some(TraceData::NonBool("7".to_string())), + width: Some(32), + elements: None, + }), + }, + ]), + }]; + let (_, concrete_items) = extract_harness_values(&processed_items).pop().unwrap(); + let concrete_item = &concrete_items[0]; + + assert!(matches!(concrete_item, ConcreteItem::Array(_))); + if let ConcreteItem::Array(concrete_vals) = concrete_item { + assert_eq!(concrete_vals.len(), 4); + let first_val = &concrete_vals[0]; + assert_eq!(first_val.byte_arr, vec![255, 255, 255, 255]); + assert_eq!(first_val.interp_val, "4294967295"); + let second_val = &concrete_vals[1]; + assert_eq!(second_val.byte_arr, vec![0, 0, 0, 128]); + assert_eq!(second_val.interp_val, "2147483648"); + let third_val = &concrete_vals[2]; + assert_eq!(third_val.byte_arr, vec![255, 255, 255, 255]); + assert_eq!(third_val.interp_val, "4294967295"); + let fourth_val = &concrete_vals[3]; + assert_eq!(fourth_val.byte_arr, vec![7, 0, 0, 0]); + assert_eq!(fourth_val.interp_val, "7"); + } } } diff --git a/tests/script-based-pre/cargo_playback_array/config.yml b/tests/script-based-pre/cargo_playback_array/config.yml new file mode 100644 index 000000000000..99bfe39f95a5 --- /dev/null +++ b/tests/script-based-pre/cargo_playback_array/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: playback_target.sh +expected: playback_target.expected diff --git a/tests/script-based-pre/cargo_playback_array/playback_target.expected b/tests/script-based-pre/cargo_playback_array/playback_target.expected new file mode 100644 index 000000000000..ef1ffa7f2f93 --- /dev/null +++ b/tests/script-based-pre/cargo_playback_array/playback_target.expected @@ -0,0 +1,9 @@ +Failed Checks: index out of bounds: the length is less than or equal to the given index + +VERIFICATION:- FAILED + +INFO: Now modifying the source code to include the concrete playback unit test: + +running 1 test + +index out of bounds: the len is 65 but the index is \ No newline at end of file diff --git a/tests/script-based-pre/cargo_playback_array/playback_target.sh b/tests/script-based-pre/cargo_playback_array/playback_target.sh new file mode 100755 index 000000000000..17eaa66a01c6 --- /dev/null +++ b/tests/script-based-pre/cargo_playback_array/playback_target.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +set +e + +function check_playback { + local OUTPUT=output.log + cargo kani playback "${@}" >& $OUTPUT + # Sort output so we can rely on the order. + echo "$(grep "test verify::.* ok" $OUTPUT | sort)" + echo + echo "======= Raw Output =======" + cat $OUTPUT + echo "==========================" + echo + rm $OUTPUT +} + +pushd sample_crate > /dev/null +cargo clean + +cargo kani --concrete-playback inplace -Z concrete-playback +check_playback -Z concrete-playback + +cargo clean +popd > /dev/null diff --git a/tests/script-based-pre/cargo_playback_array/sample_crate/Cargo.toml b/tests/script-based-pre/cargo_playback_array/sample_crate/Cargo.toml new file mode 100644 index 000000000000..202362506011 --- /dev/null +++ b/tests/script-based-pre/cargo_playback_array/sample_crate/Cargo.toml @@ -0,0 +1,10 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +[package] +name = "sample_crate" +version = "0.1.0" +edition = "2021" +doctest = false + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tests/script-based-pre/cargo_playback_array/sample_crate/src/lib.rs b/tests/script-based-pre/cargo_playback_array/sample_crate/src/lib.rs new file mode 100644 index 000000000000..8168b2a2da50 --- /dev/null +++ b/tests/script-based-pre/cargo_playback_array/sample_crate/src/lib.rs @@ -0,0 +1,16 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Test that concrete playback generates concrete values for arrays over the length of 64 +//! and that playback can run those tests and find the index out of bounds bug, +//! c.f. https://github.com/model-checking/kani/issues/3787 + +#[cfg(kani)] +mod verify { + #[kani::proof] + fn index_array_65() { + let arr: [u16; 65] = kani::any(); + let idx: usize = kani::any(); + arr[idx]; + } +} diff --git a/tests/script-based-pre/playback_already_existing/original.rs b/tests/script-based-pre/playback_already_existing/original.rs index c6fb819519df..951441ff6324 100644 --- a/tests/script-based-pre/playback_already_existing/original.rs +++ b/tests/script-based-pre/playback_already_existing/original.rs @@ -25,7 +25,7 @@ mod verify { } } #[test] - fn kani_concrete_playback_try_nz_u8_17663051139329126359() { + fn kani_concrete_playback_try_nz_u8_1592364891838466833() { let concrete_vals: Vec> = vec![ // 0 vec![0], From d588378c6ffa4754a54a3fd8a98438702773c1f6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 14:25:22 +0000 Subject: [PATCH 021/161] Automatic cargo update to 2025-02-17 (#3889) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3b776124d1bb..87286ca982bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -230,9 +230,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.13" +version = "1.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" +checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" dependencies = [ "shlex", ] @@ -282,9 +282,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.28" +version = "4.5.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" +checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184" dependencies = [ "clap_builder", "clap_derive", @@ -292,9 +292,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.27" +version = "4.5.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9" dependencies = [ "anstream", "anstyle", @@ -471,9 +471,9 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" dependencies = [ "memchr", ] @@ -600,9 +600,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -1004,9 +1004,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" dependencies = [ "adler2", ] @@ -1303,9 +1303,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" +checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88" dependencies = [ "cc", ] @@ -1613,15 +1613,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "stacker" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" +checksum = "1d08feb8f695b465baed819b03c128dc23f57a694510ab1f06c77f763975685e" dependencies = [ "cc", "cfg-if", @@ -1697,9 +1697,9 @@ checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "tempfile" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" +checksum = "a40f762a77d2afa88c2d919489e390a12bdd261ed568e60cfa7e48d4e20f0d33" dependencies = [ "cfg-if", "fastrand", @@ -1845,9 +1845,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.23" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", "serde", @@ -2193,9 +2193,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86e376c75f4f43f44db463cf729e0d3acbf954d13e22c51e26e4c264b4ab545f" +checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" dependencies = [ "memchr", ] From 53b28f2fc4cae53dbd003f981cfed9792bdc565e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 18:00:53 +0000 Subject: [PATCH 022/161] Bump tests/perf/s2n-quic from `a5d8422` to `00e3371` (#3894) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `a5d8422` to `00e3371`.
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index a5d84229dae9..00e33714d74b 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit a5d84229dae984a40ece44add3f418b00342fa01 +Subproject commit 00e33714d74b0646bc18ab65c90a45504a185828 From ac8e0b93d68542b632ff28a888243c6806f72882 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 18 Feb 2025 12:23:49 +0100 Subject: [PATCH 023/161] Adjust PropertyClass of assertions to identify UB (#3860) Anything listed as undefined behavior (UB) at https://doc.rust-lang.org/reference/behavior-considered-undefined.html must also be considered UB by Kani and should not pass under `should_fail`. In preparation of this PR, all occurrences of `PropertyClass` in the code base were audited and, where necessary, adjusted. Also, all uses of `kani::assert` were audited to confirm or adjust them. This resulted in first-time use of the `UnsupportedCheck` hook, which implied fixes to its implementation. Resolves: #3571 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Carolyn Zech --- .../codegen/statement.rs | 44 ++++++++---- .../codegen_cprover_gotoc/overrides/hooks.rs | 51 +++++++++++--- .../src/kani_middle/kani_functions.rs | 2 + .../src/kani_middle/transform/body.rs | 69 +++++++++++-------- .../transform/check_uninit/delayed_ub/mod.rs | 18 +++-- .../kani_middle/transform/check_uninit/mod.rs | 46 ++++++++----- .../transform/check_uninit/ptr_uninit/mod.rs | 6 +- .../src/kani_middle/transform/check_values.rs | 34 ++++----- .../kani_middle/transform/kani_intrinsics.rs | 50 +++----------- .../src/kani_middle/transform/mod.rs | 27 ++++++-- library/kani_core/src/lib.rs | 12 ++++ library/kani_core/src/mem.rs | 19 ++--- .../MemPredicates/adt_with_metadata.expected | 4 ++ .../MemPredicates/adt_with_metadata.rs | 1 - .../MemPredicates/fat_ptr_validity.expected | 5 ++ .../MemPredicates/fat_ptr_validity.rs | 2 - .../MemPredicates/thin_ptr_validity.expected | 5 ++ .../MemPredicates/thin_ptr_validity.rs | 2 - tests/expected/issue-3571/issue_3571.expected | 9 +++ tests/expected/issue-3571/issue_3571.rs | 16 +++++ ...s-padding-enum-diverging-variants.expected | 4 ++ .../access-padding-enum-diverging-variants.rs | 1 - ...ss-padding-enum-multiple-variants.expected | 7 ++ .../access-padding-enum-multiple-variants.rs | 2 - .../access-padding-enum-single-field.expected | 5 ++ .../access-padding-enum-single-field.rs | 1 - ...ccess-padding-enum-single-variant.expected | 4 ++ .../access-padding-enum-single-variant.rs | 1 - ...xpected => access-padding-uninit.expected} | 0 .../copy/expose_padding_via_copy.expected | 4 +- ...xpose_padding_via_copy_convoluted.expected | 4 +- .../expose_padding_via_non_byte_copy.expected | 4 +- .../uninit/copy/read_after_copy.expected | 4 +- .../uninit/delayed-ub/delayed-ub.expected | 20 +++--- tests/expected/uninit/intrinsics/expected | 20 +++--- .../uninit/multiple-instrumentations.expected | 8 +-- tests/expected/uninit/unions.expected | 14 ++-- .../valid-value-checks/constants.expected | 10 +++ .../valid-value-checks}/constants.rs | 3 - .../valid-value-checks/custom_niche.expected | 28 ++++++++ .../valid-value-checks}/custom_niche.rs | 9 --- .../valid-value-checks/maybe_uninit.expected | 5 ++ .../valid-value-checks}/maybe_uninit.rs | 1 - .../valid-value-checks/non_null.expected | 7 ++ .../valid-value-checks}/non_null.rs | 2 - .../valid-value-checks/write_bytes.expected | 5 ++ .../valid-value-checks}/write_bytes.rs | 1 - .../valid-value-checks/write_invalid.expected | 13 ++++ .../valid-value-checks}/write_invalid.rs | 3 - 49 files changed, 398 insertions(+), 214 deletions(-) create mode 100644 tests/expected/MemPredicates/adt_with_metadata.expected rename tests/{kani => expected}/MemPredicates/adt_with_metadata.rs (98%) create mode 100644 tests/expected/MemPredicates/fat_ptr_validity.expected rename tests/{kani => expected}/MemPredicates/fat_ptr_validity.rs (97%) create mode 100644 tests/expected/MemPredicates/thin_ptr_validity.expected rename tests/{kani => expected}/MemPredicates/thin_ptr_validity.rs (96%) create mode 100644 tests/expected/issue-3571/issue_3571.expected create mode 100644 tests/expected/issue-3571/issue_3571.rs create mode 100644 tests/expected/uninit/access-padding-uninit/access-padding-enum-diverging-variants.expected rename tests/{kani/Uninit => expected/uninit/access-padding-uninit}/access-padding-enum-diverging-variants.rs (97%) create mode 100644 tests/expected/uninit/access-padding-uninit/access-padding-enum-multiple-variants.expected rename tests/{kani/Uninit => expected/uninit/access-padding-uninit}/access-padding-enum-multiple-variants.rs (96%) create mode 100644 tests/expected/uninit/access-padding-uninit/access-padding-enum-single-field.expected rename tests/{kani/Uninit => expected/uninit/access-padding-uninit}/access-padding-enum-single-field.rs (97%) create mode 100644 tests/expected/uninit/access-padding-uninit/access-padding-enum-single-variant.expected rename tests/{kani/Uninit => expected/uninit/access-padding-uninit}/access-padding-enum-single-variant.rs (97%) rename tests/expected/uninit/access-padding-uninit/{expected => access-padding-uninit.expected} (100%) create mode 100644 tests/expected/valid-value-checks/constants.expected rename tests/{kani/ValidValues => expected/valid-value-checks}/constants.rs (93%) create mode 100644 tests/expected/valid-value-checks/custom_niche.expected rename tests/{kani/ValidValues => expected/valid-value-checks}/custom_niche.rs (94%) create mode 100644 tests/expected/valid-value-checks/maybe_uninit.expected rename tests/{kani/ValidValues => expected/valid-value-checks}/maybe_uninit.rs (96%) create mode 100644 tests/expected/valid-value-checks/non_null.expected rename tests/{kani/ValidValues => expected/valid-value-checks}/non_null.rs (94%) create mode 100644 tests/expected/valid-value-checks/write_bytes.expected rename tests/{kani/ValidValues => expected/valid-value-checks}/write_bytes.rs (96%) create mode 100644 tests/expected/valid-value-checks/write_invalid.expected rename tests/{kani/ValidValues => expected/valid-value-checks}/write_invalid.rs (94%) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 2d876899f235..813e07d06723 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -251,19 +251,37 @@ impl GotocCtx<'_> { if *expected { r } else { Expr::not(r) } }; - let msg = if let AssertMessage::BoundsCheck { .. } = msg { - // For bounds check the following panic message is generated at runtime: - // "index out of bounds: the length is {len} but the index is {index}", - // but CBMC only accepts static messages so we don't add values to the message. - "index out of bounds: the length is less than or equal to the given index" - } else if let AssertMessage::MisalignedPointerDereference { .. } = msg { - // Misaligned pointer dereference check messages is also a runtime messages. - // Generate a generic one here. - "misaligned pointer dereference: address must be a multiple of its type's \ - alignment" - } else { + let (msg, property_class) = match msg { + AssertMessage::BoundsCheck { .. } => { + // For bounds check the following panic message is generated at runtime: + // "index out of bounds: the length is {len} but the index is {index}", + // but CBMC only accepts static messages so we don't add values to the message. + ( + "index out of bounds: the length is less than or equal to the given index", + PropertyClass::Assertion, + ) + } + AssertMessage::MisalignedPointerDereference { .. } => { + // Misaligned pointer dereference check messages is also a runtime messages. + // Generate a generic one here. + ( + "misaligned pointer dereference: address must be a multiple of its type's \ + alignment", + PropertyClass::SafetyCheck, + ) + } // For all other assert kind we can get the static message. - msg.description().unwrap() + AssertMessage::NullPointerDereference => { + (msg.description().unwrap(), PropertyClass::SafetyCheck) + } + AssertMessage::Overflow { .. } + | AssertMessage::OverflowNeg { .. } + | AssertMessage::DivisionByZero { .. } + | AssertMessage::RemainderByZero { .. } + | AssertMessage::ResumedAfterReturn { .. } + | AssertMessage::ResumedAfterPanic { .. } => { + (msg.description().unwrap(), PropertyClass::Assertion) + } }; let (msg_str, reach_stmt) = @@ -274,7 +292,7 @@ impl GotocCtx<'_> { reach_stmt, self.codegen_assert_assume( cond.cast_to(Type::bool()), - PropertyClass::Assertion, + property_class, &msg_str, loc, ), diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index 73c887b3eeaf..e54a1fc4dd9e 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -151,6 +151,43 @@ impl GotocHook for UnsupportedCheck { unreachable!("{UNEXPECTED_CALL}") } + fn handle( + &self, + gcx: &mut GotocCtx, + _instance: Instance, + mut fargs: Vec, + _assign_to: &Place, + target: Option, + span: Span, + ) -> Stmt { + assert_eq!(fargs.len(), 1); + let msg = fargs.pop().unwrap(); + let msg = gcx.extract_const_message(&msg).unwrap(); + let caller_loc = gcx.codegen_caller_span_stable(span); + if let Some(target) = target { + Stmt::block( + vec![ + gcx.codegen_assert_assume_false( + PropertyClass::UnsupportedConstruct, + &msg, + caller_loc, + ), + Stmt::goto(bb_label(target), caller_loc), + ], + caller_loc, + ) + } else { + gcx.codegen_assert_assume_false(PropertyClass::UnsupportedConstruct, &msg, caller_loc) + } + } +} + +struct SafetyCheck; +impl GotocHook for SafetyCheck { + fn hook_applies(&self, _tcx: TyCtxt, _instance: Instance) -> bool { + unreachable!("{UNEXPECTED_CALL}") + } + fn handle( &self, gcx: &mut GotocCtx, @@ -168,12 +205,7 @@ impl GotocHook for UnsupportedCheck { let caller_loc = gcx.codegen_caller_span_stable(span); Stmt::block( vec![ - gcx.codegen_assert_assume( - cond, - PropertyClass::UnsupportedConstruct, - &msg, - caller_loc, - ), + gcx.codegen_assert_assume(cond, PropertyClass::SafetyCheck, &msg, caller_loc), Stmt::goto(bb_label(target), caller_loc), ], caller_loc, @@ -181,8 +213,8 @@ impl GotocHook for UnsupportedCheck { } } -struct SafetyCheck; -impl GotocHook for SafetyCheck { +struct SafetyCheckNoAssume; +impl GotocHook for SafetyCheckNoAssume { fn hook_applies(&self, _tcx: TyCtxt, _instance: Instance) -> bool { unreachable!("{UNEXPECTED_CALL}") } @@ -204,7 +236,7 @@ impl GotocHook for SafetyCheck { let caller_loc = gcx.codegen_caller_span_stable(span); Stmt::block( vec![ - gcx.codegen_assert_assume(cond, PropertyClass::SafetyCheck, &msg, caller_loc), + gcx.codegen_assert(cond, PropertyClass::SafetyCheck, &msg, caller_loc), Stmt::goto(bb_label(target), caller_loc), ], caller_loc, @@ -738,6 +770,7 @@ pub fn fn_hooks() -> GotocHooks { (KaniHook::Cover, Rc::new(Cover)), (KaniHook::AnyRaw, Rc::new(Nondet)), (KaniHook::SafetyCheck, Rc::new(SafetyCheck)), + (KaniHook::SafetyCheckNoAssume, Rc::new(SafetyCheckNoAssume)), (KaniHook::IsAllocated, Rc::new(IsAllocated)), (KaniHook::PointerObject, Rc::new(PointerObject)), (KaniHook::PointerOffset, Rc::new(PointerOffset)), diff --git a/kani-compiler/src/kani_middle/kani_functions.rs b/kani-compiler/src/kani_middle/kani_functions.rs index 3182ced8fa61..01d237bf0773 100644 --- a/kani-compiler/src/kani_middle/kani_functions.rs +++ b/kani-compiler/src/kani_middle/kani_functions.rs @@ -148,6 +148,8 @@ pub enum KaniHook { PointerOffset, #[strum(serialize = "SafetyCheckHook")] SafetyCheck, + #[strum(serialize = "SafetyCheckNoAssumeHook")] + SafetyCheckNoAssume, #[strum(serialize = "UnsupportedCheckHook")] UnsupportedCheck, #[strum(serialize = "UntrackedDerefHook")] diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index f2072fb21fb1..6f7b59041d12 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -181,23 +181,31 @@ impl MutableBody { check_type: &CheckType, source: &mut SourceInstruction, position: InsertPosition, - value: Local, + value: Option, msg: &str, ) { - assert_eq!( - self.locals[value].ty, - Ty::bool_ty(), - "Expected boolean value as the assert input" - ); let new_bb = self.blocks.len(); let span = source.span(&self.blocks); - let CheckType::Assert(assert_fn) = check_type; + let msg_op = self.new_str_operand(msg, span); + let (assert_fn, args) = match check_type { + CheckType::SafetyCheck(assert_fn) | CheckType::SafetyCheckNoAssume(assert_fn) => { + assert_eq!( + self.locals[value.unwrap()].ty, + Ty::bool_ty(), + "Expected boolean value as the assert input" + ); + (assert_fn, vec![Operand::Move(Place::from(value.unwrap())), msg_op]) + } + CheckType::UnsupportedCheck(assert_fn) => { + assert!(value.is_none()); + (assert_fn, vec![msg_op]) + } + }; let assert_op = Operand::Copy(Place::from(self.new_local(assert_fn.ty(), span, Mutability::Not))); - let msg_op = self.new_str_operand(msg, span); let kind = TerminatorKind::Call { func: assert_op, - args: vec![Operand::Move(Place::from(value)), msg_op], + args, destination: Place { local: self.new_local(Ty::new_tuple(&[]), span, Mutability::Not), projection: vec![], @@ -441,30 +449,35 @@ impl MutableBody { } } -// TODO: Remove this enum, since we now only support kani's assert. #[derive(Clone, Debug)] pub enum CheckType { - /// This is used by default when the `kani` crate is available. - Assert(Instance), + SafetyCheck(Instance), + SafetyCheckNoAssume(Instance), + UnsupportedCheck(Instance), } impl CheckType { - /// This will create the type of check that is available in the current crate, attempting to - /// create a check that generates an assertion following by an assumption of the same assertion. - pub fn new_assert_assume(queries: &QueryDb) -> CheckType { - let fn_def = queries.kani_functions()[&KaniHook::Assert.into()]; - CheckType::Assert(Instance::resolve(fn_def, &GenericArgs(vec![])).unwrap()) - } - - /// This will create the type of check that is available in the current crate, attempting to - /// create a check that generates an assertion, without assuming the condition afterwards. - /// - /// If `kani` crate is available, this will return [CheckType::Assert], and the instance will - /// point to `kani::assert`. Otherwise, we will collect the `core::panic_str` method and return - /// [CheckType::Panic]. - pub fn new_assert(queries: &QueryDb) -> CheckType { - let fn_def = queries.kani_functions()[&KaniHook::Check.into()]; - CheckType::Assert(Instance::resolve(fn_def, &GenericArgs(vec![])).unwrap()) + /// This will create the type of safety check that is available in the current crate, attempting + /// to create a check that generates an assertion following by an assumption of the same + /// assertion. + pub fn new_safety_check_assert_assume(queries: &QueryDb) -> CheckType { + let fn_def = queries.kani_functions()[&KaniHook::SafetyCheck.into()]; + CheckType::SafetyCheck(Instance::resolve(fn_def, &GenericArgs(vec![])).unwrap()) + } + + /// This will create the type of safety check that is available in the current crate, attempting + /// to create a check that generates an assertion, but not following by an assumption. + pub fn new_safety_check_assert_no_assume(queries: &QueryDb) -> CheckType { + let fn_def = queries.kani_functions()[&KaniHook::SafetyCheckNoAssume.into()]; + CheckType::SafetyCheckNoAssume(Instance::resolve(fn_def, &GenericArgs(vec![])).unwrap()) + } + + /// This will create the type of operation-unsupported check that is available in the current + /// crate, attempting to create a check that generates an assertion following by an assumption + /// of the same assertion. + pub fn new_unsupported_check_assert_assume_false(queries: &QueryDb) -> CheckType { + let fn_def = queries.kani_functions()[&KaniHook::UnsupportedCheck.into()]; + CheckType::UnsupportedCheck(Instance::resolve(fn_def, &GenericArgs(vec![])).unwrap()) } } diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/mod.rs b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/mod.rs index 19d625da6c53..15d5af1dced6 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/mod.rs @@ -33,13 +33,22 @@ mod instrumentation_visitor; #[derive(Debug)] pub struct DelayedUbPass { - pub check_type: CheckType, + pub safety_check_type: CheckType, + pub unsupported_check_type: CheckType, pub mem_init_fn_cache: HashMap, } impl DelayedUbPass { - pub fn new(check_type: CheckType, queries: &QueryDb) -> Self { - Self { check_type, mem_init_fn_cache: queries.kani_functions().clone() } + pub fn new( + safety_check_type: CheckType, + unsupported_check_type: CheckType, + queries: &QueryDb, + ) -> Self { + Self { + safety_check_type, + unsupported_check_type, + mem_init_fn_cache: queries.kani_functions().clone(), + } } } @@ -122,7 +131,8 @@ impl GlobalPass for DelayedUbPass { let (instrumentation_added, body) = UninitInstrumenter::run( body, instance, - self.check_type.clone(), + self.safety_check_type.clone(), + self.unsupported_check_type.clone(), &mut self.mem_init_fn_cache, target_finder, ); diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs b/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs index 892c2086c65c..e7178e2a7eb0 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs @@ -70,7 +70,8 @@ const SKIPPED_ITEMS: &[KaniFunction] = &[ /// Instruments the code with checks for uninitialized memory, agnostic to the source of targets. pub struct UninitInstrumenter<'a> { - check_type: CheckType, + safety_check_type: CheckType, + unsupported_check_type: CheckType, /// Used to cache FnDef lookups of injected memory initialization functions. mem_init_fn_cache: &'a mut HashMap, } @@ -80,11 +81,13 @@ impl<'a> UninitInstrumenter<'a> { pub(crate) fn run( body: Body, instance: Instance, - check_type: CheckType, + safety_check_type: CheckType, + unsupported_check_type: CheckType, mem_init_fn_cache: &'a mut HashMap, target_finder: impl TargetFinder, ) -> (bool, Body) { - let mut instrumenter = Self { check_type, mem_init_fn_cache }; + let mut instrumenter = + Self { safety_check_type, unsupported_check_type, mem_init_fn_cache }; let body = MutableBody::from(body); let (changed, new_body) = instrumenter.instrument(body, instance, target_finder); (changed, new_body.into()) @@ -134,12 +137,11 @@ impl<'a> UninitInstrumenter<'a> { source: &mut SourceInstruction, operation: MemoryInitOp, ) { - if let MemoryInitOp::Unsupported { reason } | MemoryInitOp::TriviallyUnsafe { reason } = - &operation - { - // If the operation is unsupported or trivially accesses uninitialized memory, encode - // the check as `assert!(false)`. - self.inject_assert_false(body, source, operation.position(), reason); + if let MemoryInitOp::Unsupported { reason } = &operation { + self.inject_unsupported_check(body, source, operation.position(), reason); + return; + } else if let MemoryInitOp::TriviallyUnsafe { reason } = &operation { + self.inject_safety_check(body, source, operation.position(), reason); return; }; @@ -162,7 +164,7 @@ impl<'a> UninitInstrumenter<'a> { let reason = format!( "Kani currently doesn't support checking memory initialization for pointers to `{pointee_ty}. {reason}", ); - self.inject_assert_false(body, source, operation.position(), &reason); + self.inject_unsupported_check(body, source, operation.position(), &reason); return; } } @@ -284,7 +286,7 @@ impl<'a> UninitInstrumenter<'a> { } PointeeLayout::TraitObject => { let reason = "Kani does not support reasoning about memory initialization of pointers to trait objects."; - self.inject_assert_false(body, source, operation.position(), reason); + self.inject_unsupported_check(body, source, operation.position(), reason); return; } PointeeLayout::Union { .. } => { @@ -292,7 +294,7 @@ impl<'a> UninitInstrumenter<'a> { // TODO: we perhaps need to check that the union at least contains an intersection // of all layouts initialized. let reason = "Interaction between raw pointers and unions is not yet supported."; - self.inject_assert_false(body, source, operation.position(), reason); + self.inject_unsupported_check(body, source, operation.position(), reason); return; } }; @@ -309,10 +311,10 @@ impl<'a> UninitInstrumenter<'a> { _ => unreachable!(), }; body.insert_check( - &self.check_type, + &self.safety_check_type, source, operation.position(), - ret_place.local, + Some(ret_place.local), &format!( "Undefined Behavior: Reading from an uninitialized pointer of type `{operand_ty}`" ), @@ -452,7 +454,7 @@ impl<'a> UninitInstrumenter<'a> { None => { let reason = "Interaction between raw pointers and unions is not yet supported."; - self.inject_assert_false(body, source, operation.position(), reason); + self.inject_unsupported_check(body, source, operation.position(), reason); return; } }; @@ -617,7 +619,7 @@ impl<'a> UninitInstrumenter<'a> { body.insert_bb(BasicBlock { statements, terminator }, source, operation.position()); } - fn inject_assert_false( + fn inject_safety_check( &self, body: &mut MutableBody, source: &mut SourceInstruction, @@ -631,7 +633,17 @@ impl<'a> UninitInstrumenter<'a> { user_ty: None, })); let result = body.insert_assignment(rvalue, source, position); - body.insert_check(&self.check_type, source, position, result, reason); + body.insert_check(&self.safety_check_type, source, position, Some(result), reason); + } + + fn inject_unsupported_check( + &self, + body: &mut MutableBody, + source: &mut SourceInstruction, + position: InsertPosition, + reason: &str, + ) { + body.insert_check(&self.unsupported_check_type, source, position, None, reason); } } diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/mod.rs b/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/mod.rs index b8a8806ec48a..3cba12bf21ee 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/mod.rs @@ -30,7 +30,8 @@ mod uninit_visitor; /// pointers. #[derive(Debug)] pub struct UninitPass { - pub check_type: CheckType, + pub safety_check_type: CheckType, + pub unsupported_check_type: CheckType, pub mem_init_fn_cache: HashMap, } @@ -66,7 +67,8 @@ impl TransformPass for UninitPass { let (instrumentation_added, body) = UninitInstrumenter::run( new_body.into(), instance, - self.check_type.clone(), + self.safety_check_type.clone(), + self.unsupported_check_type.clone(), &mut self.mem_init_fn_cache, CheckUninitVisitor::new(), ); diff --git a/kani-compiler/src/kani_middle/transform/check_values.rs b/kani-compiler/src/kani_middle/transform/check_values.rs index e2c8a153c9a1..8689bf6c59ac 100644 --- a/kani-compiler/src/kani_middle/transform/check_values.rs +++ b/kani-compiler/src/kani_middle/transform/check_values.rs @@ -27,12 +27,12 @@ use stable_mir::abi::{FieldsShape, Scalar, TagEncoding, ValueAbi, VariantsShape, use stable_mir::mir::mono::Instance; use stable_mir::mir::visit::{Location, PlaceContext, PlaceRef}; use stable_mir::mir::{ - AggregateKind, BasicBlockIdx, BinOp, Body, CastKind, ConstOperand, FieldIdx, Local, LocalDecl, - MirVisitor, Mutability, NonDivergingIntrinsic, Operand, Place, ProjectionElem, RawPtrKind, - Rvalue, Statement, StatementKind, Terminator, TerminatorKind, + AggregateKind, BasicBlockIdx, BinOp, Body, CastKind, FieldIdx, Local, LocalDecl, MirVisitor, + Mutability, NonDivergingIntrinsic, Operand, Place, ProjectionElem, RawPtrKind, Rvalue, + Statement, StatementKind, Terminator, TerminatorKind, }; use stable_mir::target::{MachineInfo, MachineSize}; -use stable_mir::ty::{AdtKind, IndexedVal, MirConst, RigidTy, Span, Ty, TyKind, UintTy}; +use stable_mir::ty::{AdtKind, IndexedVal, RigidTy, Span, Ty, TyKind, UintTy}; use std::fmt::Debug; use strum_macros::AsRefStr; use tracing::{debug, trace}; @@ -40,7 +40,8 @@ use tracing::{debug, trace}; /// Instrument the code with checks for invalid values. #[derive(Debug)] pub struct ValidValuePass { - pub check_type: CheckType, + pub safety_check_type: CheckType, + pub unsupported_check_type: CheckType, } impl TransformPass for ValidValuePass { @@ -92,10 +93,10 @@ impl ValidValuePass { let msg = format!("Undefined Behavior: Invalid value of type `{target_ty}`",); body.insert_check( - &self.check_type, + &self.safety_check_type, &mut source, InsertPosition::Before, - result, + Some(result), &msg, ); } @@ -106,10 +107,10 @@ impl ValidValuePass { let msg = format!("Undefined Behavior: Invalid value of type `{pointee_ty}`",); body.insert_check( - &self.check_type, + &self.safety_check_type, &mut source, InsertPosition::Before, - result, + Some(result), &msg, ); } @@ -130,14 +131,13 @@ impl ValidValuePass { source: &mut SourceInstruction, reason: &str, ) { - let span = source.span(body.blocks()); - let rvalue = Rvalue::Use(Operand::Constant(ConstOperand { - const_: MirConst::from_bool(false), - span, - user_ty: None, - })); - let result = body.insert_assignment(rvalue, source, InsertPosition::Before); - body.insert_check(&self.check_type, source, InsertPosition::Before, result, reason); + body.insert_check( + &self.unsupported_check_type, + source, + InsertPosition::Before, + None, + reason, + ); } } diff --git a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs index d18b4b059063..343fc2fe0c92 100644 --- a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs +++ b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs @@ -39,7 +39,7 @@ use tracing::{debug, trace}; /// Generate the body for a few Kani intrinsics. #[derive(Debug)] pub struct IntrinsicGeneratorPass { - check_type: CheckType, + unsupported_check_type: CheckType, /// Used to cache FnDef lookups for models and Kani intrinsics. kani_defs: HashMap, /// Whether the user enabled uninitialized memory checks when they invoked Kani. @@ -86,11 +86,11 @@ impl TransformPass for IntrinsicGeneratorPass { } impl IntrinsicGeneratorPass { - pub fn new(check_type: CheckType, queries: &QueryDb) -> Self { + pub fn new(unsupported_check_type: CheckType, queries: &QueryDb) -> Self { let enable_uninit = queries.args().ub_check.contains(&ExtraChecks::Uninit); let kani_defs = queries.kani_functions().clone(); debug!(?kani_defs, ?enable_uninit, "IntrinsicGeneratorPass::new"); - IntrinsicGeneratorPass { check_type, enable_uninit, kani_defs } + IntrinsicGeneratorPass { unsupported_check_type, enable_uninit, kani_defs } } /// Generate the body for valid value. Which should be something like: @@ -151,21 +151,14 @@ impl IntrinsicGeneratorPass { } Err(msg) => { // We failed to retrieve all the valid ranges. - let rvalue = Rvalue::Use(Operand::Constant(ConstOperand { - const_: MirConst::from_bool(false), - span, - user_ty: None, - })); - let result = - new_body.insert_assignment(rvalue, &mut terminator, InsertPosition::Before); let reason = format!( "Kani currently doesn't support checking validity of `{target_ty}`. {msg}" ); new_body.insert_check( - &self.check_type, + &self.unsupported_check_type, &mut terminator, InsertPosition::Before, - result, + None, &reason, ); } @@ -314,39 +307,25 @@ impl IntrinsicGeneratorPass { ); } PointeeLayout::TraitObject => { - let rvalue = Rvalue::Use(Operand::Constant(ConstOperand { - const_: MirConst::from_bool(false), - span: source.span(new_body.blocks()), - user_ty: None, - })); - let result = - new_body.insert_assignment(rvalue, &mut source, InsertPosition::Before); let reason: &str = "Kani does not support reasoning about memory initialization of pointers to trait objects."; new_body.insert_check( - &self.check_type, + &self.unsupported_check_type, &mut source, InsertPosition::Before, - result, + None, &reason, ); } PointeeLayout::Union { .. } => { - let rvalue = Rvalue::Use(Operand::Constant(ConstOperand { - const_: MirConst::from_bool(false), - span: source.span(new_body.blocks()), - user_ty: None, - })); - let result = - new_body.insert_assignment(rvalue, &mut source, InsertPosition::Before); let reason: &str = "Kani does not yet support using initialization predicates on unions."; new_body.insert_check( - &self.check_type, + &self.unsupported_check_type, &mut source, InsertPosition::Before, - result, + None, &reason, ); } @@ -354,21 +333,14 @@ impl IntrinsicGeneratorPass { } Err(reason) => { // We failed to retrieve the type layout. - let rvalue = Rvalue::Use(Operand::Constant(ConstOperand { - const_: MirConst::from_bool(false), - span: source.span(new_body.blocks()), - user_ty: None, - })); - let result = - new_body.insert_assignment(rvalue, &mut source, InsertPosition::Before); let reason = format!( "Kani currently doesn't support checking memory initialization for pointers to `{pointee_ty}. {reason}", ); new_body.insert_check( - &self.check_type, + &self.unsupported_check_type, &mut source, InsertPosition::Before, - result, + None, &reason, ); } diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index d1eac474b47a..fbe9bebf42f8 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -72,7 +72,8 @@ impl BodyTransformation { inst_passes: vec![], cache: Default::default(), }; - let check_type = CheckType::new_assert_assume(queries); + let safety_check_type = CheckType::new_safety_check_assert_assume(queries); + let unsupported_check_type = CheckType::new_unsupported_check_assert_assume_false(queries); // This has to come first, since creating harnesses affects later stubbing and contract passes. transformer.add_pass(queries, AutomaticHarnessPass::new(unit, queries)); transformer.add_pass(queries, FnStubPass::new(&unit.stubs)); @@ -81,7 +82,13 @@ impl BodyTransformation { // This has to come after the contract pass since we want this to only replace the closure // body that is relevant for this harness. transformer.add_pass(queries, AnyModifiesPass::new(tcx, queries, &unit)); - transformer.add_pass(queries, ValidValuePass { check_type: check_type.clone() }); + transformer.add_pass( + queries, + ValidValuePass { + safety_check_type: safety_check_type.clone(), + unsupported_check_type: unsupported_check_type.clone(), + }, + ); // Putting `UninitPass` after `ValidValuePass` makes sure that the code generated by // `UninitPass` does not get unnecessarily instrumented by valid value checks. However, it // would also make sense to check that the values are initialized before checking their @@ -91,11 +98,13 @@ impl BodyTransformation { queries, UninitPass { // Since this uses demonic non-determinism under the hood, should not assume the assertion. - check_type: CheckType::new_assert(queries), + safety_check_type: CheckType::new_safety_check_assert_no_assume(queries), + unsupported_check_type: unsupported_check_type.clone(), mem_init_fn_cache: queries.kani_functions().clone(), }, ); - transformer.add_pass(queries, IntrinsicGeneratorPass::new(check_type, &queries)); + transformer + .add_pass(queries, IntrinsicGeneratorPass::new(unsupported_check_type, &queries)); transformer.add_pass(queries, LoopContractPass::new(tcx, queries, &unit)); transformer.add_pass(queries, RustcIntrinsicsPass::new(&queries)); transformer @@ -198,8 +207,14 @@ pub struct GlobalPasses { impl GlobalPasses { pub fn new(queries: &QueryDb, tcx: TyCtxt) -> Self { let mut global_passes = GlobalPasses { global_passes: vec![] }; - global_passes - .add_global_pass(queries, DelayedUbPass::new(CheckType::new_assert(queries), queries)); + global_passes.add_global_pass( + queries, + DelayedUbPass::new( + CheckType::new_safety_check_assert_assume(queries), + CheckType::new_unsupported_check_assert_assume_false(queries), + queries, + ), + ); global_passes.add_global_pass(queries, DumpMirPass::new(tcx)); global_passes } diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index d3940160a91a..51b243c9ad33 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -358,6 +358,18 @@ macro_rules! kani_intrinsics { assert!(cond, "Safety check failed: {msg}"); } + #[doc(hidden)] + #[allow(dead_code)] + #[kanitool::fn_marker = "SafetyCheckNoAssumeHook"] + #[inline(never)] + pub(crate) fn safety_check_no_assume(cond: bool, msg: &'static str) { + #[cfg(not(feature = "concrete_playback"))] + return kani_intrinsic(); + + #[cfg(feature = "concrete_playback")] + assert!(cond, "Safety check failed: {msg}"); + } + /// This should indicate that Kani does not support a certain operation. #[doc(hidden)] #[allow(dead_code)] diff --git a/library/kani_core/src/mem.rs b/library/kani_core/src/mem.rs index baf90ed98158..1ab8a5ead993 100644 --- a/library/kani_core/src/mem.rs +++ b/library/kani_core/src/mem.rs @@ -178,10 +178,11 @@ macro_rules! kani_mem { // stubbed. // We first assert that the data_ptr let data_ptr = ptr as *const (); - super::assert( - unsafe { is_allocated(data_ptr, 0) }, - "Kani does not support reasoning about pointer to unallocated memory", - ); + if !unsafe { is_allocated(data_ptr, 0) } { + crate::kani::unsupported( + "Kani does not support reasoning about pointer to unallocated memory", + ); + } unsafe { is_allocated(data_ptr, sz) } } } @@ -248,11 +249,11 @@ macro_rules! kani_mem { pub fn same_allocation(addr1: *const (), addr2: *const ()) -> bool { let obj1 = crate::kani::mem::pointer_object(addr1); (obj1 == crate::kani::mem::pointer_object(addr2)) && { - // TODO(3571): This should be a unsupported check - crate::kani::assert( - unsafe { is_allocated(addr1, 0) || is_allocated(addr2, 0) }, - "Kani does not support reasoning about pointer to unallocated memory", - ); + if !unsafe { is_allocated(addr1, 0) || is_allocated(addr2, 0) } { + crate::kani::unsupported( + "Kani does not support reasoning about pointer to unallocated memory", + ); + } unsafe { is_allocated(addr1, 0) && is_allocated(addr2, 0) } } } diff --git a/tests/expected/MemPredicates/adt_with_metadata.expected b/tests/expected/MemPredicates/adt_with_metadata.expected new file mode 100644 index 000000000000..b3d971a40e61 --- /dev/null +++ b/tests/expected/MemPredicates/adt_with_metadata.expected @@ -0,0 +1,4 @@ +Failed Checks: Kani does not support reasoning about pointer to unallocated memory + +Verification failed for - invalid_access::check_invalid_dyn_ptr +Complete - 3 successfully verified harnesses, 1 failures, 4 total. diff --git a/tests/kani/MemPredicates/adt_with_metadata.rs b/tests/expected/MemPredicates/adt_with_metadata.rs similarity index 98% rename from tests/kani/MemPredicates/adt_with_metadata.rs rename to tests/expected/MemPredicates/adt_with_metadata.rs index aa536b26279f..9ec0a3787964 100644 --- a/tests/kani/MemPredicates/adt_with_metadata.rs +++ b/tests/expected/MemPredicates/adt_with_metadata.rs @@ -42,7 +42,6 @@ mod invalid_access { use super::*; use std::ptr; #[kani::proof] - #[kani::should_panic] pub fn check_invalid_dyn_ptr() { unsafe fn new_dead_ptr(val: T) -> *const T { let local = val; diff --git a/tests/expected/MemPredicates/fat_ptr_validity.expected b/tests/expected/MemPredicates/fat_ptr_validity.expected new file mode 100644 index 000000000000..b57ca4986e60 --- /dev/null +++ b/tests/expected/MemPredicates/fat_ptr_validity.expected @@ -0,0 +1,5 @@ +Failed Checks: Kani does not support reasoning about pointer to unallocated memory + +Verification failed for - invalid_access::check_invalid_slice_ptr +Verification failed for - invalid_access::check_invalid_dyn_ptr +Complete - 4 successfully verified harnesses, 2 failures, 6 total. diff --git a/tests/kani/MemPredicates/fat_ptr_validity.rs b/tests/expected/MemPredicates/fat_ptr_validity.rs similarity index 97% rename from tests/kani/MemPredicates/fat_ptr_validity.rs rename to tests/expected/MemPredicates/fat_ptr_validity.rs index c4f037f3a646..f39392b62c5c 100644 --- a/tests/kani/MemPredicates/fat_ptr_validity.rs +++ b/tests/expected/MemPredicates/fat_ptr_validity.rs @@ -38,14 +38,12 @@ mod valid_access { mod invalid_access { use super::*; #[kani::proof] - #[kani::should_panic] pub fn check_invalid_dyn_ptr() { let raw_ptr: *const dyn PartialEq = unsafe { new_dead_ptr::(0) }; assert!(can_dereference(raw_ptr)); } #[kani::proof] - #[kani::should_panic] pub fn check_invalid_slice_ptr() { let raw_ptr: *const [char] = unsafe { new_dead_ptr::<[char; 2]>(['a', 'b']) }; assert!(can_dereference(raw_ptr)); diff --git a/tests/expected/MemPredicates/thin_ptr_validity.expected b/tests/expected/MemPredicates/thin_ptr_validity.expected new file mode 100644 index 000000000000..54245a84a491 --- /dev/null +++ b/tests/expected/MemPredicates/thin_ptr_validity.expected @@ -0,0 +1,5 @@ +Failed Checks: Kani does not support reasoning about pointer to unallocated memory + +Verification failed for - invalid_access::check_invalid_array +Verification failed for - invalid_access::check_invalid_ptr +Complete - 3 successfully verified harnesses, 2 failures, 5 total. diff --git a/tests/kani/MemPredicates/thin_ptr_validity.rs b/tests/expected/MemPredicates/thin_ptr_validity.rs similarity index 96% rename from tests/kani/MemPredicates/thin_ptr_validity.rs rename to tests/expected/MemPredicates/thin_ptr_validity.rs index 553c5beab9f8..519a07297192 100644 --- a/tests/kani/MemPredicates/thin_ptr_validity.rs +++ b/tests/expected/MemPredicates/thin_ptr_validity.rs @@ -32,14 +32,12 @@ mod valid_access { mod invalid_access { use super::*; #[kani::proof] - #[kani::should_panic] pub fn check_invalid_ptr() { let raw_ptr = unsafe { new_dead_ptr::(0) }; assert!(!can_dereference(raw_ptr)); } #[kani::proof] - #[kani::should_panic] pub fn check_invalid_array() { let raw_ptr = unsafe { new_dead_ptr::<[char; 2]>(['a', 'b']) }; assert!(can_dereference(raw_ptr)); diff --git a/tests/expected/issue-3571/issue_3571.expected b/tests/expected/issue-3571/issue_3571.expected new file mode 100644 index 000000000000..bed39a66b7af --- /dev/null +++ b/tests/expected/issue-3571/issue_3571.expected @@ -0,0 +1,9 @@ +Failed Checks: misaligned pointer dereference: address must be a multiple of its type's alignment + +Failed Checks: null pointer dereference occurred + +VERIFICATION:- FAILED (encountered failures other than panics, which were unexpected) + +Verification failed for - rust_ub_should_fail +Verification failed for - rust_ub_fails +Complete - 0 successfully verified harnesses, 2 failures, 2 total. diff --git a/tests/expected/issue-3571/issue_3571.rs b/tests/expected/issue-3571/issue_3571.rs new file mode 100644 index 000000000000..c4d55f39b4e4 --- /dev/null +++ b/tests/expected/issue-3571/issue_3571.rs @@ -0,0 +1,16 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#[kani::proof] +#[kani::should_panic] +pub fn rust_ub_fails() { + let ptr = 0 as *const u32; + let _invalid_ref = unsafe { &*ptr }; +} + +#[kani::proof] +#[kani::should_panic] +pub fn rust_ub_should_fail() { + let ptr = 10 as *const u32; + let _invalid_read = unsafe { *ptr }; +} diff --git a/tests/expected/uninit/access-padding-uninit/access-padding-enum-diverging-variants.expected b/tests/expected/uninit/access-padding-uninit/access-padding-enum-diverging-variants.expected new file mode 100644 index 000000000000..89a3572de316 --- /dev/null +++ b/tests/expected/uninit/access-padding-uninit/access-padding-enum-diverging-variants.expected @@ -0,0 +1,4 @@ +Failed Checks: Kani currently doesn't support checking memory initialization for pointers to `E1. Cannot determine layout for an Enum of type E1, as it has multiple variants that have different padding. + +Verification failed for - access_padding_unsupported +Complete - 1 successfully verified harnesses, 1 failures, 2 total. diff --git a/tests/kani/Uninit/access-padding-enum-diverging-variants.rs b/tests/expected/uninit/access-padding-uninit/access-padding-enum-diverging-variants.rs similarity index 97% rename from tests/kani/Uninit/access-padding-enum-diverging-variants.rs rename to tests/expected/uninit/access-padding-uninit/access-padding-enum-diverging-variants.rs index fae491c40622..6f41961acb13 100644 --- a/tests/kani/Uninit/access-padding-enum-diverging-variants.rs +++ b/tests/expected/uninit/access-padding-uninit/access-padding-enum-diverging-variants.rs @@ -21,7 +21,6 @@ enum E2 { } #[kani::proof] -#[kani::should_panic] fn access_padding_unsupported() { let s = E1::A(0, 0); let ptr: *const u8 = addr_of!(s) as *const u8; diff --git a/tests/expected/uninit/access-padding-uninit/access-padding-enum-multiple-variants.expected b/tests/expected/uninit/access-padding-uninit/access-padding-enum-multiple-variants.expected new file mode 100644 index 000000000000..29fa17a972f5 --- /dev/null +++ b/tests/expected/uninit/access-padding-uninit/access-padding-enum-multiple-variants.expected @@ -0,0 +1,7 @@ +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const u8` + +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const u8` + +Verification failed for - access_padding_uninit_b +Verification failed for - access_padding_uninit_a +Complete - 2 successfully verified harnesses, 2 failures, 4 total. diff --git a/tests/kani/Uninit/access-padding-enum-multiple-variants.rs b/tests/expected/uninit/access-padding-uninit/access-padding-enum-multiple-variants.rs similarity index 96% rename from tests/kani/Uninit/access-padding-enum-multiple-variants.rs rename to tests/expected/uninit/access-padding-uninit/access-padding-enum-multiple-variants.rs index dd6942252cb2..11c70dd7cd92 100644 --- a/tests/kani/Uninit/access-padding-enum-multiple-variants.rs +++ b/tests/expected/uninit/access-padding-uninit/access-padding-enum-multiple-variants.rs @@ -33,7 +33,6 @@ fn access_padding_init_b() { } #[kani::proof] -#[kani::should_panic] fn access_padding_uninit_a() { let s = E::A(0, 0); let ptr: *const u8 = addr_of!(s) as *const u8; @@ -41,7 +40,6 @@ fn access_padding_uninit_a() { } #[kani::proof] -#[kani::should_panic] fn access_padding_uninit_b() { let s = E::A(0, 0); let ptr: *const u8 = addr_of!(s) as *const u8; diff --git a/tests/expected/uninit/access-padding-uninit/access-padding-enum-single-field.expected b/tests/expected/uninit/access-padding-uninit/access-padding-enum-single-field.expected new file mode 100644 index 000000000000..caab803ad09a --- /dev/null +++ b/tests/expected/uninit/access-padding-uninit/access-padding-enum-single-field.expected @@ -0,0 +1,5 @@ +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const u8` +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const u8` + +Verification failed for - access_padding_uninit +Complete - 1 successfully verified harnesses, 1 failures, 2 total. diff --git a/tests/kani/Uninit/access-padding-enum-single-field.rs b/tests/expected/uninit/access-padding-uninit/access-padding-enum-single-field.rs similarity index 97% rename from tests/kani/Uninit/access-padding-enum-single-field.rs rename to tests/expected/uninit/access-padding-uninit/access-padding-enum-single-field.rs index 63f7f6043905..da1f2a202364 100644 --- a/tests/kani/Uninit/access-padding-enum-single-field.rs +++ b/tests/expected/uninit/access-padding-uninit/access-padding-enum-single-field.rs @@ -25,7 +25,6 @@ fn access_padding_init() { } #[kani::proof] -#[kani::should_panic] fn access_padding_uninit() { let s = E::A(0); let ptr: *const u8 = addr_of!(s) as *const u8; diff --git a/tests/expected/uninit/access-padding-uninit/access-padding-enum-single-variant.expected b/tests/expected/uninit/access-padding-uninit/access-padding-enum-single-variant.expected new file mode 100644 index 000000000000..fa661dfbe329 --- /dev/null +++ b/tests/expected/uninit/access-padding-uninit/access-padding-enum-single-variant.expected @@ -0,0 +1,4 @@ +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const u8` + +Verification failed for - access_padding_uninit +Complete - 1 successfully verified harnesses, 1 failures, 2 total. diff --git a/tests/kani/Uninit/access-padding-enum-single-variant.rs b/tests/expected/uninit/access-padding-uninit/access-padding-enum-single-variant.rs similarity index 97% rename from tests/kani/Uninit/access-padding-enum-single-variant.rs rename to tests/expected/uninit/access-padding-uninit/access-padding-enum-single-variant.rs index bb87d36d26c8..9946fc83cee9 100644 --- a/tests/kani/Uninit/access-padding-enum-single-variant.rs +++ b/tests/expected/uninit/access-padding-uninit/access-padding-enum-single-variant.rs @@ -24,7 +24,6 @@ fn access_padding_init() { } #[kani::proof] -#[kani::should_panic] fn access_padding_uninit() { let s = E::A(0, 0); let ptr: *const u8 = addr_of!(s) as *const u8; diff --git a/tests/expected/uninit/access-padding-uninit/expected b/tests/expected/uninit/access-padding-uninit/access-padding-uninit.expected similarity index 100% rename from tests/expected/uninit/access-padding-uninit/expected rename to tests/expected/uninit/access-padding-uninit/access-padding-uninit.expected diff --git a/tests/expected/uninit/copy/expose_padding_via_copy.expected b/tests/expected/uninit/copy/expose_padding_via_copy.expected index 83d8badc8bf5..28686a566df4 100644 --- a/tests/expected/uninit/copy/expose_padding_via_copy.expected +++ b/tests/expected/uninit/copy/expose_padding_via_copy.expected @@ -1,8 +1,8 @@ -std::ptr::read::.assertion.1\ +std::ptr::read::.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `*const u64`"\ -std::ptr::read::.assertion.2\ +std::ptr::read::.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u64`"\ diff --git a/tests/expected/uninit/copy/expose_padding_via_copy_convoluted.expected b/tests/expected/uninit/copy/expose_padding_via_copy_convoluted.expected index cbe7ec97cb7b..b9cdd6137592 100644 --- a/tests/expected/uninit/copy/expose_padding_via_copy_convoluted.expected +++ b/tests/expected/uninit/copy/expose_padding_via_copy_convoluted.expected @@ -1,8 +1,8 @@ -std::ptr::read::.assertion.1\ +std::ptr::read::.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `*const u64`"\ -std::ptr::read::.assertion.2\ +std::ptr::read::.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u64`"\ diff --git a/tests/expected/uninit/copy/expose_padding_via_non_byte_copy.expected b/tests/expected/uninit/copy/expose_padding_via_non_byte_copy.expected index 3fc86e45a46e..d3b1db9bd498 100644 --- a/tests/expected/uninit/copy/expose_padding_via_non_byte_copy.expected +++ b/tests/expected/uninit/copy/expose_padding_via_non_byte_copy.expected @@ -1,8 +1,8 @@ -std::ptr::read::.assertion.1\ +std::ptr::read::.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `*const u64`"\ -std::ptr::read::.assertion.2\ +std::ptr::read::.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u64`"\ diff --git a/tests/expected/uninit/copy/read_after_copy.expected b/tests/expected/uninit/copy/read_after_copy.expected index 56a3460a1d7b..c5d133d787b9 100644 --- a/tests/expected/uninit/copy/read_after_copy.expected +++ b/tests/expected/uninit/copy/read_after_copy.expected @@ -1,8 +1,8 @@ -std::ptr::read::.assertion.1\ +std::ptr::read::.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `*const u64`"\ -std::ptr::read::.assertion.2\ +std::ptr::read::.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u64`"\ diff --git a/tests/expected/uninit/delayed-ub/delayed-ub.expected b/tests/expected/uninit/delayed-ub/delayed-ub.expected index f062a30cf6b0..d998801c2ca7 100644 --- a/tests/expected/uninit/delayed-ub/delayed-ub.expected +++ b/tests/expected/uninit/delayed-ub/delayed-ub.expected @@ -1,40 +1,40 @@ -delayed_ub_trigger_copy.assertion.\ +delayed_ub_trigger_copy.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`"\ -delayed_ub_structs.assertion.\ +delayed_ub_structs.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `U`" -delayed_ub_double_copy.assertion.\ +delayed_ub_double_copy.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`"\ -delayed_ub_copy.assertion.\ +delayed_ub_copy.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`" -delayed_ub_closure_capture_laundered.assertion.\ +delayed_ub_closure_capture_laundered.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`" -delayed_ub_closure_laundered.assertion.\ +delayed_ub_closure_laundered.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`" -delayed_ub_laundered.assertion.\ +delayed_ub_laundered.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`" -delayed_ub_static.assertion.\ +delayed_ub_static.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`" -delayed_ub_transmute.assertion.\ +delayed_ub_transmute.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`" -delayed_ub.assertion.\ +delayed_ub.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`" diff --git a/tests/expected/uninit/intrinsics/expected b/tests/expected/uninit/intrinsics/expected index b40fe6714ba8..d19d694df75e 100644 --- a/tests/expected/uninit/intrinsics/expected +++ b/tests/expected/uninit/intrinsics/expected @@ -1,36 +1,32 @@ -std::ptr::write::>.assertion.1\ +std::ptr::read::>.unsupported_construct.\ - Status: FAILURE\ - Description: "Interaction between raw pointers and unions is not yet supported." -std::ptr::write::>.assertion.1\ - - Status: FAILURE\ - - Description: "Interaction between raw pointers and unions is not yet supported."\ - -check_typed_swap_nonoverlapping.assertion.1\ +check_typed_swap_nonoverlapping.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `*mut u8`" -check_typed_swap_nonoverlapping.assertion.2\ +check_typed_swap_nonoverlapping.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `*mut u8`" -check_volatile_load.assertion.1\ +check_volatile_load.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `*const u8`" -check_compare_bytes.assertion.1\ +check_compare_bytes.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `*const u8`" -check_compare_bytes.assertion.2\ +check_compare_bytes.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `*const u8`" -std::ptr::read::.assertion.1\ +std::ptr::read::.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `*const u8`" -std::ptr::read::.assertion.2\ +std::ptr::read::.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u8`" diff --git a/tests/expected/uninit/multiple-instrumentations.expected b/tests/expected/uninit/multiple-instrumentations.expected index 0f503b624ca4..82f37b22ed0d 100644 --- a/tests/expected/uninit/multiple-instrumentations.expected +++ b/tests/expected/uninit/multiple-instrumentations.expected @@ -1,16 +1,16 @@ -multiple_instrumentations_different_vars.assertion\ +multiple_instrumentations_different_vars.safety_check\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`" -multiple_instrumentations_different_vars.assertion\ +multiple_instrumentations_different_vars.safety_check\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u64`" -multiple_instrumentations.assertion\ +multiple_instrumentations.safety_check\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`" -multiple_instrumentations.assertion\ +multiple_instrumentations.safety_check\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`" diff --git a/tests/expected/uninit/unions.expected b/tests/expected/uninit/unions.expected index ca7f777c4065..cf40fbb608df 100644 --- a/tests/expected/uninit/unions.expected +++ b/tests/expected/uninit/unions.expected @@ -1,28 +1,28 @@ -union_update_should_fail.assertion.1\ +union_update_should_fail.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u32`" -union_complex_subfields_should_fail.assertion.1\ +union_complex_subfields_should_fail.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u16`" -basic_union_should_fail.assertion.1\ +basic_union_should_fail.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u32`" -cross_function_union_should_fail::helper.assertion.1\ +cross_function_union_should_fail::helper.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u32`" -cross_function_multi_union_should_fail::helper.assertion.1\ +cross_function_multi_union_should_fail::helper.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u32`" -multi_cross_function_union_should_fail::sub_helper.assertion.1\ +multi_cross_function_union_should_fail::sub_helper.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u32`" -basic_multifield_union_should_fail.assertion.7\ +basic_multifield_union_should_fail.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `u128`" diff --git a/tests/expected/valid-value-checks/constants.expected b/tests/expected/valid-value-checks/constants.expected new file mode 100644 index 000000000000..639e193bc561 --- /dev/null +++ b/tests/expected/valid-value-checks/constants.expected @@ -0,0 +1,10 @@ +Failed Checks: Undefined Behavior: Invalid value of type `[char; 2]` + +Failed Checks: Undefined Behavior: Invalid value of type `char` + +Failed Checks: Undefined Behavior: Invalid value of type `bool` + +Verification failed for - cast_to_invalid_offset +Verification failed for - cast_to_invalid_char +Verification failed for - transmute_invalid_bool +Complete - 3 successfully verified harnesses, 3 failures, 6 total. diff --git a/tests/kani/ValidValues/constants.rs b/tests/expected/valid-value-checks/constants.rs similarity index 93% rename from tests/kani/ValidValues/constants.rs rename to tests/expected/valid-value-checks/constants.rs index 5230e6e5e6cb..aa7aba6fe079 100644 --- a/tests/kani/ValidValues/constants.rs +++ b/tests/expected/valid-value-checks/constants.rs @@ -21,19 +21,16 @@ fn cast_to_valid_offset() { } #[kani::proof] -#[kani::should_panic] fn transmute_invalid_bool() { let _b = unsafe { std::mem::transmute::(2) }; } #[kani::proof] -#[kani::should_panic] fn cast_to_invalid_char() { let _c = unsafe { *(&u32::MAX as *const u32 as *const char) }; } #[kani::proof] -#[kani::should_panic] fn cast_to_invalid_offset() { let val = [100u32, u32::MAX]; let _c = unsafe { *(&val as *const [u32; 2] as *const [char; 2]) }; diff --git a/tests/expected/valid-value-checks/custom_niche.expected b/tests/expected/valid-value-checks/custom_niche.expected new file mode 100644 index 000000000000..3d78563ef8b1 --- /dev/null +++ b/tests/expected/valid-value-checks/custom_niche.expected @@ -0,0 +1,28 @@ +Failed Checks: Undefined Behavior: Invalid value of type `Rating` + +Failed Checks: Kani currently doesn't support checking validity of `copy_nonoverlapping` for `*const Rating` + +Failed Checks: Kani currently doesn't support checking validity of `copy_nonoverlapping` for `*const Rating` + +Failed Checks: Undefined Behavior: Invalid value of type `Rating` + +Failed Checks: Undefined Behavior: Invalid value of type `Rating` + +Failed Checks: Undefined Behavior: Invalid value of type `Rating` + +Failed Checks: Undefined Behavior: Invalid value of type `Rating` + +Failed Checks: Undefined Behavior: Invalid value of type `Rating` + +Failed Checks: Undefined Behavior: Invalid value of type `Rating` + +Verification failed for - check_invalid_increment +Verification failed for - check_copy_nonoverlap_ub +Verification failed for - check_copy_nonoverlap +Verification failed for - check_invalid_transmute_copy +Verification failed for - check_invalid_transmute +Verification failed for - check_invalid_dereference +Verification failed for - check_new_with_ub_limits +Verification failed for - check_unchecked_new_ub +Verification failed for - check_new_with_ub +Complete - 2 successfully verified harnesses, 9 failures, 11 total. diff --git a/tests/kani/ValidValues/custom_niche.rs b/tests/expected/valid-value-checks/custom_niche.rs similarity index 94% rename from tests/kani/ValidValues/custom_niche.rs rename to tests/expected/valid-value-checks/custom_niche.rs index b282fec3c645..c27bc6fdc057 100644 --- a/tests/kani/ValidValues/custom_niche.rs +++ b/tests/expected/valid-value-checks/custom_niche.rs @@ -38,41 +38,35 @@ impl Rating { } #[kani::proof] -#[kani::should_panic] pub fn check_new_with_ub() { assert_eq!(Rating::new(10), None); } #[kani::proof] -#[kani::should_panic] pub fn check_unchecked_new_ub() { let val = kani::any(); assert_eq!(unsafe { Rating::new_unchecked(val).stars }, val); } #[kani::proof] -#[kani::should_panic] pub fn check_new_with_ub_limits() { let stars = kani::any_where(|s: &u8| *s == 0 || *s > 5); let _ = Rating::new(stars); } #[kani::proof] -#[kani::should_panic] pub fn check_invalid_dereference() { let any: u8 = kani::any(); let _rating: Rating = unsafe { *(&any as *const _ as *const _) }; } #[kani::proof] -#[kani::should_panic] pub fn check_invalid_transmute() { let any: u8 = kani::any(); let _rating: Rating = unsafe { mem::transmute(any) }; } #[kani::proof] -#[kani::should_panic] pub fn check_invalid_transmute_copy() { let any: u8 = kani::any(); let _rating: Rating = unsafe { mem::transmute_copy(&any) }; @@ -82,7 +76,6 @@ pub fn check_invalid_transmute_copy() { /// /// FIX-ME: This is not supported today, and we fail due to unsupported check. #[kani::proof] -#[kani::should_panic] pub fn check_copy_nonoverlap() { let stars = kani::any_where(|s: &u8| *s == 0 || *s > 5); let mut rating: Rating = kani::any(); @@ -90,7 +83,6 @@ pub fn check_copy_nonoverlap() { } #[kani::proof] -#[kani::should_panic] pub fn check_copy_nonoverlap_ub() { let any: u8 = kani::any(); let mut rating: Rating = kani::any(); @@ -98,7 +90,6 @@ pub fn check_copy_nonoverlap_ub() { } #[kani::proof] -#[kani::should_panic] pub fn check_invalid_increment() { let mut orig: Rating = kani::any(); unsafe { orig.stars += 1 }; diff --git a/tests/expected/valid-value-checks/maybe_uninit.expected b/tests/expected/valid-value-checks/maybe_uninit.expected new file mode 100644 index 000000000000..8264876dc4f7 --- /dev/null +++ b/tests/expected/valid-value-checks/maybe_uninit.expected @@ -0,0 +1,5 @@ + ** 1 of 14 failed (1 unreachable)\ +Failed Checks: Undefined Behavior: Invalid value of type `std::num::NonZero` + +Verification failed for - check_invalid_zeroed +Complete - 1 successfully verified harnesses, 1 failures, 2 total. diff --git a/tests/kani/ValidValues/maybe_uninit.rs b/tests/expected/valid-value-checks/maybe_uninit.rs similarity index 96% rename from tests/kani/ValidValues/maybe_uninit.rs rename to tests/expected/valid-value-checks/maybe_uninit.rs index 620ad8ef5ba3..926277a744dc 100644 --- a/tests/kani/ValidValues/maybe_uninit.rs +++ b/tests/expected/valid-value-checks/maybe_uninit.rs @@ -14,7 +14,6 @@ pub fn check_valid_zeroed() { } #[kani::proof] -#[kani::should_panic] pub fn check_invalid_zeroed() { let maybe = MaybeUninit::zeroed(); let _val: NonZeroI64 = unsafe { maybe.assume_init() }; diff --git a/tests/expected/valid-value-checks/non_null.expected b/tests/expected/valid-value-checks/non_null.expected new file mode 100644 index 000000000000..bc8572b879c6 --- /dev/null +++ b/tests/expected/valid-value-checks/non_null.expected @@ -0,0 +1,7 @@ +Failed Checks: Undefined Behavior: Invalid value of type `std::ptr::NonNull` + +Failed Checks: Undefined Behavior: Invalid value of type `std::ptr::NonNull` + +Verification failed for - check_invalid_value_cfg +Verification failed for - check_invalid_value +Complete - 1 successfully verified harnesses, 2 failures, 3 total. diff --git a/tests/kani/ValidValues/non_null.rs b/tests/expected/valid-value-checks/non_null.rs similarity index 94% rename from tests/kani/ValidValues/non_null.rs rename to tests/expected/valid-value-checks/non_null.rs index 4874b61bf2d0..85d1a3876785 100644 --- a/tests/kani/ValidValues/non_null.rs +++ b/tests/expected/valid-value-checks/non_null.rs @@ -7,13 +7,11 @@ use std::num::NonZeroU8; use std::ptr::{self, NonNull}; #[kani::proof] -#[kani::should_panic] pub fn check_invalid_value() { let _ = unsafe { NonNull::new_unchecked(ptr::null_mut::()) }; } #[kani::proof] -#[kani::should_panic] pub fn check_invalid_value_cfg() { let nn = unsafe { NonNull::new_unchecked(ptr::null_mut::()) }; // This should be unreachable. TODO: Make this expected test. diff --git a/tests/expected/valid-value-checks/write_bytes.expected b/tests/expected/valid-value-checks/write_bytes.expected new file mode 100644 index 000000000000..8c14a9046d8d --- /dev/null +++ b/tests/expected/valid-value-checks/write_bytes.expected @@ -0,0 +1,5 @@ + ** 1 of 11 failed (1 unreachable)\ +Failed Checks: Undefined Behavior: Invalid value of type `char` + +Verification failed for - check_invalid_write +Complete - 1 successfully verified harnesses, 1 failures, 2 total. diff --git a/tests/kani/ValidValues/write_bytes.rs b/tests/expected/valid-value-checks/write_bytes.rs similarity index 96% rename from tests/kani/ValidValues/write_bytes.rs rename to tests/expected/valid-value-checks/write_bytes.rs index e4f73b1f3479..0b0ae6f343db 100644 --- a/tests/kani/ValidValues/write_bytes.rs +++ b/tests/expected/valid-value-checks/write_bytes.rs @@ -5,7 +5,6 @@ #![feature(core_intrinsics)] #[kani::proof] -#[kani::should_panic] pub fn check_invalid_write() { let mut val = 'a'; let ptr = &mut val as *mut char; diff --git a/tests/expected/valid-value-checks/write_invalid.expected b/tests/expected/valid-value-checks/write_invalid.expected new file mode 100644 index 000000000000..ba6c5b2ba65c --- /dev/null +++ b/tests/expected/valid-value-checks/write_invalid.expected @@ -0,0 +1,13 @@ + ** 1 of 19 failed (2 unreachable)\ +Failed Checks: Kani currently doesn't support checking validity of `transmute` for `std::option::Option>` + + ** 1 of 43 failed (2 unreachable)\ +Failed Checks: Kani currently doesn't support checking validity of `transmute` for `std::option::Option>` + + ** 1 of 18 failed (1 unreachable)\ +Failed Checks: Kani currently doesn't support checking validity of `transmute` for `std::option::Option>` + +Verification failed for - read_invalid_is_ub +Verification failed for - write_valid_before_read +Verification failed for - write_invalid_bytes_no_ub_with_spurious_cex +Complete - 0 successfully verified harnesses, 3 failures, 3 total. diff --git a/tests/kani/ValidValues/write_invalid.rs b/tests/expected/valid-value-checks/write_invalid.rs similarity index 94% rename from tests/kani/ValidValues/write_invalid.rs rename to tests/expected/valid-value-checks/write_invalid.rs index 05d3705bd69a..677ffe4edf61 100644 --- a/tests/kani/ValidValues/write_invalid.rs +++ b/tests/expected/valid-value-checks/write_invalid.rs @@ -9,7 +9,6 @@ use std::num::NonZeroU8; #[kani::proof] -#[kani::should_panic] pub fn write_invalid_bytes_no_ub_with_spurious_cex() { let mut non_zero: NonZeroU8 = kani::any(); let dest = &mut non_zero as *mut _; @@ -17,7 +16,6 @@ pub fn write_invalid_bytes_no_ub_with_spurious_cex() { } #[kani::proof] -#[kani::should_panic] pub fn write_valid_before_read() { let mut non_zero: NonZeroU8 = kani::any(); let mut non_zero_2: NonZeroU8 = kani::any(); @@ -28,7 +26,6 @@ pub fn write_valid_before_read() { } #[kani::proof] -#[kani::should_panic] pub fn read_invalid_is_ub() { let mut non_zero: NonZeroU8 = kani::any(); let dest = &mut non_zero as *mut _; From 006e5daa9c48ff5e055de342780dd5172213a979 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Wed, 19 Feb 2025 10:31:50 -0500 Subject: [PATCH 024/161] Fix: regression test from #3888 has version control change (#3892) Resolves #3891 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Michael Tautschnig --- .../cargo_playback_array/playback_target.sh | 27 ------------------- .../sample_crate/Cargo.toml | 10 ------- .../src/lib.rs => playback_array/array.rs} | 0 .../config.yml | 4 +-- .../playback_array.expected} | 0 .../playback_array/playback_array.sh | 22 +++++++++++++++ 6 files changed, 24 insertions(+), 39 deletions(-) delete mode 100755 tests/script-based-pre/cargo_playback_array/playback_target.sh delete mode 100644 tests/script-based-pre/cargo_playback_array/sample_crate/Cargo.toml rename tests/script-based-pre/{cargo_playback_array/sample_crate/src/lib.rs => playback_array/array.rs} (100%) rename tests/script-based-pre/{cargo_playback_array => playback_array}/config.yml (54%) rename tests/script-based-pre/{cargo_playback_array/playback_target.expected => playback_array/playback_array.expected} (100%) create mode 100755 tests/script-based-pre/playback_array/playback_array.sh diff --git a/tests/script-based-pre/cargo_playback_array/playback_target.sh b/tests/script-based-pre/cargo_playback_array/playback_target.sh deleted file mode 100755 index 17eaa66a01c6..000000000000 --- a/tests/script-based-pre/cargo_playback_array/playback_target.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -# Copyright Kani Contributors -# SPDX-License-Identifier: Apache-2.0 OR MIT - -set +e - -function check_playback { - local OUTPUT=output.log - cargo kani playback "${@}" >& $OUTPUT - # Sort output so we can rely on the order. - echo "$(grep "test verify::.* ok" $OUTPUT | sort)" - echo - echo "======= Raw Output =======" - cat $OUTPUT - echo "==========================" - echo - rm $OUTPUT -} - -pushd sample_crate > /dev/null -cargo clean - -cargo kani --concrete-playback inplace -Z concrete-playback -check_playback -Z concrete-playback - -cargo clean -popd > /dev/null diff --git a/tests/script-based-pre/cargo_playback_array/sample_crate/Cargo.toml b/tests/script-based-pre/cargo_playback_array/sample_crate/Cargo.toml deleted file mode 100644 index 202362506011..000000000000 --- a/tests/script-based-pre/cargo_playback_array/sample_crate/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright Kani Contributors -# SPDX-License-Identifier: Apache-2.0 OR MIT -[package] -name = "sample_crate" -version = "0.1.0" -edition = "2021" -doctest = false - -[lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tests/script-based-pre/cargo_playback_array/sample_crate/src/lib.rs b/tests/script-based-pre/playback_array/array.rs similarity index 100% rename from tests/script-based-pre/cargo_playback_array/sample_crate/src/lib.rs rename to tests/script-based-pre/playback_array/array.rs diff --git a/tests/script-based-pre/cargo_playback_array/config.yml b/tests/script-based-pre/playback_array/config.yml similarity index 54% rename from tests/script-based-pre/cargo_playback_array/config.yml rename to tests/script-based-pre/playback_array/config.yml index 99bfe39f95a5..88b420e8a6f0 100644 --- a/tests/script-based-pre/cargo_playback_array/config.yml +++ b/tests/script-based-pre/playback_array/config.yml @@ -1,4 +1,4 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -script: playback_target.sh -expected: playback_target.expected +script: playback_array.sh +expected: playback_array.expected diff --git a/tests/script-based-pre/cargo_playback_array/playback_target.expected b/tests/script-based-pre/playback_array/playback_array.expected similarity index 100% rename from tests/script-based-pre/cargo_playback_array/playback_target.expected rename to tests/script-based-pre/playback_array/playback_array.expected diff --git a/tests/script-based-pre/playback_array/playback_array.sh b/tests/script-based-pre/playback_array/playback_array.sh new file mode 100755 index 000000000000..3cceb8da6297 --- /dev/null +++ b/tests/script-based-pre/playback_array/playback_array.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +set -e +set -o pipefail +set -o nounset + +cleanup() +{ + rm ${RS_FILE} +} +trap cleanup EXIT + +RS_FILE="modified.rs" +cp array.rs ${RS_FILE} + +echo "[TEST] Generate test..." +kani ${RS_FILE} -Z concrete-playback --concrete-playback=inplace || true + +echo "[TEST] Run test..." +kani playback -Z concrete-playback ${RS_FILE} || true From 51de000bb2063e9ab4420add3e8d8bf58804271d Mon Sep 17 00:00:00 2001 From: thanhnguyen-aws Date: Fri, 21 Feb 2025 11:01:51 -0800 Subject: [PATCH 025/161] Upgrade toolchain to 2025-02-11 (#3887) Update Rust to nightly-2025-02-11 Upstream PR: https://github.com/rust-lang/rust/commit/ee7dc06cf181c073b1040669a40bc325d00f8c6d#diff-51b3860a8aed92b0e981635d4118d369c49850f5b7acf780d31f5ddd5d5d0bc2 Resolves #3884 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Zyad Hassan Co-authored-by: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> --- .../codegen_cprover_gotoc/codegen/function.rs | 36 ++++++++----------- kani-driver/src/call_cbmc.rs | 10 ++---- kani-driver/src/coverage/cov_results.rs | 1 - rust-toolchain.toml | 2 +- 4 files changed, 17 insertions(+), 32 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs index bf64f0dd2e66..15a4e6357349 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs @@ -220,7 +220,7 @@ impl GotocCtx<'_> { pub mod rustc_smir { use crate::codegen_cprover_gotoc::codegen::source_region::{SourceRegion, make_source_region}; use crate::stable_mir::CrateDef; - use rustc_middle::mir::coverage::CovTerm; + use rustc_middle::mir::coverage::BasicCoverageBlock; use rustc_middle::mir::coverage::MappingKind::Code; use rustc_middle::ty::TyCtxt; use rustc_smir::rustc_internal; @@ -236,16 +236,16 @@ pub mod rustc_smir { coverage_opaque: &CoverageOpaque, instance: Instance, ) -> Option<(SourceRegion, Filename)> { - let cov_term = parse_coverage_opaque(coverage_opaque); - region_from_coverage(tcx, cov_term, instance) + let bcb = parse_coverage_opaque(coverage_opaque); + region_from_coverage(tcx, bcb, instance) } - /// Retrieves the `SourceRegion` associated with a `CovTerm` object. + /// Retrieves the `SourceRegion` associated with a `BasicCoverageBlock` object. /// /// Note: This function could be in the internal `rustc` impl for `Coverage`. pub fn region_from_coverage( tcx: TyCtxt<'_>, - coverage: CovTerm, + coverage: BasicCoverageBlock, instance: Instance, ) -> Option<(SourceRegion, Filename)> { // We need to pull the coverage info from the internal MIR instance. @@ -257,10 +257,10 @@ pub mod rustc_smir { if let Some(cov_info) = &body.function_coverage_info { // Iterate over the coverage mappings and match with the coverage term. for mapping in &cov_info.mappings { - let Code(term) = mapping.kind else { unreachable!() }; + let Code { bcb } = mapping.kind else { unreachable!() }; let source_map = tcx.sess.source_map(); let file = source_map.lookup_source_file(cov_info.body_span.lo()); - if term == coverage { + if bcb == coverage { return Some(( make_source_region(source_map, cov_info, &file, mapping.span).unwrap(), rustc_internal::stable(cov_info.body_span).get_filename(), @@ -271,25 +271,17 @@ pub mod rustc_smir { None } - /// Parse a `CoverageOpaque` item and return the corresponding `CovTerm`: - /// - /// - /// At present, a `CovTerm` can be one of the following: - /// - `CounterIncrement()`: A physical counter. - /// - `ExpressionUsed()`: An expression-based counter. - /// - `Zero`: A counter with a constant zero value. - fn parse_coverage_opaque(coverage_opaque: &Opaque) -> CovTerm { + /// Parse a `CoverageOpaque` item and return the corresponding `BasicCoverageBlock`: + fn parse_coverage_opaque(coverage_opaque: &Opaque) -> BasicCoverageBlock { let coverage_str = coverage_opaque.to_string(); - if let Some(rest) = coverage_str.strip_prefix("CounterIncrement(") { - let (num_str, _rest) = rest.split_once(')').unwrap(); - let num = num_str.parse::().unwrap(); - CovTerm::Counter(num.into()) - } else if let Some(rest) = coverage_str.strip_prefix("ExpressionUsed(") { + if let Some(rest) = coverage_str.strip_prefix("VirtualCounter(bcb") { let (num_str, _rest) = rest.split_once(')').unwrap(); let num = num_str.parse::().unwrap(); - CovTerm::Expression(num.into()) + BasicCoverageBlock::from_u32(num) } else { - CovTerm::Zero + // When the coverage statement is injected into mir_body, it always has the form CoverageKind::VirtualCounter { bcb } + // https://github.com/rust-lang/rust/pull/136053/files#diff-c99ec9a281dce4a381fa7e11cf2d04f55dba5573d1d14389d47929fe0a154d24R209-R212 + unreachable!(); } } } diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index b26b84ca2632..92c82f25888d 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -501,7 +501,7 @@ fn coverage_results_from_properties(properties: &[Property]) -> Option = OnceLock::new(); COUNTER_RE.get_or_init(|| { Regex::new( - r#"^(?CounterIncrement|ExpressionUsed)\((?[0-9]+)\) \$(?[^\$]+)\$ - (?.+)"#, + r#"^(?VirtualCounter\(bcb)(?[0-9]+)\) \$(?[^\$]+)\$ - (?.+)"#, ) .unwrap() }) @@ -511,20 +511,14 @@ fn coverage_results_from_properties(properties: &[Property]) -> Option CoverageTerm::Counter(counter_id), - "ExpressionUsed" => CoverageTerm::Expression(counter_id), - _ => unreachable!("counter kind could not be recognized: {:?}", kind), - }; + let term = CoverageTerm::Counter(counter_id); let region = CoverageRegion::from_str(span); let cov_check = CoverageCheck::new(function, term, region, status); diff --git a/kani-driver/src/coverage/cov_results.rs b/kani-driver/src/coverage/cov_results.rs index 845ae7de21bb..694cb480bc4f 100644 --- a/kani-driver/src/coverage/cov_results.rs +++ b/kani-driver/src/coverage/cov_results.rs @@ -64,7 +64,6 @@ impl CoverageCheck { #[derive(Debug, Clone, Serialize, Deserialize)] pub enum CoverageTerm { Counter(u32), - Expression(u32), } #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 307d7e8a6120..066f744f4675 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-02-10" +channel = "nightly-2025-02-11" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 2e95d8bc47a04f01badcb3d30875269be29a46aa Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Fri, 21 Feb 2025 16:54:49 -0500 Subject: [PATCH 026/161] Remove isize overflow check for zst offsets (#3897) #3755 introduced additional safety checks for the `offset` intrinsic, including a check for whether `count` overflows `isize`. However, such overflow is allowed for ZSTs. This PR changes the model to check for ZSTs before computing the offset to avoid triggering an overflow failure for ZSTs. I also moved an existing test called `offset-overflow` into another test, since #3755 changed the failure for that test to be about an out of bounds allocation, not an isize overflow. Resolves #3896 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- library/kani_core/src/models.rs | 46 +++++++++++-------- .../out_of_bounds_ub_check.expected | 6 ++- .../out_of_bounds_ub_check.rs | 15 ++++++ tests/expected/offset-overflow/expected | 4 -- tests/expected/offset-overflow/main.rs | 19 -------- .../expected/offset-overflows-isize/expected | 8 ++++ tests/expected/offset-overflows-isize/main.rs | 21 +++++++++ 7 files changed, 74 insertions(+), 45 deletions(-) delete mode 100644 tests/expected/offset-overflow/expected delete mode 100644 tests/expected/offset-overflow/main.rs create mode 100644 tests/expected/offset-overflows-isize/expected create mode 100644 tests/expected/offset-overflows-isize/main.rs diff --git a/library/kani_core/src/models.rs b/library/kani_core/src/models.rs index f95e1acc4899..8913c399c959 100644 --- a/library/kani_core/src/models.rs +++ b/library/kani_core/src/models.rs @@ -99,29 +99,35 @@ macro_rules! generate_models { /// An offset model that checks UB. #[kanitool::fn_marker = "OffsetModel"] pub fn offset, O: ToISize>(ptr: P, offset: O) -> P { - let offset = offset.to_isize(); let t_size = core::mem::size_of::() as isize; - if offset == 0 || t_size == 0 { + if t_size == 0 { + // It's always safe to perform an offset on a ZST. + return ptr; + } + + // Note that this check must come after the t_size check, c.f. https://github.com/model-checking/kani/issues/3896 + let offset = offset.to_isize(); + if offset == 0 { // It's always safe to perform an offset of length 0. - ptr - } else { - let (byte_offset, overflow) = offset.overflowing_mul(t_size); - kani::safety_check(!overflow, "Offset in bytes overflows isize"); - let orig_ptr = ptr.to_const_ptr(); - // NOTE: For CBMC, using the pointer addition can have unexpected behavior - // when the offset is higher than the object bits since it will wrap around. - // See for more details: https://github.com/model-checking/kani/issues/1150 - // - // However, when I tried implementing this using usize operation, we got some - // unexpected failures that still require further debugging. - // let new_ptr = orig_ptr.addr().wrapping_add_signed(byte_offset) as *const T; - let new_ptr = orig_ptr.wrapping_byte_offset(byte_offset); - kani::safety_check( - kani::mem::same_allocation_internal(orig_ptr, new_ptr), - "Offset result and original pointer must point to the same allocation", - ); - P::from_const_ptr(new_ptr) + return ptr; } + + let (byte_offset, overflow) = offset.overflowing_mul(t_size); + kani::safety_check(!overflow, "Offset in bytes overflows isize"); + let orig_ptr = ptr.to_const_ptr(); + // NOTE: For CBMC, using the pointer addition can have unexpected behavior + // when the offset is higher than the object bits since it will wrap around. + // See for more details: https://github.com/model-checking/kani/issues/1150 + // + // However, when I tried implementing this using usize operation, we got some + // unexpected failures that still require further debugging. + // let new_ptr = orig_ptr.addr().wrapping_add_signed(byte_offset) as *const T; + let new_ptr = orig_ptr.wrapping_byte_offset(byte_offset); + kani::safety_check( + kani::mem::same_allocation_internal(orig_ptr, new_ptr), + "Offset result and original pointer must point to the same allocation", + ); + P::from_const_ptr(new_ptr) } pub trait Ptr { diff --git a/tests/expected/offset-bounds-check/out_of_bounds_ub_check.expected b/tests/expected/offset-bounds-check/out_of_bounds_ub_check.expected index d8c6e135adc9..e0cb28198a18 100644 --- a/tests/expected/offset-bounds-check/out_of_bounds_ub_check.expected +++ b/tests/expected/offset-bounds-check/out_of_bounds_ub_check.expected @@ -1,4 +1,6 @@ Failed Checks: Offset result and original pointer must point to the same allocation -Verification failed for - check_ptr_oob +Failed Checks: Offset result and original pointer must point to the same allocation -Complete - 1 successfully verified harnesses, 1 failures, 2 total. +Verification failed for - test_offset_overflow +Verification failed for - check_ptr_oob +Complete - 1 successfully verified harnesses, 2 failures, 3 total. diff --git a/tests/expected/offset-bounds-check/out_of_bounds_ub_check.rs b/tests/expected/offset-bounds-check/out_of_bounds_ub_check.rs index e6b4310269a1..fd08e7a76876 100644 --- a/tests/expected/offset-bounds-check/out_of_bounds_ub_check.rs +++ b/tests/expected/offset-bounds-check/out_of_bounds_ub_check.rs @@ -2,6 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Check that Kani offset operations correctly detect out-of-bound access. +#![feature(core_intrinsics)] +use std::intrinsics::offset; + /// Verification should fail because safety violation is not a regular panic. #[kani::proof] #[kani::should_panic] @@ -24,3 +27,15 @@ fn check_ptr_end() { // Safety: Pointers point to the same allocation assert_eq!(unsafe { end_ptr.offset_from(base_ptr) }, 1); } + +#[kani::proof] +fn test_offset_overflow() { + let s: &str = "123"; + let ptr: *const u8 = s.as_ptr(); + + unsafe { + // This should fail because adding `isize::MAX` to `ptr` would overflow + // `isize` + let _d = offset(ptr, isize::MAX); + } +} diff --git a/tests/expected/offset-overflow/expected b/tests/expected/offset-overflow/expected deleted file mode 100644 index af5ea73875ca..000000000000 --- a/tests/expected/offset-overflow/expected +++ /dev/null @@ -1,4 +0,0 @@ -Failed Checks: Offset result and original pointer must point to the same allocation -Verification failed for - test_offset_overflow - -Complete - 0 successfully verified harnesses, 1 failures, 1 total. diff --git a/tests/expected/offset-overflow/main.rs b/tests/expected/offset-overflow/main.rs deleted file mode 100644 index fc9f36b9cc5d..000000000000 --- a/tests/expected/offset-overflow/main.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT - -// Checks that `offset` fails if the offset computation would -// result in an arithmetic overflow -#![feature(core_intrinsics)] -use std::intrinsics::offset; - -#[kani::proof] -fn test_offset_overflow() { - let s: &str = "123"; - let ptr: *const u8 = s.as_ptr(); - - unsafe { - // This should fail because adding `isize::MAX` to `ptr` would overflow - // `isize` - let _d = offset(ptr, isize::MAX); - } -} diff --git a/tests/expected/offset-overflows-isize/expected b/tests/expected/offset-overflows-isize/expected new file mode 100644 index 000000000000..f554e2b1804b --- /dev/null +++ b/tests/expected/offset-overflows-isize/expected @@ -0,0 +1,8 @@ +::to_isize.safety_check\ + - Status: FAILURE\ + - Description: "Offset value overflows isize" + +Failed Checks: Offset value overflows isize + +Verification failed for - test_non_zst +Complete - 1 successfully verified harnesses, 1 failures, 2 total. diff --git a/tests/expected/offset-overflows-isize/main.rs b/tests/expected/offset-overflows-isize/main.rs new file mode 100644 index 000000000000..06400c1163d7 --- /dev/null +++ b/tests/expected/offset-overflows-isize/main.rs @@ -0,0 +1,21 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Checks that `offset` does not accept a `count` greater than isize::MAX, +// except for ZSTs, c.f. https://github.com/model-checking/kani/issues/3896 + +#[kani::proof] +fn test_zst() { + let mut x = (); + let ptr: *mut () = &mut x as *mut (); + let count: usize = (isize::MAX as usize) + 1; + let _ = unsafe { ptr.add(count) }; +} + +#[kani::proof] +fn test_non_zst() { + let mut x = 7; + let ptr: *mut i32 = &mut x as *mut i32; + let count: usize = (isize::MAX as usize) + 1; + let _ = unsafe { ptr.add(count) }; +} From fe0d9d2a63c969898bfe733b3ab4a13cc834bf0b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Feb 2025 14:24:15 -0800 Subject: [PATCH 027/161] Automatic toolchain upgrade to nightly-2025-02-12 (#3898) Update Rust toolchain from nightly-2025-02-11 to nightly-2025-02-12 without any other source changes. Co-authored-by: carolynzech <71352687+carolynzech@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 066f744f4675..2286133bf40d 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-02-11" +channel = "nightly-2025-02-12" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 4e54539c9b0f0354c13c0b273ed9d484cdc5d217 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Fri, 21 Feb 2025 16:30:52 -0800 Subject: [PATCH 028/161] Upgrade the toolchain to 2025-02-21 (#3899) Upstream PRs requiring changes: https://github.com/rust-lang/rust/pull/135994 https://github.com/rust-lang/rust/pull/136466 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs | 2 +- kani-compiler/src/kani_middle/mod.rs | 2 +- kani-compiler/src/kani_middle/resolve.rs | 4 ++-- rust-toolchain.toml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index 860619668c9d..0c2a81c3082c 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -667,7 +667,7 @@ impl GotocCtx<'_> { assert!(operands.len() == 2); let typ = self.codegen_ty_stable(res_ty); let layout = self.layout_of_stable(res_ty); - assert!(layout.ty.is_unsafe_ptr()); + assert!(layout.ty.is_raw_ptr()); let data = self.codegen_operand_stable(&operands[0]); match pointee_ty.kind() { TyKind::RigidTy(RigidTy::Slice(inner_ty)) => { diff --git a/kani-compiler/src/kani_middle/mod.rs b/kani-compiler/src/kani_middle/mod.rs index 508e92f76479..690ff03058bc 100644 --- a/kani-compiler/src/kani_middle/mod.rs +++ b/kani-compiler/src/kani_middle/mod.rs @@ -37,7 +37,7 @@ pub mod transform; /// error was found. pub fn check_crate_items(tcx: TyCtxt, ignore_asm: bool) { let krate = tcx.crate_name(LOCAL_CRATE); - for item in tcx.hir().items() { + for item in tcx.hir_free_items() { let def_id = item.owner_id.def_id.to_def_id(); KaniAttributes::for_item(tcx, def_id).check_attributes(); if tcx.def_kind(def_id) == DefKind::GlobalAsm { diff --git a/kani-compiler/src/kani_middle/resolve.rs b/kani-compiler/src/kani_middle/resolve.rs index cd1b72c6dc66..8f278ae20ed8 100644 --- a/kani-compiler/src/kani_middle/resolve.rs +++ b/kani-compiler/src/kani_middle/resolve.rs @@ -430,8 +430,8 @@ fn resolve_relative(tcx: TyCtxt, current_module: LocalModDefId, name: &str) -> R debug!(?name, ?current_module, "resolve_relative"); let mut glob_imports = vec![]; - let result = tcx.hir().module_items(current_module).find_map(|item_id| { - let item = tcx.hir().item(item_id); + let result = tcx.hir_module_free_items(current_module).find_map(|item_id| { + let item = tcx.hir_item(item_id); if item.ident.as_str() == name { match item.kind { ItemKind::Use(use_path, UseKind::Single) => use_path.res[0].opt_def_id(), diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 2286133bf40d..8078beff6d73 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-02-12" +channel = "nightly-2025-02-21" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From f64f53e25b8e93e8bb00cecc57b0677d9235b11c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 23 Feb 2025 22:18:13 -0800 Subject: [PATCH 029/161] Automatic cargo update to 2025-02-24 (#3901) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 68 +++++++++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87286ca982bf..6392115346eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,9 +101,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" [[package]] name = "arrayvec" @@ -230,9 +230,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.14" +version = "1.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" +checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" dependencies = [ "shlex", ] @@ -282,9 +282,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.29" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184" +checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" dependencies = [ "clap_builder", "clap_derive", @@ -292,9 +292,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.29" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9" +checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" dependencies = [ "anstream", "anstyle", @@ -559,9 +559,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" [[package]] name = "encode_unicode" @@ -930,9 +930,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "linear-map" @@ -962,9 +962,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "macros" @@ -1004,9 +1004,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", ] @@ -1371,9 +1371,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" dependencies = [ "bitflags", ] @@ -1500,9 +1500,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] @@ -1518,9 +1518,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", @@ -1529,9 +1529,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.138" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" dependencies = [ "indexmap", "itoa", @@ -1619,9 +1619,9 @@ checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "stacker" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d08feb8f695b465baed819b03c128dc23f57a694510ab1f06c77f763975685e" +checksum = "d9156ebd5870ef293bfb43f91c7a74528d363ec0d424afe24160ed5a4343d08a" dependencies = [ "cc", "cfg-if", @@ -1697,9 +1697,9 @@ checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "tempfile" -version = "3.17.0" +version = "3.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a40f762a77d2afa88c2d919489e390a12bdd261ed568e60cfa7e48d4e20f0d33" +checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" dependencies = [ "cfg-if", "fastrand", @@ -1970,9 +1970,9 @@ dependencies = [ [[package]] name = "tree-sitter-language" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38eee4db33814de3d004de9d8d825627ed3320d0989cce0dea30efaf5be4736c" +checksum = "c4013970217383f67b18aef68f6fb2e8d409bc5755227092d32efb0422ba24b8" [[package]] name = "tree-sitter-rust" @@ -1986,9 +1986,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "unicode-segmentation" @@ -2193,9 +2193,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" +checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" dependencies = [ "memchr", ] From 303302d649680a2c00c51e9d88686e2fc9009a27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 18:49:49 +0000 Subject: [PATCH 030/161] Bump ncipollo/release-action from 1.15.0 to 1.16.0 (#3902) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.15.0 to 1.16.0.
Release notes

Sourced from ncipollo/release-action's releases.

v1.16.0

What's Changed

New Contributors

Full Changelog: https://github.com/ncipollo/release-action/compare/v1...v1.16.0

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ncipollo/release-action&package-manager=github_actions&previous-version=1.15.0&new-version=1.16.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1db596441f58..df2710ee3e04 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -287,7 +287,7 @@ jobs: - name: Create release id: create_release - uses: ncipollo/release-action@v1.15.0 + uses: ncipollo/release-action@v1.16.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From bc573ef8bd131880a640db7cb00d3d8d6c04aea2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 19:04:17 +0000 Subject: [PATCH 031/161] Bump tests/perf/s2n-quic from `00e3371` to `cfb314b` (#3903) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `00e3371` to `cfb314b`.
Commits
  • cfb314b build(deps): update rand requirement except for s2n-quic-sim (#2475)
  • 5e553f3 feat(s2n-quic-platform): add a new Tokio IO API to configure only_v6 (#2473)
  • dad94a7 feat(s2n-quic-dc): accept linger parameter instead of always setting it (#2476)
  • 5c78b33 refactor(s2n-quic-dc): Replace fixed maps with hashbrown::HashTable (#2477)
  • See full diff in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 00e33714d74b..cfb314b2e812 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 00e33714d74b0646bc18ab65c90a45504a185828 +Subproject commit cfb314b2e812158d645d083e0529fff46832149d From 366ce2015b6f5250c59e1f17b210e6dcc2843550 Mon Sep 17 00:00:00 2001 From: Florian Bartels <108917393+flba-eb@users.noreply.github.com> Date: Wed, 26 Feb 2025 17:58:44 +0100 Subject: [PATCH 032/161] Convert raw URL to link (#3907) This improves the documentation by creating a markdown link from a "raw" URL. --- docs/src/undefined-behaviour.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/undefined-behaviour.md b/docs/src/undefined-behaviour.md index 8d883b1e4152..1089b32342af 100644 --- a/docs/src/undefined-behaviour.md +++ b/docs/src/undefined-behaviour.md @@ -24,7 +24,7 @@ A non-exhaustive list of these, based on the non-exhaustive list from the [Rust * Data races. * Kani focuses on sequential code. -* Breaking the pointer aliasing rules (http://llvm.org/docs/LangRef.html#pointer-aliasing-rules). +* Breaking the [pointer aliasing rules](http://llvm.org/docs/LangRef.html#pointer-aliasing-rules). * Kani can detect if misuse of pointers causes memory safety or assertion violations, but does not track reference lifetimes. * Mutating immutable data. * Kani can detect if modification of immutable data causes memory safety or assertion violations, but does not track reference lifetimes. From 8d46dc61d89b8b1cb8a3c2f609f74ee1db469f77 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 00:22:40 -0800 Subject: [PATCH 033/161] Automatic cargo update to 2025-03-03 (#3913) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6392115346eb..69990084ed31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,9 +150,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "brownstone" @@ -216,9 +216,9 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.19.1" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" dependencies = [ "camino", "cargo-platform", @@ -230,9 +230,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.15" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ "shlex", ] @@ -282,9 +282,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.30" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" +checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" dependencies = [ "clap_builder", "clap_derive", @@ -292,9 +292,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.30" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" +checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" dependencies = [ "anstream", "anstyle", @@ -375,9 +375,9 @@ dependencies = [ [[package]] name = "console" -version = "0.15.10" +version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" dependencies = [ "encode_unicode", "libc", From 40e46bc978c3b3bb741bd9fe849fbe0f5985cb1c Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Mon, 3 Mar 2025 22:57:02 +0100 Subject: [PATCH 034/161] Install toolchain with rustup >= 1.28.0 (#3917) See https://github.com/rust-lang/rustup/blob/master/CHANGELOG.md#1280---2025-03-04 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Zyad Hassan --- .github/workflows/deny.yml | 2 ++ scripts/setup/install_rustup.sh | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deny.yml b/.github/workflows/deny.yml index a5db349f8abc..c8beaa33a91d 100644 --- a/.github/workflows/deny.yml +++ b/.github/workflows/deny.yml @@ -20,6 +20,8 @@ jobs: - uses: actions/checkout@v4 with: submodules: recursive + - name: Install rustup + run: ./scripts/setup/install_rustup.sh - uses: EmbarkStudios/cargo-deny-action@v2 with: arguments: --all-features --workspace diff --git a/scripts/setup/install_rustup.sh b/scripts/setup/install_rustup.sh index 69478996a0c8..dcc3cd74e866 100755 --- a/scripts/setup/install_rustup.sh +++ b/scripts/setup/install_rustup.sh @@ -5,5 +5,11 @@ set -eux # Install Rust toolchain -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ - && source ~/.cargo/env +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +# Don't use .cargo/env as that won't prepend .cargo/bin to the PATH when it's +# already somewhere in there +export PATH="$HOME/.cargo/bin:$PATH" +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +pushd ${SCRIPT_DIR}/../../ +rustup toolchain install +popd From 182fb6c74f52159c2b3ebcf8cf72829cb5614975 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Mar 2025 10:18:20 +0100 Subject: [PATCH 035/161] Bump tests/perf/s2n-quic from `cfb314b` to `d88faa4` (#3916) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `cfb314b` to `d88faa4`.
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Carolyn Zech --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index cfb314b2e812..d88faa482544 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit cfb314b2e812158d645d083e0529fff46832149d +Subproject commit d88faa482544f916c90b3d65f754591edd099667 From fecc7e47bac7625bf85ff5888af37d96904d246e Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 4 Mar 2025 19:06:13 +0100 Subject: [PATCH 036/161] Remove Ubuntu 20.04 CI usage (#3918) This is deprecated per https://github.com/actions/runner-images/issues/11101 and will be fully removed on 2025-04-01. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .github/workflows/bench.yml | 6 +++--- .github/workflows/cbmc-latest.yml | 6 +++--- .github/workflows/format-check.yml | 4 ++-- .github/workflows/kani.yml | 12 ++++++------ .github/workflows/release.yml | 21 +++++++++------------ .github/workflows/slow-tests.yml | 2 +- 6 files changed, 24 insertions(+), 27 deletions(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 12b08eba7c9c..dcd5b5004f2f 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -15,7 +15,7 @@ on: jobs: perf-benchcomp: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: Save push event HEAD and HEAD~ to environment variables if: ${{ github.event_name == 'push' }} @@ -46,13 +46,13 @@ jobs: - name: Set up Kani Dependencies (old variant) uses: ./old/.github/actions/setup with: - os: ubuntu-20.04 + os: ubuntu-24.04 kani_dir: old - name: Set up Kani Dependencies (new variant) uses: ./new/.github/actions/setup with: - os: ubuntu-20.04 + os: ubuntu-24.04 kani_dir: new - name: Copy benchmarks from new to old diff --git a/.github/workflows/cbmc-latest.yml b/.github/workflows/cbmc-latest.yml index b71ad55ec130..c2781408b6ae 100644 --- a/.github/workflows/cbmc-latest.yml +++ b/.github/workflows/cbmc-latest.yml @@ -19,7 +19,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-13, ubuntu-20.04, ubuntu-22.04] + os: [macos-13, ubuntu-22.04, ubuntu-24.04] steps: - name: Checkout Kani under "kani" uses: actions/checkout@v4 @@ -61,7 +61,7 @@ jobs: run: ./scripts/kani-regression.sh perf: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: Checkout Kani under "kani" uses: actions/checkout@v4 @@ -71,7 +71,7 @@ jobs: - name: Setup Kani Dependencies uses: ./kani/.github/actions/setup with: - os: ubuntu-20.04 + os: ubuntu-24.04 kani_dir: 'kani' - name: Build Kani using release mode diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml index 1bfc2582c87c..c75b457073b8 100644 --- a/.github/workflows/format-check.yml +++ b/.github/workflows/format-check.yml @@ -31,7 +31,7 @@ jobs: ./scripts/run-autopep8.sh clippy-check: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: Checkout Kani uses: actions/checkout@v4 @@ -39,7 +39,7 @@ jobs: - name: Setup Kani Dependencies uses: ./.github/actions/setup with: - os: ubuntu-20.04 + os: ubuntu-24.04 - name: 'Install jq for parsing.' run: | diff --git a/.github/workflows/kani.yml b/.github/workflows/kani.yml index ad2fe757dc17..1c8cd119c393 100644 --- a/.github/workflows/kani.yml +++ b/.github/workflows/kani.yml @@ -18,7 +18,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-13, ubuntu-20.04, ubuntu-22.04, ubuntu-24.04, macos-14] + os: [macos-13, ubuntu-22.04, ubuntu-24.04, macos-14] steps: - name: Checkout Kani uses: actions/checkout@v4 @@ -32,7 +32,7 @@ jobs: run: ./scripts/kani-regression.sh benchcomp-tests: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: Checkout Kani uses: actions/checkout@v4 @@ -46,7 +46,7 @@ jobs: - name: Setup Kani Dependencies uses: ./.github/actions/setup with: - os: ubuntu-20.04 + os: ubuntu-24.04 - name: Build Kani using release mode run: cargo build-dev -- --release @@ -55,7 +55,7 @@ jobs: run: pushd tools/benchcomp && PATH=$(realpath ../../scripts):$PATH test/run perf: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: Checkout Kani uses: actions/checkout@v4 @@ -63,7 +63,7 @@ jobs: - name: Setup Kani Dependencies uses: ./.github/actions/setup with: - os: ubuntu-20.04 + os: ubuntu-24.04 - name: Execute Kani performance tests run: ./scripts/kani-perf.sh @@ -71,7 +71,7 @@ jobs: RUST_TEST_THREADS: 1 documentation: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 permissions: contents: write steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df2710ee3e04..244c8063f0c6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -70,7 +70,7 @@ jobs: build_bundle_linux: name: BuildBundle-Linux - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 outputs: version: ${{ steps.bundle.outputs.version }} bundle: ${{ steps.bundle.outputs.bundle }} @@ -83,7 +83,7 @@ jobs: - name: Setup Kani Dependencies uses: ./.github/actions/setup with: - os: ubuntu-20.04 + os: ubuntu-22.04 - name: Build bundle id: bundle @@ -97,14 +97,11 @@ jobs: needs: [build_bundle_macos, build_bundle_linux] strategy: matrix: - os: [macos-13, ubuntu-20.04, ubuntu-22.04, ubuntu-24.04] + os: [macos-13, ubuntu-22.04, ubuntu-24.04] include: - os: macos-13 rust_target: x86_64-apple-darwin prev_job: ${{ needs.build_bundle_macos.outputs }} - - os: ubuntu-20.04 - rust_target: x86_64-unknown-linux-gnu - prev_job: ${{ needs.build_bundle_linux.outputs }} - os: ubuntu-22.04 rust_target: x86_64-unknown-linux-gnu prev_job: ${{ needs.build_bundle_linux.outputs }} @@ -200,7 +197,7 @@ jobs: needs: [build_bundle_macos, build_bundle_linux] strategy: matrix: - os: [macos-13, ubuntu-20.04, ubuntu-22.04, ubuntu-24.04] + os: [macos-13, ubuntu-22.04, ubuntu-24.04] include: # Stores the output of the previous job conditional to the OS - prev_job: ${{ needs.build_bundle_linux.outputs }} @@ -244,7 +241,7 @@ jobs: kani_release: if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/kani-') }} name: Release - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 needs: [build_bundle_macos, build_bundle_macos_aarch64, build_bundle_linux, test_bundle] outputs: version: ${{ steps.versioning.outputs.version }} @@ -301,12 +298,12 @@ jobs: package_docker: name: Package Docker needs: kani_release - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 permissions: contents: write packages: write env: - os: ubuntu-20.04 + os: ubuntu-24.04 target: x86_64-unknown-linux-gnu steps: - name: Checkout code @@ -315,7 +312,7 @@ jobs: - name: Setup Kani Dependencies uses: ./.github/actions/setup with: - os: ubuntu-20.04 + os: ubuntu-24.04 - name: 'Build release bundle' run: | @@ -339,7 +336,7 @@ jobs: uses: docker/build-push-action@v6 with: context: . - file: scripts/ci/Dockerfile.bundle-release-20-04 + file: scripts/ci/Dockerfile.bundle-release-24-04 push: true github-token: ${{ secrets.GITHUB_TOKEN }} tags: | diff --git a/.github/workflows/slow-tests.yml b/.github/workflows/slow-tests.yml index 952f02a9f225..2eb44008b14d 100644 --- a/.github/workflows/slow-tests.yml +++ b/.github/workflows/slow-tests.yml @@ -18,7 +18,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-13, ubuntu-20.04, ubuntu-22.04] + os: [macos-13, ubuntu-22.04, ubuntu-24.04] steps: - name: Checkout Kani uses: actions/checkout@v4 From f604bef4070cbead4ebc78a934faf8aca78a091f Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 4 Mar 2025 20:26:53 +0100 Subject: [PATCH 037/161] scanner: Fix loop stats in overall function stats summary (#3915) We already have loop statistics ever since d2141e4c5f59683, they were just not merged into the overall per-function summary (which had an unset `has_loop` column). By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- tools/scanner/src/analysis.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/scanner/src/analysis.rs b/tools/scanner/src/analysis.rs index 744ca4aec9e5..89459de72520 100644 --- a/tools/scanner/src/analysis.rs +++ b/tools/scanner/src/analysis.rs @@ -34,7 +34,7 @@ struct FnStats { is_unsafe: Option, has_unsafe_ops: Option, has_unsupported_input: Option, - has_loop: Option, + has_loop_or_iterator: Option, } impl FnStats { @@ -44,8 +44,7 @@ impl FnStats { is_unsafe: None, has_unsafe_ops: None, has_unsupported_input: None, - // TODO: Implement this. - has_loop: None, + has_loop_or_iterator: None, } } } @@ -191,7 +190,10 @@ impl OverallStats { if !kind.is_fn() { return None; }; - Some(FnLoops::new(item.name()).collect(&item.body())) + let fn_props = FnLoops::new(item.name()).collect(&item.body()); + self.fn_stats.get_mut(&item).unwrap().has_loop_or_iterator = + Some(fn_props.has_iterators() || fn_props.has_loops()); + Some(fn_props) }) .partition::, _>(|props| props.has_iterators() || props.has_loops()); From b93e591556d1aaf054b73148604256521a0424fa Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 4 Mar 2025 20:27:31 +0100 Subject: [PATCH 038/161] Move standard-library metrics script to verify-rust-std repo (#3914) Added to verify-rust-std in https://github.com/model-checking/verify-rust-std/pull/261. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- scripts/std-analysis.sh | 115 ---------------------------------------- 1 file changed, 115 deletions(-) delete mode 100755 scripts/std-analysis.sh diff --git a/scripts/std-analysis.sh b/scripts/std-analysis.sh deleted file mode 100755 index 324b9616c430..000000000000 --- a/scripts/std-analysis.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/env bash -# Copyright Kani Contributors -# SPDX-License-Identifier: Apache-2.0 OR MIT - -# Collect some metrics related to the crates that compose the standard library. -# -# Files generates so far: -# -# - ${crate}_scan_overall.csv: Summary of function metrics, such as safe vs unsafe. -# - ${crate}_scan_input_tys.csv: Detailed information about the inputs' type of each -# function found in this crate. -# -# How we collect metrics: -# -# - Compile the standard library using the `scan` tool to collect some metrics. -# - After compilation, move all CSV files that were generated by the scanner, -# to the results folder. -set -eu - -# Test for platform -PLATFORM=$(uname -sp) -if [[ $PLATFORM == "Linux x86_64" ]] -then - TARGET="x86_64-unknown-linux-gnu" - # 'env' necessary to avoid bash built-in 'time' - WRAPPER="env time -v" -elif [[ $PLATFORM == "Darwin i386" ]] -then - TARGET="x86_64-apple-darwin" - # mac 'time' doesn't have -v - WRAPPER="time" -elif [[ $PLATFORM == "Darwin arm" ]] -then - TARGET="aarch64-apple-darwin" - # mac 'time' doesn't have -v - WRAPPER="time" -else - echo - echo "Std-Lib codegen regression only works on Linux or OSX x86 platforms, skipping..." - echo - exit 0 -fi - -# Get Kani root -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -KANI_DIR=$(dirname "$SCRIPT_DIR") - -echo "-------------------------------------------------------" -echo "-- Starting analysis of the Rust standard library... --" -echo "-------------------------------------------------------" - -echo "-- Build scanner" -cd $KANI_DIR -cargo build -p scanner - -echo "-- Build std" -cd /tmp -if [ -d std_lib_analysis ] -then - rm -rf std_lib_analysis -fi -cargo new std_lib_analysis --lib -cd std_lib_analysis - -echo ' -pub fn dummy() { -} -' > src/lib.rs - -# Use same nightly toolchain used to build Kani -cp ${KANI_DIR}/rust-toolchain.toml . - -export RUST_BACKTRACE=1 -export RUSTC_LOG=error - -RUST_FLAGS=( - "-Cpanic=abort" - "-Zalways-encode-mir" -) -export RUSTFLAGS="${RUST_FLAGS[@]}" -export RUSTC="$KANI_DIR/target/debug/scan" -# Compile rust with our extension -$WRAPPER cargo build --verbose -Z build-std --lib --target $TARGET - -echo "-- Process results" - -# Move files to results folder -results=/tmp/std_lib_analysis/results -mkdir $results -find /tmp/std_lib_analysis/target -name "*.csv" -exec mv {} $results \; - -# Create a summary table -summary=$results/summary.csv - -# write header -echo -n "crate," > $summary -tr -d "[:digit:],;" < $results/alloc_scan_overall.csv \ - | tr -s '\n' ',' >> $summary -echo "" >> $summary - -# write body -for f in $results/*overall.csv; do - # Join all crate summaries into one table - fname=$(basename $f) - crate=${fname%_scan_overall.csv} - echo -n "$crate," >> $summary - tr -d '[:alpha:]_,;' < $f | tr -s '\n' ',' \ - >> $summary - echo "" >> $summary -done - -echo "-------------------------------------------------------" -echo "Finished analysis successfully..." -echo "- See results at ${results}" -echo "-------------------------------------------------------" From 9ea1f38cdb14e4c9b24473e076f387c6ae82f314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Delmas?= Date: Wed, 5 Mar 2025 12:36:03 -0500 Subject: [PATCH 039/161] Update toolchain to 2025-03-02 (#3911) Changes to attribute parsing and representation https://github.com/rust-lang/rust/pull/135726 Map methods moved to `TyCtx` https://github.com/rust-lang/rust/pull/137162 https://github.com/rust-lang/rust/pull/137397 Remove `BackendRepr::Unihabited` https://github.com/rust-lang/rust/pull/136985 Intrinsics rint, roundeven, nearbyint replaced by `round_ties_even`. https://github.com/rust-lang/rust/pull/136543. Use `__sort_of_CPROVER_round_to_integral` to model `round_ties_even`. Rename `sub_ptr` to `offset_from_unsigned`. The feature gate is still `ptr_sub_ptr`. https://github.com/rust-lang/rust/pull/137483. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Remi Delmas --- cprover_bindings/src/goto_program/builtin.rs | 13 ++- cprover_bindings/src/machine_model.rs | 1 + docs/src/rust-feature-support/intrinsics.md | 8 +- .../codegen_cprover_gotoc/codegen/contract.rs | 4 +- .../codegen/intrinsic.rs | 39 ++++++- .../codegen_cprover_gotoc/codegen/rvalue.rs | 6 +- .../codegen/statement.rs | 7 +- .../src/codegen_cprover_gotoc/codegen/typ.rs | 8 +- kani-compiler/src/intrinsics.rs | 30 ++--- kani-compiler/src/kani_middle/attributes.rs | 81 +++++++------- kani-compiler/src/kani_middle/intrinsics.rs | 2 +- .../src/kani_middle/kani_functions.rs | 4 +- .../points_to/points_to_analysis.rs | 6 +- kani-compiler/src/kani_middle/reachability.rs | 8 ++ kani-compiler/src/kani_middle/resolve.rs | 4 +- .../check_uninit/ptr_uninit/uninit_visitor.rs | 4 - .../src/kani_middle/transform/check_values.rs | 1 - .../kani_middle/transform/rustc_intrinsics.rs | 4 +- library/kani_core/src/models.rs | 4 +- rust-toolchain.toml | 2 +- .../ptrs/pointer_generator_error.expected | 2 +- ...expected => offset_from_unsigned.expected} | 20 ++-- .../{sub_ptr.rs => offset_from_unsigned.rs} | 28 ++--- .../Intrinsics/Math/Rounding/RInt/rintf32.rs | 104 ------------------ .../Intrinsics/Math/Rounding/RInt/rintf64.rs | 104 ------------------ .../round_ties_even_f32.rs} | 20 ++-- .../round_ties_even_f64.rs} | 20 ++-- tests/kani/PointerOffset/offset_from_vec.rs | 2 +- 28 files changed, 183 insertions(+), 353 deletions(-) rename tests/expected/offset-bounds-check/{sub_ptr.expected => offset_from_unsigned.expected} (52%) rename tests/expected/offset-bounds-check/{sub_ptr.rs => offset_from_unsigned.rs} (62%) delete mode 100644 tests/kani/Intrinsics/Math/Rounding/RInt/rintf32.rs delete mode 100644 tests/kani/Intrinsics/Math/Rounding/RInt/rintf64.rs rename tests/kani/Intrinsics/Math/Rounding/{NearbyInt/nearbyintf32.rs => RoundTiesEven/round_ties_even_f32.rs} (79%) rename tests/kani/Intrinsics/Math/Rounding/{NearbyInt/nearbyintf64.rs => RoundTiesEven/round_ties_even_f64.rs} (79%) diff --git a/cprover_bindings/src/goto_program/builtin.rs b/cprover_bindings/src/goto_program/builtin.rs index a0c1f211b5e2..0ff2296a1024 100644 --- a/cprover_bindings/src/goto_program/builtin.rs +++ b/cprover_bindings/src/goto_program/builtin.rs @@ -6,7 +6,7 @@ use super::{Expr, Location, Symbol, Type}; use std::fmt::Display; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum BuiltinFn { Abort, Assert, @@ -59,6 +59,8 @@ pub enum BuiltinFn { Rintf, Round, Roundf, + RoundToIntegralF, + RoundToIntegral, Sin, Sinf, Sqrt, @@ -123,6 +125,9 @@ impl Display for BuiltinFn { Rintf => "rintf", Round => "round", Roundf => "roundf", + // TODO remove the sort_of prefix once we move up from CBMC 6.4.1 + RoundToIntegralF => "__sort_of_CPROVER_round_to_integralf", + RoundToIntegral => "__sort_of_CPROVER_round_to_integral", Sin => "sin", Sinf => "sinf", Sqrt => "sqrt", @@ -188,6 +193,8 @@ impl BuiltinFn { Rintf => vec![Type::float()], Round => vec![Type::double()], Roundf => vec![Type::float()], + RoundToIntegralF => vec![Type::c_int(), Type::float()], + RoundToIntegral => vec![Type::c_int(), Type::double()], Sin => vec![Type::double()], Sinf => vec![Type::float()], Sqrt => vec![Type::double()], @@ -252,6 +259,8 @@ impl BuiltinFn { Rintf => Type::float(), Round => Type::double(), Roundf => Type::float(), + RoundToIntegralF => Type::float(), + RoundToIntegral => Type::double(), Sin => Type::double(), Sinf => Type::float(), Sqrt => Type::double(), @@ -316,6 +325,8 @@ impl BuiltinFn { Rintf, Round, Roundf, + RoundToIntegralF, + RoundToIntegral, Sin, Sinf, Sqrt, diff --git a/cprover_bindings/src/machine_model.rs b/cprover_bindings/src/machine_model.rs index 31427a83a629..463fe8f047e6 100644 --- a/cprover_bindings/src/machine_model.rs +++ b/cprover_bindings/src/machine_model.rs @@ -45,6 +45,7 @@ pub enum RoundingMode { Downward = 1, Upward = 2, TowardsZero = 3, + ToAway = 4, } impl From for BigInt { diff --git a/docs/src/rust-feature-support/intrinsics.md b/docs/src/rust-feature-support/intrinsics.md index 9f3495da0adc..2fa8b54094fc 100644 --- a/docs/src/rust-feature-support/intrinsics.md +++ b/docs/src/rust-feature-support/intrinsics.md @@ -180,8 +180,6 @@ minnumf32 | Yes | | minnumf64 | Yes | | move_val_init | No | | mul_with_overflow | Yes | | -nearbyintf32 | Yes | | -nearbyintf64 | Yes | | needs_drop | Yes | | nontemporal_store | No | | offset | Partial | Doesn't check [all UB conditions](https://doc.rust-lang.org/std/primitive.pointer.html#safety-2) | @@ -198,8 +196,10 @@ ptr_guaranteed_eq | Yes | | ptr_guaranteed_ne | Yes | | ptr_offset_from | Partial | Doesn't check [all UB conditions](https://doc.rust-lang.org/std/primitive.pointer.html#safety-4) | raw_eq | Partial | Cannot detect [uninitialized memory](#uninitialized-memory) | -rintf32 | Yes | | -rintf64 | Yes | | +round_ties_even_f16 | No | | +round_ties_even_f32 | Yes | | +round_ties_even_f64 | Yes | | +round_ties_even_f128 | No | | rotate_left | Yes | | rotate_right | Yes | | roundf32 | Yes | | diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs index 254fc854e8b5..d3d4481bf258 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs @@ -63,9 +63,7 @@ impl GotocCtx<'_> { // Return item tagged with `#[kanitool::recursion_tracker]` let mut recursion_trackers = items.iter().filter_map(|item| { let MonoItem::Static(static_item) = item else { return None }; - if !static_item - .attrs_by_path(&["kanitool".into(), "recursion_tracker".into()]) - .is_empty() + if !static_item.tool_attrs(&["kanitool".into(), "recursion_tracker".into()]).is_empty() { let span = static_item.span(); let loc = self.codegen_span_stable(span); diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index bbdc0be7910a..ac5b44ec4163 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -414,8 +414,6 @@ impl GotocCtx<'_> { Intrinsic::MulWithOverflow => { self.codegen_op_with_overflow(BinaryOperator::OverflowResultMult, fargs, place, loc) } - Intrinsic::NearbyIntF32 => codegen_simple_intrinsic!(Nearbyintf), - Intrinsic::NearbyIntF64 => codegen_simple_intrinsic!(Nearbyint), Intrinsic::NeedsDrop => codegen_intrinsic_const!(), Intrinsic::PowF32 => codegen_simple_intrinsic!(Powf), Intrinsic::PowF64 => codegen_simple_intrinsic!(Pow), @@ -425,12 +423,24 @@ impl GotocCtx<'_> { Intrinsic::PtrGuaranteedCmp => self.codegen_ptr_guaranteed_cmp(fargs, place, loc), Intrinsic::RawEq => self.codegen_intrinsic_raw_eq(instance, fargs, place, loc), Intrinsic::RetagBoxToRaw => self.codegen_retag_box_to_raw(fargs, place, loc), - Intrinsic::RintF32 => codegen_simple_intrinsic!(Rintf), - Intrinsic::RintF64 => codegen_simple_intrinsic!(Rint), Intrinsic::RotateLeft => codegen_intrinsic_binop!(rol), Intrinsic::RotateRight => codegen_intrinsic_binop!(ror), Intrinsic::RoundF32 => codegen_simple_intrinsic!(Roundf), Intrinsic::RoundF64 => codegen_simple_intrinsic!(Round), + Intrinsic::RoundTiesEvenF32 => self.codegen_round_to_integral( + BuiltinFn::RoundToIntegralF, + cbmc::RoundingMode::ToNearest, + fargs, + place, + loc, + ), + Intrinsic::RoundTiesEvenF64 => self.codegen_round_to_integral( + BuiltinFn::RoundToIntegral, + cbmc::RoundingMode::ToNearest, + fargs, + place, + loc, + ), Intrinsic::SaturatingAdd => codegen_intrinsic_binop_with_mm!(saturating_add), Intrinsic::SaturatingSub => codegen_intrinsic_binop_with_mm!(saturating_sub), Intrinsic::SinF32 => codegen_simple_intrinsic!(Sinf), @@ -638,6 +648,27 @@ impl GotocCtx<'_> { dividend_is_int_min.and(divisor_is_minus_one).not() } + // Builds a call to the round_to_integral CPROVER function with specified cbmc::RoundingMode. + fn codegen_round_to_integral( + &mut self, + function: BuiltinFn, + rounding_mode: cbmc::RoundingMode, + mut fargs: Vec, + place: &Place, + loc: Location, + ) -> Stmt { + assert!(function == BuiltinFn::RoundToIntegralF || function == BuiltinFn::RoundToIntegral); + let mm = self.symbol_table.machine_model(); + fargs.insert(0, Expr::int_constant(rounding_mode, Type::c_int())); + let casted_fargs = Expr::cast_arguments_to_target_equivalent_function_parameter_types( + &function.as_expr(), + fargs, + mm, + ); + let expr = function.call(casted_fargs, loc); + self.codegen_expr_to_place_stable(place, expr, loc) + } + /// Intrinsics of the form *_with_overflow fn codegen_op_with_overflow( &mut self, diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index 0c2a81c3082c..f2327b51b6ad 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -1002,8 +1002,10 @@ impl GotocCtx<'_> { // https://github.com/rust-lang/rust/blob/fee75fbe11b1fad5d93c723234178b2a329a3c03/compiler/rustc_codegen_ssa/src/mir/place.rs#L247 // See also the cranelift backend: // https://github.com/rust-lang/rust/blob/05d22212e89588e7c443cc6b9bc0e4e02fdfbc8d/compiler/rustc_codegen_cranelift/src/discriminant.rs#L116 - let offset = match &layout.fields { - FieldsShape::Arbitrary { offsets, .. } => offsets[0usize.into()], + let offset: rustc_abi::Size = match &layout.fields { + FieldsShape::Arbitrary { offsets, .. } => { + offsets[rustc_abi::FieldIdx::from_usize(0)] + } _ => unreachable!("niche encoding must have arbitrary fields"), }; diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 813e07d06723..0ffa893056be 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -7,6 +7,7 @@ use crate::codegen_cprover_gotoc::codegen::function::rustc_smir::region_from_cov use crate::codegen_cprover_gotoc::{GotocCtx, VtableCtx}; use crate::unwrap_or_return_codegen_unimplemented_stmt; use cbmc::goto_program::{Expr, Location, Stmt, Type}; +use rustc_abi::Size; use rustc_abi::{FieldsShape, Primitive, TagEncoding, Variants}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{List, TypingEnv}; @@ -350,8 +351,10 @@ impl GotocCtx<'_> { } TagEncoding::Niche { untagged_variant, niche_variants, niche_start } => { if *untagged_variant != variant_index_internal { - let offset = match &layout.fields { - FieldsShape::Arbitrary { offsets, .. } => offsets[0usize.into()], + let offset: Size = match &layout.fields { + FieldsShape::Arbitrary { offsets, .. } => { + offsets[rustc_abi::FieldIdx::from_usize(0)] + } _ => unreachable!("niche encoding must have arbitrary fields"), }; let discr_ty = self.codegen_enum_discr_typ(dest_ty_internal); diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index a22528d55407..efbcbe2e06e5 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -718,7 +718,7 @@ impl<'tcx> GotocCtx<'tcx> { let mut final_fields = Vec::with_capacity(flds.len()); let mut offset = initial_offset; for idx in layout.fields.index_by_increasing_offset() { - let fld_offset = offsets[idx.into()]; + let fld_offset = offsets[rustc_abi::FieldIdx::from(idx)]; let (fld_name, fld_ty) = &flds[idx]; if let Some(padding) = self.codegen_struct_padding(offset, fld_offset, final_fields.len()) @@ -1557,9 +1557,9 @@ impl<'tcx> GotocCtx<'tcx> { let components = fields_shape .index_by_increasing_offset() .map(|idx| { - let idx = idx.into(); - let name = fields[idx].name.to_string().intern(); - let field_ty = fields[idx].ty(ctx.tcx, adt_args); + let fidx = FieldIdx::from(idx); + let name = fields[fidx].name.to_string().intern(); + let field_ty = fields[fidx].ty(ctx.tcx, adt_args); let typ = if !ctx.is_zst(field_ty) { last_type.clone() } else { diff --git a/kani-compiler/src/intrinsics.rs b/kani-compiler/src/intrinsics.rs index f58a40bf8cb6..5d9ab8221453 100644 --- a/kani-compiler/src/intrinsics.rs +++ b/kani-compiler/src/intrinsics.rs @@ -86,8 +86,6 @@ pub enum Intrinsic { MinNumF32, MinNumF64, MulWithOverflow, - NearbyIntF32, - NearbyIntF64, NeedsDrop, PowF32, PowF64, @@ -99,12 +97,12 @@ pub enum Intrinsic { PtrOffsetFromUnsigned, RawEq, RetagBoxToRaw, - RintF32, - RintF64, RotateLeft, RotateRight, RoundF32, RoundF64, + RoundTiesEvenF32, + RoundTiesEvenF64, SaturatingAdd, SaturatingSub, SinF32, @@ -676,10 +674,6 @@ fn try_match_f32(intrinsic_instance: &Instance) -> Option { assert_sig_matches!(sig, RigidTy::Float(FloatTy::F32), RigidTy::Float(FloatTy::F32) => RigidTy::Float(FloatTy::F32)); Some(Intrinsic::MinNumF32) } - "nearbyintf32" => { - assert_sig_matches!(sig, RigidTy::Float(FloatTy::F32) => RigidTy::Float(FloatTy::F32)); - Some(Intrinsic::NearbyIntF32) - } "powf32" => { assert_sig_matches!(sig, RigidTy::Float(FloatTy::F32), RigidTy::Float(FloatTy::F32) => RigidTy::Float(FloatTy::F32)); Some(Intrinsic::PowF32) @@ -688,14 +682,14 @@ fn try_match_f32(intrinsic_instance: &Instance) -> Option { assert_sig_matches!(sig, RigidTy::Float(FloatTy::F32), RigidTy::Int(IntTy::I32) => RigidTy::Float(FloatTy::F32)); Some(Intrinsic::PowIF32) } - "rintf32" => { - assert_sig_matches!(sig, RigidTy::Float(FloatTy::F32) => RigidTy::Float(FloatTy::F32)); - Some(Intrinsic::RintF32) - } "roundf32" => { assert_sig_matches!(sig, RigidTy::Float(FloatTy::F32) => RigidTy::Float(FloatTy::F32)); Some(Intrinsic::RoundF32) } + "round_ties_even_f32" => { + assert_sig_matches!(sig, RigidTy::Float(FloatTy::F32) => RigidTy::Float(FloatTy::F32)); + Some(Intrinsic::RoundTiesEvenF32) + } "sinf32" => { assert_sig_matches!(sig, RigidTy::Float(FloatTy::F32) => RigidTy::Float(FloatTy::F32)); Some(Intrinsic::SinF32) @@ -770,10 +764,6 @@ fn try_match_f64(intrinsic_instance: &Instance) -> Option { assert_sig_matches!(sig, RigidTy::Float(FloatTy::F64), RigidTy::Float(FloatTy::F64) => RigidTy::Float(FloatTy::F64)); Some(Intrinsic::MinNumF64) } - "nearbyintf64" => { - assert_sig_matches!(sig, RigidTy::Float(FloatTy::F64) => RigidTy::Float(FloatTy::F64)); - Some(Intrinsic::NearbyIntF64) - } "powf64" => { assert_sig_matches!(sig, RigidTy::Float(FloatTy::F64), RigidTy::Float(FloatTy::F64) => RigidTy::Float(FloatTy::F64)); Some(Intrinsic::PowF64) @@ -782,14 +772,14 @@ fn try_match_f64(intrinsic_instance: &Instance) -> Option { assert_sig_matches!(sig, RigidTy::Float(FloatTy::F64), RigidTy::Int(IntTy::I32) => RigidTy::Float(FloatTy::F64)); Some(Intrinsic::PowIF64) } - "rintf64" => { - assert_sig_matches!(sig, RigidTy::Float(FloatTy::F64) => RigidTy::Float(FloatTy::F64)); - Some(Intrinsic::RintF64) - } "roundf64" => { assert_sig_matches!(sig, RigidTy::Float(FloatTy::F64) => RigidTy::Float(FloatTy::F64)); Some(Intrinsic::RoundF64) } + "round_ties_even_f64" => { + assert_sig_matches!(sig, RigidTy::Float(FloatTy::F64) => RigidTy::Float(FloatTy::F64)); + Some(Intrinsic::RoundTiesEvenF64) + } "sinf64" => { assert_sig_matches!(sig, RigidTy::Float(FloatTy::F64) => RigidTy::Float(FloatTy::F64)); Some(Intrinsic::SinF64) diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index bdd6db831d6c..0e27a3bdc3b6 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -8,7 +8,7 @@ use kani_metadata::{CbmcSolver, HarnessAttributes, HarnessKind, Stub}; use quote::ToTokens; use rustc_ast::{LitKind, MetaItem, MetaItemKind, attr}; use rustc_errors::ErrorGuaranteed; -use rustc_hir::{AttrArgs, AttrKind, Attribute, def::DefKind, def_id::DefId}; +use rustc_hir::{AttrArgs, Attribute, def::DefKind, def_id::DefId}; use rustc_middle::ty::{Instance, TyCtxt, TyKind}; use rustc_session::Session; use rustc_smir::rustc_internal; @@ -214,7 +214,7 @@ impl<'tcx> KaniAttributes<'tcx> { .resolve_from_mod(name.as_str()) .map_err(|e| { let mut err = self.tcx.dcx().struct_span_err( - attr.span, + attr.span(), format!( "Failed to resolve replacement function {}: {e}", name.as_str() @@ -229,7 +229,7 @@ impl<'tcx> KaniAttributes<'tcx> { err.emit(); }) .ok()?; - Some((name, def, attr.span)) + Some((name, def, attr.span())) }) .collect() } @@ -247,10 +247,10 @@ impl<'tcx> KaniAttributes<'tcx> { self.expect_maybe_one(KaniAttributeKind::ProofForContract).and_then(|target| { let name = expect_key_string_value(self.tcx.sess, target).ok()?; self.resolve_from_mod(name.as_str()) - .map(|ok| (name, ok, target.span)) + .map(|ok| (name, ok, target.span())) .map_err(|resolve_err| { let mut err = self.tcx.dcx().struct_span_err( - target.span, + target.span(), format!( "Failed to resolve checking function {} because {resolve_err}", name.as_str() @@ -336,7 +336,7 @@ impl<'tcx> KaniAttributes<'tcx> { // Check that all attributes are correctly used and well formed. let is_harness = self.is_proof_harness(); for (&kind, attrs) in self.map.iter() { - let local_error = |msg| self.tcx.dcx().span_err(attrs[0].span, msg); + let local_error = |msg| self.tcx.dcx().span_err(attrs[0].span(), msg); if !is_harness && kind.is_harness_only() { local_error(format!( @@ -451,7 +451,7 @@ impl<'tcx> KaniAttributes<'tcx> { kind.as_ref() ); if let Some(attr) = self.map.get(&kind).unwrap().first() { - self.tcx.dcx().span_err(attr.span, msg); + self.tcx.dcx().span_err(attr.span(), msg); } else { self.tcx.dcx().err(msg); } @@ -646,7 +646,7 @@ impl<'tcx> KaniAttributes<'tcx> { /// Check that if this item is tagged with a proof_attribute, it is a valid harness. fn check_proof_attribute(&self, kind: KaniAttributeKind, proof_attribute: &Attribute) { - let span = proof_attribute.span; + let span = proof_attribute.span(); let tcx = self.tcx; if let KaniAttributeKind::Proof = kind { expect_no_args(tcx, kind, proof_attribute); @@ -719,7 +719,7 @@ fn expect_key_string_value( sess: &Session, attr: &Attribute, ) -> Result { - let span = attr.span; + let span = attr.span(); let AttrArgs::Eq { expr, .. } = &attr.get_normal_item().args else { return Err(sess .dcx() @@ -743,7 +743,7 @@ fn expect_single<'a>( .expect(&format!("expected at least one attribute {} in {attributes:?}", kind.as_ref())); if attributes.len() > 1 { tcx.dcx().span_err( - attr.span, + attr.span(), format!("only one '#[kani::{}]' attribute is allowed per harness", kind.as_ref()), ); } @@ -774,7 +774,7 @@ impl UnstableAttrParseError<'_> { fn report(&self, tcx: TyCtxt) -> ErrorGuaranteed { tcx.dcx() .struct_span_err( - self.attr.span, + self.attr.span(), format!("failed to parse `#[kani::unstable_feature]`: {}", self.reason), ) .with_note(format!( @@ -817,7 +817,7 @@ impl<'a> TryFrom<&'a Attribute> for UnstableAttribute { fn expect_no_args(tcx: TyCtxt, kind: KaniAttributeKind, attr: &Attribute) { if !attr.is_word() { tcx.dcx() - .struct_span_err(attr.span, format!("unexpected argument for `{}`", kind.as_ref())) + .struct_span_err(attr.span(), format!("unexpected argument for `{}`", kind.as_ref())) .with_help("remove the extra argument") .emit(); } @@ -830,7 +830,7 @@ fn parse_unwind(tcx: TyCtxt, attr: &Attribute) -> Option { None => { // There are no integers or too many arguments given to the attribute tcx.dcx().span_err( - attr.span, + attr.span(), "invalid argument for `unwind` attribute, expected an integer", ); None @@ -839,7 +839,7 @@ fn parse_unwind(tcx: TyCtxt, attr: &Attribute) -> Option { if let Ok(val) = unwind_integer_value.try_into() { Some(val) } else { - tcx.dcx().span_err(attr.span, "value above maximum permitted value - u32::MAX"); + tcx.dcx().span_err(attr.span(), "value above maximum permitted value - u32::MAX"); None } } @@ -854,13 +854,13 @@ fn parse_stubs(tcx: TyCtxt, harness: DefId, attributes: &[&Attribute]) -> Vec { /* no-op */ } Ok(FnResolution::FnImpl { .. }) => { tcx.dcx().span_err( - attr.span, + attr.span(), "Kani currently does not support stubbing trait implementations.", ); } Err(err) => { tcx.dcx().span_err( - attr.span, + attr.span(), format!("failed to resolve `{}`: {err}", pretty_type_path(path)), ); } @@ -871,7 +871,7 @@ fn parse_stubs(tcx: TyCtxt, harness: DefId, attributes: &[&Attribute]) -> Vec Vec { tcx.dcx().span_err( - attr.span, + attr.span(), format!( "attribute `kani::stub` takes two path arguments; found {}", paths.len() @@ -912,7 +912,7 @@ fn parse_solver(tcx: TyCtxt, attr: &Attribute) -> Option { const ATTRIBUTE: &str = "#[kani::solver]"; let invalid_arg_err = |attr: &Attribute| { tcx.dcx().span_err( - attr.span, + attr.span(), format!("invalid argument for `{ATTRIBUTE}` attribute, expected one of the supported solvers (e.g. `kissat`) or a SAT solver binary (e.g. `bin=\"\"`)"), ) }; @@ -920,7 +920,7 @@ fn parse_solver(tcx: TyCtxt, attr: &Attribute) -> Option { let attr_args = attr.meta_item_list().unwrap(); if attr_args.len() != 1 { tcx.dcx().span_err( - attr.span, + attr.span(), format!( "the `{ATTRIBUTE}` attribute expects a single argument. Got {} arguments.", attr_args.len() @@ -943,7 +943,7 @@ fn parse_solver(tcx: TyCtxt, attr: &Attribute) -> Option { match solver { Ok(solver) => Some(solver), Err(_) => { - tcx.dcx().span_err(attr.span, format!("unknown solver `{ident_str}`")); + tcx.dcx().span_err(attr.span(), format!("unknown solver `{ident_str}`")); None } } @@ -1016,26 +1016,25 @@ fn parse_str_value(attr: &Attribute) -> Option { /// If the attribute is named `kanitool::name`, this extracts `name` fn attr_kind(tcx: TyCtxt, attr: &Attribute) -> Option { - match &attr.kind { - AttrKind::Normal(normal) => { - let segments = &normal.path.segments; - if (!segments.is_empty()) && segments[0].as_str() == "kanitool" { - let ident_str = segments[1..] - .iter() - .map(|segment| segment.as_str()) - .intersperse("::") - .collect::(); - KaniAttributeKind::try_from(ident_str.as_str()) - .inspect_err(|&err| { - debug!(?err, "attr_kind_failed"); - tcx.dcx().span_err(attr.span, format!("unknown attribute `{ident_str}`")); - }) - .ok() - } else { - None - } + if let Attribute::Unparsed(normal) = attr { + let segments = &normal.path.segments; + if (!segments.is_empty()) && segments[0].as_str() == "kanitool" { + let ident_str = segments[1..] + .iter() + .map(|segment| segment.as_str()) + .intersperse("::") + .collect::(); + KaniAttributeKind::try_from(ident_str.as_str()) + .inspect_err(|&err| { + debug!(?err, "attr_kind_failed"); + tcx.dcx().span_err(attr.span(), format!("unknown attribute `{ident_str}`")); + }) + .ok() + } else { + None } - _ => None, + } else { + None } } @@ -1099,7 +1098,7 @@ fn pretty_type_path(path: &TypePath) -> String { /// Retrieve the value of the `fn_marker` attribute for the given definition if it has one. pub(crate) fn fn_marker(def: T) -> Option { let fn_marker: [SymbolStable; 2] = ["kanitool".into(), "fn_marker".into()]; - let marker = def.attrs_by_path(&fn_marker).pop()?; + let marker = def.tool_attrs(&fn_marker).pop()?; let attribute = syn_attr_stable(&marker); let meta_name = attribute.meta.require_name_value().unwrap_or_else(|_| { panic!("Expected name value attribute for `kanitool::fn_marker`, but found: `{:?}`", marker) diff --git a/kani-compiler/src/kani_middle/intrinsics.rs b/kani-compiler/src/kani_middle/intrinsics.rs index a3451c99e029..a53abef90e88 100644 --- a/kani-compiler/src/kani_middle/intrinsics.rs +++ b/kani-compiler/src/kani_middle/intrinsics.rs @@ -93,7 +93,7 @@ fn simd_len_and_type<'tcx>(tcx: TyCtxt<'tcx>, simd_ty: Ty<'tcx>) -> (Const<'tcx> ty::Adt(def, args) => { assert!(def.repr().simd(), "`simd_size_and_type` called on non-SIMD type"); let variant = def.non_enum_variant(); - let f0_ty = variant.fields[0u32.into()].ty(tcx, args); + let f0_ty = variant.fields[rustc_abi::FieldIdx::from_usize(0)].ty(tcx, args); match f0_ty.kind() { ty::Array(elem_ty, len) => (*len, *elem_ty), diff --git a/kani-compiler/src/kani_middle/kani_functions.rs b/kani-compiler/src/kani_middle/kani_functions.rs index 01d237bf0773..85936041cc38 100644 --- a/kani-compiler/src/kani_middle/kani_functions.rs +++ b/kani-compiler/src/kani_middle/kani_functions.rs @@ -87,8 +87,8 @@ pub enum KaniModel { Offset, #[strum(serialize = "PtrOffsetFromModel")] PtrOffsetFrom, - #[strum(serialize = "PtrSubPtrModel")] - PtrSubPtr, + #[strum(serialize = "PtrOffsetFromUnsignedModel")] + PtrOffsetFromUnsigned, #[strum(serialize = "RunContractModel")] RunContract, #[strum(serialize = "RunLoopContractModel")] diff --git a/kani-compiler/src/kani_middle/points_to/points_to_analysis.rs b/kani-compiler/src/kani_middle/points_to/points_to_analysis.rs index a89baf963e7f..2008ef1eb0b8 100644 --- a/kani-compiler/src/kani_middle/points_to/points_to_analysis.rs +++ b/kani-compiler/src/kani_middle/points_to/points_to_analysis.rs @@ -664,8 +664,6 @@ fn is_identity_aliasing_intrinsic(intrinsic: Intrinsic) -> bool { | Intrinsic::MinNumF32 | Intrinsic::MinNumF64 | Intrinsic::MulWithOverflow - | Intrinsic::NearbyIntF32 - | Intrinsic::NearbyIntF64 | Intrinsic::NeedsDrop | Intrinsic::PowF32 | Intrinsic::PowF64 @@ -677,12 +675,12 @@ fn is_identity_aliasing_intrinsic(intrinsic: Intrinsic) -> bool { | Intrinsic::PtrOffsetFromUnsigned | Intrinsic::RawEq | Intrinsic::RetagBoxToRaw - | Intrinsic::RintF32 - | Intrinsic::RintF64 | Intrinsic::RotateLeft | Intrinsic::RotateRight | Intrinsic::RoundF32 | Intrinsic::RoundF64 + | Intrinsic::RoundTiesEvenF32 + | Intrinsic::RoundTiesEvenF64 | Intrinsic::SaturatingAdd | Intrinsic::SaturatingSub | Intrinsic::SinF32 diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index fdc81e177d1f..e8bff532f1e7 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -83,6 +83,14 @@ where crate_items .iter() .filter_map(|item| { + // avoid stable MIR panic + // https://github.com/model-checking/kani/issues/3919 + if let Ok(instance) = Instance::try_from(*item) { + let int_def_id = rustc_internal::internal(tcx, instance.def.def_id()); + if matches!(tcx.def_kind(int_def_id), rustc_hir::def::DefKind::GlobalAsm) { + return None; + } + }; // Only collect monomorphic items. matches!(item.kind(), ItemKind::Fn) .then(|| { diff --git a/kani-compiler/src/kani_middle/resolve.rs b/kani-compiler/src/kani_middle/resolve.rs index 8f278ae20ed8..accb4807f00e 100644 --- a/kani-compiler/src/kani_middle/resolve.rs +++ b/kani-compiler/src/kani_middle/resolve.rs @@ -307,7 +307,7 @@ fn resolve_prefix<'tcx>( (None, Some(segment)) if segment.ident == CRATE => { // Find the module at the root of the crate. let current_module_hir_id = tcx.local_def_id_to_hir_id(current_module); - let crate_root = match tcx.hir().parent_iter(current_module_hir_id).last() { + let crate_root = match tcx.hir_parent_iter(current_module_hir_id).last() { None => current_module, Some((hir_id, _)) => hir_id.owner.def_id, }; @@ -366,7 +366,7 @@ where I: Iterator, { let current_module_hir_id = tcx.local_def_id_to_hir_id(current_module); - let mut parents = tcx.hir().parent_iter(current_module_hir_id); + let mut parents = tcx.hir_parent_iter(current_module_hir_id); let mut base_module = current_module; while segments.next_if(|segment| segment.ident == SUPER).is_some() { if let Some((parent, _)) = parents.next() { diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs b/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs index 20f4538453b5..02d928eff75e 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs @@ -625,8 +625,6 @@ fn can_skip_intrinsic(intrinsic: Intrinsic) -> bool { | Intrinsic::MinNumF32 | Intrinsic::MinNumF64 | Intrinsic::MulWithOverflow - | Intrinsic::NearbyIntF32 - | Intrinsic::NearbyIntF64 | Intrinsic::NeedsDrop | Intrinsic::PowF32 | Intrinsic::PowF64 @@ -634,8 +632,6 @@ fn can_skip_intrinsic(intrinsic: Intrinsic) -> bool { | Intrinsic::PowIF64 | Intrinsic::PrefAlignOf | Intrinsic::RawEq - | Intrinsic::RintF32 - | Intrinsic::RintF64 | Intrinsic::RotateLeft | Intrinsic::RotateRight | Intrinsic::RoundF32 diff --git a/kani-compiler/src/kani_middle/transform/check_values.rs b/kani-compiler/src/kani_middle/transform/check_values.rs index 8689bf6c59ac..b536de1e2695 100644 --- a/kani-compiler/src/kani_middle/transform/check_values.rs +++ b/kani-compiler/src/kani_middle/transform/check_values.rs @@ -213,7 +213,6 @@ impl ValidValueReq { } ValueAbi::Scalar(_) | ValueAbi::ScalarPair(_, _) - | ValueAbi::Uninhabited | ValueAbi::Vector { .. } | ValueAbi::Aggregate { .. } => None, } diff --git a/kani-compiler/src/kani_middle/transform/rustc_intrinsics.rs b/kani-compiler/src/kani_middle/transform/rustc_intrinsics.rs index 79238e423e62..43f8e5b7013f 100644 --- a/kani-compiler/src/kani_middle/transform/rustc_intrinsics.rs +++ b/kani-compiler/src/kani_middle/transform/rustc_intrinsics.rs @@ -168,7 +168,9 @@ impl MutMirVisitor for ReplaceIntrinsicCallVisitor<'_> { Intrinsic::SizeOfVal => self.models[&KaniModel::SizeOfVal], Intrinsic::MinAlignOfVal => self.models[&KaniModel::AlignOfVal], Intrinsic::PtrOffsetFrom => self.models[&KaniModel::PtrOffsetFrom], - Intrinsic::PtrOffsetFromUnsigned => self.models[&KaniModel::PtrSubPtr], + Intrinsic::PtrOffsetFromUnsigned => { + self.models[&KaniModel::PtrOffsetFromUnsigned] + } // The rest is handled in codegen. _ => { return self.super_terminator(term); diff --git a/library/kani_core/src/models.rs b/library/kani_core/src/models.rs index 8913c399c959..39fd786f0249 100644 --- a/library/kani_core/src/models.rs +++ b/library/kani_core/src/models.rs @@ -89,8 +89,8 @@ macro_rules! generate_models { } } - #[kanitool::fn_marker = "PtrSubPtrModel"] - pub unsafe fn ptr_sub_ptr(ptr1: *const T, ptr2: *const T) -> usize { + #[kanitool::fn_marker = "PtrOffsetFromUnsignedModel"] + pub unsafe fn ptr_offset_from_unsigned(ptr1: *const T, ptr2: *const T) -> usize { let offset = ptr_offset_from(ptr1, ptr2); kani::safety_check(offset >= 0, "Expected non-negative distance between pointers"); offset as usize diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 8078beff6d73..4167255ebd78 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-02-21" +channel = "nightly-2025-03-02" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/expected/arbitrary/ptrs/pointer_generator_error.expected b/tests/expected/arbitrary/ptrs/pointer_generator_error.expected index a0592c586a03..06496f4313d7 100644 --- a/tests/expected/arbitrary/ptrs/pointer_generator_error.expected +++ b/tests/expected/arbitrary/ptrs/pointer_generator_error.expected @@ -1,6 +1,6 @@ error[E0080]: evaluation of `kani::PointerGenerator::<0>::VALID` failed\ -the evaluated program panicked at 'PointerGenerator requires at least one byte.' +evaluation panicked: PointerGenerator requires at least one byte.\ note: the above error was encountered while instantiating `fn kani::PointerGenerator::<0>::new`\ pointer_generator_error.rs\ diff --git a/tests/expected/offset-bounds-check/sub_ptr.expected b/tests/expected/offset-bounds-check/offset_from_unsigned.expected similarity index 52% rename from tests/expected/offset-bounds-check/sub_ptr.expected rename to tests/expected/offset-bounds-check/offset_from_unsigned.expected index 611fe2e565c7..fff02a3d96af 100644 --- a/tests/expected/offset-bounds-check/sub_ptr.expected +++ b/tests/expected/offset-bounds-check/offset_from_unsigned.expected @@ -1,29 +1,29 @@ -Checking harness check_sub_ptr_same_dangling... +Checking harness check_offset_from_unsigned_same_dangling... VERIFICATION:- SUCCESSFUL -Checking harness check_sub_ptr_unit_panic... +Checking harness check_offset_from_unsigned_unit_panic... Failed Checks: assertion failed: 0 < pointee_size && pointee_size <= isize::MAX as usize VERIFICATION:- SUCCESSFUL (encountered one or more panics as expected) -Checking harness check_sub_ptr_negative_result... +Checking harness check_offset_from_unsigned_negative_result... Failed Checks: Expected non-negative distance between pointers VERIFICATION:- FAILED -Checking harness check_sub_ptr_diff_alloc... +Checking harness check_offset_from_unsigned_diff_alloc... Failed Checks: Offset result and original pointer should point to the same allocation VERIFICATION:- FAILED -Checking harness check_sub_ptr_oob_ptr... +Checking harness check_offset_from_unsigned_oob_ptr... Failed Checks: Offset result and original pointer should point to the same allocation VERIFICATION:- FAILED -Checking harness check_sub_ptr_self_oob... +Checking harness check_offset_from_unsigned_self_oob... Failed Checks: Offset result and original pointer should point to the same allocation VERIFICATION:- FAILED Summary: -Verification failed for - check_sub_ptr_negative_result -Verification failed for - check_sub_ptr_diff_alloc -Verification failed for - check_sub_ptr_oob_ptr -Verification failed for - check_sub_ptr_self_oob +Verification failed for - check_offset_from_unsigned_negative_result +Verification failed for - check_offset_from_unsigned_diff_alloc +Verification failed for - check_offset_from_unsigned_oob_ptr +Verification failed for - check_offset_from_unsigned_self_oob Complete - 2 successfully verified harnesses, 4 failures, 6 total. diff --git a/tests/expected/offset-bounds-check/sub_ptr.rs b/tests/expected/offset-bounds-check/offset_from_unsigned.rs similarity index 62% rename from tests/expected/offset-bounds-check/sub_ptr.rs rename to tests/expected/offset-bounds-check/offset_from_unsigned.rs index 7ddfa96d94f8..f5b11a741403 100644 --- a/tests/expected/offset-bounds-check/sub_ptr.rs +++ b/tests/expected/offset-bounds-check/offset_from_unsigned.rs @@ -1,47 +1,47 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -//! Check that Kani can detect UB due to `sub_ptr` with out-of-bounds pointer or wrong order. +//! Check that Kani can detect UB due to `offset_from_unsigned` with out-of-bounds pointer or wrong order. #![feature(ptr_sub_ptr)] #[kani::proof] -fn check_sub_ptr_self_oob() { +fn check_offset_from_unsigned_self_oob() { let val = 10u128; let ptr: *const u128 = &val; let ptr_oob: *const u128 = ptr.wrapping_add(10); // SAFETY: This is not safe! - let _offset = unsafe { ptr_oob.sub_ptr(ptr) }; + let _offset = unsafe { ptr_oob.offset_from_unsigned(ptr) }; } #[kani::proof] -fn check_sub_ptr_oob_ptr() { +fn check_offset_from_unsigned_oob_ptr() { let val = 10u128; let ptr: *const u128 = &val; let ptr_oob: *const u128 = ptr.wrapping_sub(10); // SAFETY: This is not safe! - let _offset = unsafe { ptr.sub_ptr(ptr_oob) }; + let _offset = unsafe { ptr.offset_from_unsigned(ptr_oob) }; } #[kani::proof] -fn check_sub_ptr_diff_alloc() { +fn check_offset_from_unsigned_diff_alloc() { let val1 = kani::any(); let val2 = kani::any(); let ptr1: *const u128 = &val1; let ptr2: *const u128 = &val2; // SAFETY: This is not safe! - let _offset = unsafe { ptr1.sub_ptr(ptr2) }; + let _offset = unsafe { ptr1.offset_from_unsigned(ptr2) }; } #[kani::proof] -fn check_sub_ptr_negative_result() { +fn check_offset_from_unsigned_negative_result() { let val: [u8; 10] = kani::any(); let ptr_first: *const _ = &val[0]; let ptr_last: *const _ = &val[9]; // SAFETY: This is safe! - let offset_ok = unsafe { ptr_last.sub_ptr(ptr_first) }; + let offset_ok = unsafe { ptr_last.offset_from_unsigned(ptr_first) }; // SAFETY: This is not safe! - let offset_not_ok = unsafe { ptr_first.sub_ptr(ptr_last) }; + let offset_not_ok = unsafe { ptr_first.offset_from_unsigned(ptr_last) }; // Just use the result. assert!(offset_ok != offset_not_ok); @@ -49,22 +49,22 @@ fn check_sub_ptr_negative_result() { #[kani::proof] #[kani::should_panic] -fn check_sub_ptr_unit_panic() { +fn check_offset_from_unsigned_unit_panic() { let val1 = kani::any(); let val2 = kani::any(); let ptr1: *const () = &val1 as *const _ as *const (); let ptr2: *const () = &val2 as *const _ as *const (); // SAFETY: This is safe but will panic... - let _offset = unsafe { ptr1.sub_ptr(ptr2) }; + let _offset = unsafe { ptr1.offset_from_unsigned(ptr2) }; } #[kani::proof] -fn check_sub_ptr_same_dangling() { +fn check_offset_from_unsigned_same_dangling() { let val = 10u128; let ptr: *const u128 = &val; let ptr_oob_1: *const u128 = ptr.wrapping_add(10); let ptr_oob_2: *const u128 = ptr.wrapping_add(5).wrapping_add(5); // SAFETY: This is safe since the pointer is the same! - let offset = unsafe { ptr_oob_1.sub_ptr(ptr_oob_2) }; + let offset = unsafe { ptr_oob_1.offset_from_unsigned(ptr_oob_2) }; assert_eq!(offset, 0); } diff --git a/tests/kani/Intrinsics/Math/Rounding/RInt/rintf32.rs b/tests/kani/Intrinsics/Math/Rounding/RInt/rintf32.rs deleted file mode 100644 index 79a0a4f9be2c..000000000000 --- a/tests/kani/Intrinsics/Math/Rounding/RInt/rintf32.rs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT - -// Checks that `rintf32` returns the nearest integer to the argument. The -// default rounding mode is rounding half to even, which is described here: -// https://en.wikipedia.org/wiki/Rounding#Round_half_to_even -// -// `rintf32` works like `nearbyintf32`, but it may raise an inexact -// floating-point exception which isn't supported in Rust: -// https://github.com/rust-lang/rust/issues/10186 -// So in practice, `rintf32` and `nearbyintf32` work the same way. -#![feature(core_intrinsics)] -use std::intrinsics::rintf32; - -#[kani::proof] -fn test_one() { - let one = 1.0; - let result = unsafe { rintf32(one) }; - assert!(result == 1.0); -} - -#[kani::proof] -fn test_one_frac() { - let one_frac = 1.9; - let result = unsafe { rintf32(one_frac) }; - assert!(result == 2.0); -} - -#[kani::proof] -fn test_half_down() { - let one_frac = 2.5; - let result = unsafe { rintf32(one_frac) }; - assert!(result == 2.0); -} - -#[kani::proof] -fn test_half_up() { - let one_frac = 3.5; - let result = unsafe { rintf32(one_frac) }; - assert!(result == 4.0); -} - -#[kani::proof] -fn test_conc() { - let conc = -42.6; - let result = unsafe { rintf32(conc) }; - assert!(result == -43.0); -} - -#[kani::proof] -fn test_conc_sci() { - let conc = 5.4e-2; - let result = unsafe { rintf32(conc) }; - assert!(result == 0.0); -} - -#[kani::proof] -fn test_towards_nearest() { - let x: f32 = kani::any(); - kani::assume(!x.is_nan()); - kani::assume(!x.is_infinite()); - let result = unsafe { rintf32(x) }; - let frac = x.fract().abs(); - if x.is_sign_positive() { - if frac > 0.5 { - assert!(result > x); - } else if frac < 0.5 { - assert!(result <= x); - } else { - // This would fail if conversion checks were on - let integer = x as i64; - if integer % 2 == 0 { - assert!(result < x); - } else { - assert!(result > x); - } - } - } else { - if frac > 0.5 { - assert!(result < x); - } else if frac < 0.5 { - assert!(result >= x); - } else { - // This would fail if conversion checks were on - let integer = x as i64; - if integer % 2 == 0 { - assert!(result > x); - } else { - assert!(result < x); - } - } - } -} - -#[kani::proof] -fn test_diff_half_one() { - let x: f32 = kani::any(); - kani::assume(!x.is_nan()); - kani::assume(!x.is_infinite()); - let result = unsafe { rintf32(x) }; - let diff = (x - result).abs(); - assert!(diff <= 0.5); - assert!(diff >= 0.0); -} diff --git a/tests/kani/Intrinsics/Math/Rounding/RInt/rintf64.rs b/tests/kani/Intrinsics/Math/Rounding/RInt/rintf64.rs deleted file mode 100644 index 8c8ea583a2d5..000000000000 --- a/tests/kani/Intrinsics/Math/Rounding/RInt/rintf64.rs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT - -// Checks that `rintf64` returns the nearest integer to the argument. The -// default rounding mode is rounding half to even, which is described here: -// https://en.wikipedia.org/wiki/Rounding#Round_half_to_even -// -// `rintf64` works like `nearbyintf64`, but it may raise an inexact -// floating-point exception which isn't supported in Rust: -// https://github.com/rust-lang/rust/issues/10186 -// So in practice, `rintf64` and `nearbyintf64` work the same way. -#![feature(core_intrinsics)] -use std::intrinsics::rintf64; - -#[kani::proof] -fn test_one() { - let one = 1.0; - let result = unsafe { rintf64(one) }; - assert!(result == 1.0); -} - -#[kani::proof] -fn test_one_frac() { - let one_frac = 1.9; - let result = unsafe { rintf64(one_frac) }; - assert!(result == 2.0); -} - -#[kani::proof] -fn test_half_down() { - let one_frac = 2.5; - let result = unsafe { rintf64(one_frac) }; - assert!(result == 2.0); -} - -#[kani::proof] -fn test_half_up() { - let one_frac = 3.5; - let result = unsafe { rintf64(one_frac) }; - assert!(result == 4.0); -} - -#[kani::proof] -fn test_conc() { - let conc = -42.6; - let result = unsafe { rintf64(conc) }; - assert!(result == -43.0); -} - -#[kani::proof] -fn test_conc_sci() { - let conc = 5.4e-2; - let result = unsafe { rintf64(conc) }; - assert!(result == 0.0); -} - -#[kani::proof] -fn test_towards_nearest() { - let x: f64 = kani::any(); - kani::assume(!x.is_nan()); - kani::assume(!x.is_infinite()); - let result = unsafe { rintf64(x) }; - let frac = x.fract().abs(); - if x.is_sign_positive() { - if frac > 0.5 { - assert!(result > x); - } else if frac < 0.5 { - assert!(result <= x); - } else { - // This would fail if conversion checks were on - let integer = x as i64; - if integer % 2 == 0 { - assert!(result < x); - } else { - assert!(result > x); - } - } - } else { - if frac > 0.5 { - assert!(result < x); - } else if frac < 0.5 { - assert!(result >= x); - } else { - // This would fail if conversion checks were on - let integer = x as i64; - if integer % 2 == 0 { - assert!(result > x); - } else { - assert!(result < x); - } - } - } -} - -#[kani::proof] -fn test_diff_half_one() { - let x: f64 = kani::any(); - kani::assume(!x.is_nan()); - kani::assume(!x.is_infinite()); - let result = unsafe { rintf64(x) }; - let diff = (x - result).abs(); - assert!(diff <= 0.5); - assert!(diff >= 0.0); -} diff --git a/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf32.rs b/tests/kani/Intrinsics/Math/Rounding/RoundTiesEven/round_ties_even_f32.rs similarity index 79% rename from tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf32.rs rename to tests/kani/Intrinsics/Math/Rounding/RoundTiesEven/round_ties_even_f32.rs index 25e02f45a943..057483b3b0cc 100644 --- a/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf32.rs +++ b/tests/kani/Intrinsics/Math/Rounding/RoundTiesEven/round_ties_even_f32.rs @@ -1,51 +1,51 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// Checks that `nearbyintf32` returns the nearest integer to the argument. The +// Checks that `round_ties_even_f32` returns the nearest integer to the argument. The // default rounding mode is rounding half to even, which is described here: // https://en.wikipedia.org/wiki/Rounding#Round_half_to_even #![feature(core_intrinsics)] -use std::intrinsics::nearbyintf32; +use std::intrinsics::round_ties_even_f32; #[kani::proof] fn test_one() { let one = 1.0; - let result = unsafe { nearbyintf32(one) }; + let result = round_ties_even_f32(one); assert!(result == 1.0); } #[kani::proof] fn test_one_frac() { let one_frac = 1.9; - let result = unsafe { nearbyintf32(one_frac) }; + let result = round_ties_even_f32(one_frac); assert!(result == 2.0); } #[kani::proof] fn test_half_down() { let one_frac = 2.5; - let result = unsafe { nearbyintf32(one_frac) }; + let result = round_ties_even_f32(one_frac); assert!(result == 2.0); } #[kani::proof] fn test_half_up() { let one_frac = 3.5; - let result = unsafe { nearbyintf32(one_frac) }; + let result = round_ties_even_f32(one_frac); assert!(result == 4.0); } #[kani::proof] fn test_conc() { let conc = -42.6; - let result = unsafe { nearbyintf32(conc) }; + let result = round_ties_even_f32(conc); assert!(result == -43.0); } #[kani::proof] fn test_conc_sci() { let conc = 5.4e-2; - let result = unsafe { nearbyintf32(conc) }; + let result = round_ties_even_f32(conc); assert!(result == 0.0); } @@ -54,7 +54,7 @@ fn test_towards_nearest() { let x: f32 = kani::any(); kani::assume(!x.is_nan()); kani::assume(!x.is_infinite()); - let result = unsafe { nearbyintf32(x) }; + let result = round_ties_even_f32(x); let frac = x.fract().abs(); if x.is_sign_positive() { if frac > 0.5 { @@ -92,7 +92,7 @@ fn test_diff_half_one() { let x: f32 = kani::any(); kani::assume(!x.is_nan()); kani::assume(!x.is_infinite()); - let result = unsafe { nearbyintf32(x) }; + let result = round_ties_even_f32(x); let diff = (x - result).abs(); assert!(diff <= 0.5); assert!(diff >= 0.0); diff --git a/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf64.rs b/tests/kani/Intrinsics/Math/Rounding/RoundTiesEven/round_ties_even_f64.rs similarity index 79% rename from tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf64.rs rename to tests/kani/Intrinsics/Math/Rounding/RoundTiesEven/round_ties_even_f64.rs index 589a44a4d1ac..c7c1a9611bae 100644 --- a/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf64.rs +++ b/tests/kani/Intrinsics/Math/Rounding/RoundTiesEven/round_ties_even_f64.rs @@ -1,51 +1,51 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// Checks that `nearbyintf64` returns the nearest integer to the argument. The +// Checks that `round_ties_even_f64` returns the nearest integer to the argument. The // default rounding mode is rounding half to even, which is described here: // https://en.wikipedia.org/wiki/Rounding#Round_half_to_even #![feature(core_intrinsics)] -use std::intrinsics::nearbyintf64; +use std::intrinsics::round_ties_even_f64; #[kani::proof] fn test_one() { let one = 1.0; - let result = unsafe { nearbyintf64(one) }; + let result = round_ties_even_f64(one); assert!(result == 1.0); } #[kani::proof] fn test_one_frac() { let one_frac = 1.9; - let result = unsafe { nearbyintf64(one_frac) }; + let result = round_ties_even_f64(one_frac); assert!(result == 2.0); } #[kani::proof] fn test_half_down() { let one_frac = 2.5; - let result = unsafe { nearbyintf64(one_frac) }; + let result = round_ties_even_f64(one_frac); assert!(result == 2.0); } #[kani::proof] fn test_half_up() { let one_frac = 3.5; - let result = unsafe { nearbyintf64(one_frac) }; + let result = round_ties_even_f64(one_frac); assert!(result == 4.0); } #[kani::proof] fn test_conc() { let conc = -42.6; - let result = unsafe { nearbyintf64(conc) }; + let result = round_ties_even_f64(conc); assert!(result == -43.0); } #[kani::proof] fn test_conc_sci() { let conc = 5.4e-2; - let result = unsafe { nearbyintf64(conc) }; + let result = round_ties_even_f64(conc); assert!(result == 0.0); } @@ -54,7 +54,7 @@ fn test_towards_nearest() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); kani::assume(!x.is_infinite()); - let result = unsafe { nearbyintf64(x) }; + let result = round_ties_even_f64(x); let frac = x.fract().abs(); if x.is_sign_positive() { if frac > 0.5 { @@ -92,7 +92,7 @@ fn test_diff_half_one() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); kani::assume(!x.is_infinite()); - let result = unsafe { nearbyintf64(x) }; + let result = round_ties_even_f64(x); let diff = (x - result).abs(); assert!(diff <= 0.5); assert!(diff >= 0.0); diff --git a/tests/kani/PointerOffset/offset_from_vec.rs b/tests/kani/PointerOffset/offset_from_vec.rs index 482b4905df3c..29865e2c8f81 100644 --- a/tests/kani/PointerOffset/offset_from_vec.rs +++ b/tests/kani/PointerOffset/offset_from_vec.rs @@ -18,6 +18,6 @@ fn offset_non_power_two() { let offset = kani::any_where(|o: &usize| *o <= v.len()); let begin = v.as_mut_ptr(); let end = begin.add(offset); - assert_eq!(end.sub_ptr(begin), offset); + assert_eq!(end.offset_from_unsigned(begin), offset); } } From 68cb4ee4bea61514c93277919dadcd3b9caf098d Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Wed, 5 Mar 2025 20:36:44 -0500 Subject: [PATCH 040/161] Autoharness Misc. Improvements (#3922) Follow up to https://github.com/model-checking/kani/pull/3874 implementing the features requested during review. Hopefully reviewing commit-by-commit makes the size manageable (most of the changes come from the additional metadata logging) but happy to split this into multiple PRs if not. The list below maps each commit to the review comment on https://github.com/model-checking/kani/pull/3874 that inspired it. - 86859257546de3ed1da1493ae772b31aa75e2eb5: https://github.com/model-checking/kani/pull/3874#discussion_r1945282348 - f060d66e01973b57e281953c935b89332bb870b9: https://github.com/model-checking/kani/pull/3874#discussion_r1950007068 - 7a7f6548efb707fda468dbfc43772157e3631e9b: https://github.com/model-checking/kani/pull/3874#discussion_r1956657233 - 4f3add81ac0a6e856d1979853eb9b1dd2ddc3b15: https://github.com/model-checking/kani/pull/3874#discussion_r1950071073 - 212b0ffe1e8f3ecab07a3b091357b99eef121414: https://github.com/model-checking/kani/pull/3874#discussion_r1950034157 - f105a9c796cfa88bc81bdda7b5c1127c0a183a0e: https://github.com/model-checking/kani/pull/3874#pullrequestreview-2599556038 and - 6a8dc61ef682d93a3db46b29cdf9c0522631741c: https://github.com/model-checking/kani/pull/3874#pullrequestreview-2599556038 - 6f946bb7937f8b2c40b9ce5d4a89b62ac151d019 and 6997afb86730c66216b99ded74d71fdca4916858: https://github.com/model-checking/kani/pull/3874#issuecomment-2640076433 Towards https://github.com/model-checking/kani/issues/3832 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../src/reference/experimental/autoharness.md | 49 +++- kani-compiler/src/args.rs | 10 +- .../compiler_interface.rs | 1 + kani-compiler/src/kani_middle/attributes.rs | 11 + .../src/kani_middle/codegen_units.rs | 211 ++++++++++++------ kani-driver/src/args/autoharness_args.rs | 18 +- kani-driver/src/autoharness/mod.rs | 140 ++++++++++-- kani-driver/src/call_cbmc.rs | 3 +- kani-driver/src/harness_runner.rs | 16 +- kani-driver/src/main.rs | 33 +-- kani-driver/src/metadata.rs | 2 + kani_metadata/src/harness.rs | 6 +- kani_metadata/src/lib.rs | 33 ++- kani_metadata/src/unstable.rs | 2 + .../contracts.expected | 28 ++- .../cargo_autoharness_contracts/contracts.sh | 2 +- .../cargo_autoharness_dependencies/Cargo.toml | 12 + .../cargo_autoharness_dependencies/config.yml | 4 + .../dependencies.expected | 14 ++ .../dependencies.sh | 9 + .../other_crate/Cargo.toml | 8 + .../other_crate/src/lib.rs | 6 + .../cargo_autoharness_dependencies/src/lib.rs | 10 + .../exclude.expected | 23 +- .../cargo_autoharness_exclude/exclude.sh | 2 +- .../cargo_autoharness_filter/filter.expected | 114 +++++++++- .../cargo_autoharness_filter/filter.sh | 2 +- .../harnesses_fail.expected | 33 ++- .../harnesses_fail.sh | 2 +- .../include.expected | 23 +- .../cargo_autoharness_include/include.sh | 2 +- .../loops.expected | 1 - .../cargo_autoharness_loops_fixme/src/lib.rs | 39 ---- .../Cargo.toml | 2 +- .../config.yml | 4 +- .../cargo_autoharness_termination/src/lib.rs | 38 ++++ .../termination.expected | 42 ++++ .../termination.sh} | 2 +- 38 files changed, 742 insertions(+), 215 deletions(-) create mode 100644 tests/script-based-pre/cargo_autoharness_dependencies/Cargo.toml create mode 100644 tests/script-based-pre/cargo_autoharness_dependencies/config.yml create mode 100644 tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected create mode 100755 tests/script-based-pre/cargo_autoharness_dependencies/dependencies.sh create mode 100644 tests/script-based-pre/cargo_autoharness_dependencies/other_crate/Cargo.toml create mode 100644 tests/script-based-pre/cargo_autoharness_dependencies/other_crate/src/lib.rs create mode 100644 tests/script-based-pre/cargo_autoharness_dependencies/src/lib.rs delete mode 100644 tests/script-based-pre/cargo_autoharness_loops_fixme/loops.expected delete mode 100644 tests/script-based-pre/cargo_autoharness_loops_fixme/src/lib.rs rename tests/script-based-pre/{cargo_autoharness_loops_fixme => cargo_autoharness_termination}/Cargo.toml (83%) rename tests/script-based-pre/{cargo_autoharness_loops_fixme => cargo_autoharness_termination}/config.yml (58%) create mode 100644 tests/script-based-pre/cargo_autoharness_termination/src/lib.rs create mode 100644 tests/script-based-pre/cargo_autoharness_termination/termination.expected rename tests/script-based-pre/{cargo_autoharness_loops_fixme/loops.sh => cargo_autoharness_termination/termination.sh} (61%) diff --git a/docs/src/reference/experimental/autoharness.md b/docs/src/reference/experimental/autoharness.md index 022d6471ea0e..67f46a9abf47 100644 --- a/docs/src/reference/experimental/autoharness.md +++ b/docs/src/reference/experimental/autoharness.md @@ -8,18 +8,18 @@ Recall the harness for `estimate_size` that we wrote in [First Steps](../../tuto This harness first declares a local variable `x` using `kani::any()`, then calls `estimate_size` with argument `x`. Many proof harnesses follow this predictable format—to verify a function `foo`, we create arbitrary values for each of `foo`'s arguments, then call `foo` on those arguments. -The `autoharness` subcommand leverages this observation to automatically generate and run harnesses. +The `autoharness` subcommand leverages this observation to automatically generate harnesses and run Kani against them. Kani scans the crate for functions whose arguments all implement the `kani::Arbitrary` trait, generates harnesses for them, then runs them. -These harnesses are internal to Kani--i.e., Kani does not make any changes to your source code. +These harnesses are internal to Kani—i.e., Kani does not make any changes to your source code. ## Usage Run either: ``` -# cargo kani autoharness -Z unstable-options +# cargo kani autoharness -Z autoharness ``` or ``` -# kani autoharness -Z unstable-options +# kani autoharness -Z autoharness ``` If Kani detects that all of a function `foo`'s arguments implement `kani::Arbitrary`, it will generate and run a `#[kani::proof]` harness, which prints: @@ -42,11 +42,11 @@ These flags look for partial matches against the fully qualified name of a funct For example, if a module `my_module` has many functions, but we are only interested in `my_module::foo` and `my_module::bar`, we can run: ``` -cargo run autoharness -Z unstable-options --include-function foo include-function bar +cargo run autoharness -Z autoharness --include-function foo --include-function bar ``` To exclude `my_module` entirely, run: ``` -cargo run autoharness -Z unstable-options --exclude-function my_module +cargo run autoharness -Z autoharness --exclude-function my_module ``` ## Example @@ -58,7 +58,7 @@ Using the `estimate_size` example from [First Steps](../../tutorial-first-steps. We get: ``` -# cargo kani autoharness -Z unstable-options +# cargo kani autoharness -Z autoharness Autoharness: Checking function estimate_size against all possible inputs... RESULTS: Check 3: estimate_size.assertion.1 @@ -76,9 +76,44 @@ If you have ideas for improving the user experience of this feature, please add them to [this GitHub issue](https://github.com/model-checking/kani/issues/3832). ## Limitations +### Arguments Implementing Arbitrary Kani will only generate an automatic harness for a function if it can determine that all of the function's arguments implement Arbitrary. It does not attempt to derive/implement Arbitrary for any types, even if those types could implement Arbitrary. +For example, imagine a user defines `struct MyStruct { x: u8, y: u8}`, but does not derive or implement Arbitrary for `MyStruct`. +Kani does not attempt to add such derivations itself, so it will not generate a harness for a function that takes `MyStruct` as input. +### Generic Functions +The current implementation does not generate harnesses for generic functions. +For example, given: +```rust +fn foo(x: T, y: T) { + if x == y { + panic!("x and y are equal"); + } +} +``` +Kani would report that no functions were eligible for automatic harness generation. + +If, however, some caller of `foo` is eligible for an automatic harness, then a monomorphized version of `foo` may still be reachable during verification. +For instance, if we add `main`: +```rust +fn main() { + let x: u8 = 2; + let y: u8 = 2; + foo(x, y); +} +``` +and run the autoharness subcommand, we get: +``` +Autoharness: Checking function main against all possible inputs... + +Failed Checks: x and y are equal + File: "src/lib.rs", line 3, in foo:: + +VERIFICATION:- FAILED +``` + +### Loop Contracts If a function contains a loop with a loop contract, Kani will detect the presence of a loop contract and verify that contract. If, however, the loop does not have a contract, then there is currently no way to specify an unwinding bound for the function, meaning that Kani may hang as it tries to unwind the loop. We recommend using the `--exclude-function` option to exclude any functions that have this issue (or `--harness-timeout` to bail after attempting verification for some amount of time). \ No newline at end of file diff --git a/kani-compiler/src/args.rs b/kani-compiler/src/args.rs index e524b6e5ba2a..ce969d195675 100644 --- a/kani-compiler/src/args.rs +++ b/kani-compiler/src/args.rs @@ -91,10 +91,14 @@ pub struct Arguments { /// Print the final LLBC file to stdout. #[clap(long)] pub print_llbc: bool, - /// If we are running the autoharness subcommand, the functions to autoharness - #[arg(long = "autoharness-include-function", num_args(1))] + /// If we are running the autoharness subcommand, the functions to include + #[arg( + long = "autoharness-include-function", + num_args(1), + conflicts_with = "autoharness_excluded_functions" + )] pub autoharness_included_functions: Vec, - /// If we are running the autoharness subcommand, the functions to exclude from autoverification + /// If we are running the autoharness subcommand, the functions to exclude #[arg(long = "autoharness-exclude-function", num_args(1))] pub autoharness_excluded_functions: Vec, } diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index d6f353fe995d..57b149c2bc98 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -613,6 +613,7 @@ impl GotoCodegenResults { // removes any contracts logic for ReachabilityType::Test or ReachabilityType::PubFns, // which are the two ReachabilityTypes under which the compiler calls this function. contracted_functions: vec![], + autoharness_skipped_fns: None, } } diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index 0e27a3bdc3b6..20d5f3ad6a4b 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -310,6 +310,17 @@ impl<'tcx> KaniAttributes<'tcx> { }) } + // Is this a function inserted by Kani instrumentation? + pub fn is_kani_instrumentation(&self) -> bool { + self.fn_marker().is_some() || self.is_contract_generated() + } + + // Is this a contract-generated function? + // Note that this function currently always returns false because of https://github.com/model-checking/kani/issues/3921 + fn is_contract_generated(&self) -> bool { + self.map.contains_key(&KaniAttributeKind::IsContractGenerated) + } + /// Return a function marker if any. pub fn fn_marker(&self) -> Option { self.attribute_value(KaniAttributeKind::FnMarker) diff --git a/kani-compiler/src/kani_middle/codegen_units.rs b/kani-compiler/src/kani_middle/codegen_units.rs index 1fc659ea2f80..189c14b246b3 100644 --- a/kani-compiler/src/kani_middle/codegen_units.rs +++ b/kani-compiler/src/kani_middle/codegen_units.rs @@ -7,7 +7,7 @@ //! Today, only stub / contracts can affect the harness codegen. Thus, we group the harnesses //! according to their stub configuration. -use crate::args::ReachabilityType; +use crate::args::{Arguments, ReachabilityType}; use crate::kani_middle::attributes::{KaniAttributes, is_proof_harness}; use crate::kani_middle::kani_functions::{KaniIntrinsic, KaniModel}; use crate::kani_middle::metadata::{ @@ -17,14 +17,17 @@ use crate::kani_middle::reachability::filter_crate_items; use crate::kani_middle::resolve::expect_resolve_fn; use crate::kani_middle::stubbing::{check_compatibility, harness_stub_map}; use crate::kani_queries::QueryDb; -use kani_metadata::{ArtifactType, AssignsContract, HarnessKind, HarnessMetadata, KaniMetadata}; +use kani_metadata::{ + ArtifactType, AssignsContract, AutoHarnessSkipReason, AutoHarnessSkippedFns, HarnessKind, + HarnessMetadata, KaniMetadata, +}; use rustc_hir::def_id::DefId; use rustc_middle::ty::TyCtxt; use rustc_session::config::OutputType; use rustc_smir::rustc_internal; -use stable_mir::CrateDef; use stable_mir::mir::{TerminatorKind, mono::Instance}; use stable_mir::ty::{FnDef, GenericArgKind, GenericArgs, IndexedVal, RigidTy, TyKind}; +use stable_mir::{CrateDef, CrateItem}; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::fs::File; use std::io::BufWriter; @@ -46,9 +49,10 @@ struct CrateInfo { /// We group the harnesses that have the same stubs. pub struct CodegenUnits { - units: Vec, - harness_info: HashMap, + autoharness_skipped_fns: Option, crate_info: CrateInfo, + harness_info: HashMap, + units: Vec, } #[derive(Clone, Default, Debug)] @@ -70,7 +74,12 @@ impl CodegenUnits { let units = group_by_stubs(tcx, &all_harnesses); validate_units(tcx, &units); debug!(?units, "CodegenUnits::new"); - CodegenUnits { units, harness_info: all_harnesses, crate_info } + CodegenUnits { + units, + harness_info: all_harnesses, + crate_info, + autoharness_skipped_fns: None, + } } ReachabilityType::AllFns => { let mut all_harnesses = get_all_manual_harnesses(tcx, base_filename); @@ -80,23 +89,16 @@ impl CodegenUnits { let kani_fns = queries.kani_functions(); let kani_harness_intrinsic = kani_fns.get(&KaniIntrinsic::AutomaticHarness.into()).unwrap(); - let kani_any_inst = kani_fns.get(&KaniModel::Any.into()).unwrap(); - - let verifiable_fns = filter_crate_items(tcx, |_, instance: Instance| -> bool { - // If the user specified functions to include or exclude, only allow instances matching those filters. - let user_included = if !args.autoharness_included_functions.is_empty() { - fn_list_contains_instance(&instance, &args.autoharness_included_functions) - } else if !args.autoharness_excluded_functions.is_empty() { - !fn_list_contains_instance(&instance, &args.autoharness_excluded_functions) - } else { - true - }; - user_included - && is_eligible_for_automatic_harness(tcx, instance, *kani_any_inst) - }); + + let (chosen, skipped) = automatic_harness_partition( + tcx, + args, + *kani_fns.get(&KaniModel::Any.into()).unwrap(), + ); + let automatic_harnesses = get_all_automatic_harnesses( tcx, - verifiable_fns, + chosen, *kani_harness_intrinsic, base_filename, ); @@ -111,14 +113,25 @@ impl CodegenUnits { }) .collect::>(), ); - all_harnesses.extend(automatic_harnesses); + all_harnesses.extend(automatic_harnesses.clone()); + // No need to validate the units again because validation only checks stubs, and we haven't added any stubs. debug!(?units, "CodegenUnits::new"); - CodegenUnits { units, harness_info: all_harnesses, crate_info } + CodegenUnits { + units, + harness_info: all_harnesses, + crate_info, + autoharness_skipped_fns: Some(skipped), + } } _ => { // Leave other reachability type handling as is for now. - CodegenUnits { units: vec![], harness_info: HashMap::default(), crate_info } + CodegenUnits { + units: vec![], + harness_info: HashMap::default(), + crate_info, + autoharness_skipped_fns: None, + } } } } @@ -139,13 +152,6 @@ impl CodegenUnits { for harness in harnesses { let metadata = self.harness_info.get_mut(harness).unwrap(); metadata.has_loop_contracts = true; - // If we're generating this harness automatically and we encounter a loop contract, - // ensure that the HarnessKind is updated to be a contract harness - // targeting the function to verify. - if metadata.is_automatically_generated { - metadata.attributes.kind = - HarnessKind::ProofForContract { target_fn: metadata.pretty_name.clone() } - } } } @@ -170,6 +176,7 @@ impl CodegenUnits { unsupported_features: vec![], test_harnesses, contracted_functions: gen_contracts_metadata(tcx), + autoharness_skipped_fns: self.autoharness_skipped_fns.clone(), } } } @@ -346,46 +353,112 @@ fn get_all_automatic_harnesses( .collect::>() } -/// Determine whether `instance` is eligible for automatic verification. -fn is_eligible_for_automatic_harness(tcx: TyCtxt, instance: Instance, any_inst: FnDef) -> bool { - // `instance` is ineligble if it is a harness or has an nonexistent/empty body - if is_proof_harness(tcx, instance) || !instance.has_body() { - return false; - } - let body = instance.body().unwrap(); - - // `instance` is ineligble if it is an associated item of a Kani trait implementation, - // or part of Kani contract instrumentation. - // FIXME -- find a less hardcoded way of checking the former condition (perhaps upstream PR to StableMIR). - if instance.name().contains("kani::Arbitrary") - || instance.name().contains("kani::Invariant") - || KaniAttributes::for_instance(tcx, instance) - .fn_marker() - .is_some_and(|m| m.as_str() == "kani_contract_mode") - { - return false; - } +/// Partition every function in the crate into (chosen, skipped), where `chosen` is a vector of the Instances for which we'll generate automatic harnesses, +/// and `skipped` is a map of function names to the reason why we skipped them. +fn automatic_harness_partition( + tcx: TyCtxt, + args: &Arguments, + kani_any_def: FnDef, +) -> (Vec, BTreeMap) { + // If `filter_list` contains `name`, either as an exact match or a substring. + let filter_contains = |name: &str, filter_list: &[String]| -> bool { + filter_list.iter().any(|filter_name| name.contains(filter_name)) + }; + + // If `func` is not eligible for an automatic harness, return the reason why; if it is eligible, return None. + let skip_reason = |fn_item: CrateItem| -> Option { + if KaniAttributes::for_def_id(tcx, fn_item.def_id()).is_kani_instrumentation() { + return Some(AutoHarnessSkipReason::KaniImpl); + } - // Each non-generic argument of `instance`` must implement Arbitrary. - body.arg_locals().iter().all(|arg| { - let kani_any_body = - Instance::resolve(any_inst, &GenericArgs(vec![GenericArgKind::Type(arg.ty)])) - .unwrap() - .body() - .unwrap(); - if let TerminatorKind::Call { func, .. } = &kani_any_body.blocks[0].terminator.kind { - if let Some((def, args)) = func.ty(body.arg_locals()).unwrap().kind().fn_def() { - return Instance::resolve(def, &args).is_ok(); + let instance = match Instance::try_from(fn_item) { + Ok(inst) => inst, + Err(_) => { + return Some(AutoHarnessSkipReason::GenericFn); } + }; + + if !instance.has_body() { + return Some(AutoHarnessSkipReason::NoBody); } - false - }) -} -/// Return whether the name of `instance` is included in `fn_list`. -/// If `exact = true`, then `instance`'s exact, fully-qualified name must be in `fn_list`; otherwise, `fn_list` -/// can have a substring of `instance`'s name. -fn fn_list_contains_instance(instance: &Instance, fn_list: &[String]) -> bool { - let pretty_name = instance.name(); - fn_list.contains(&pretty_name) || fn_list.iter().any(|fn_name| pretty_name.contains(fn_name)) + let name = instance.name(); + let body = instance.body().unwrap(); + + if is_proof_harness(tcx, instance) + || name.contains("kani::Arbitrary") + || name.contains("kani::Invariant") + { + return Some(AutoHarnessSkipReason::KaniImpl); + } + + if (!args.autoharness_included_functions.is_empty() + && !filter_contains(&name, &args.autoharness_included_functions)) + || (!args.autoharness_excluded_functions.is_empty() + && filter_contains(&name, &args.autoharness_excluded_functions)) + { + return Some(AutoHarnessSkipReason::UserFilter); + } + + // Each argument of `instance` must implement Arbitrary. + // Note that we've already filtered out generic functions, so we know that each of these arguments has a concrete type. + let mut problematic_args = vec![]; + for (idx, arg) in body.arg_locals().iter().enumerate() { + let kani_any_body = + Instance::resolve(kani_any_def, &GenericArgs(vec![GenericArgKind::Type(arg.ty)])) + .unwrap() + .body() + .unwrap(); + + let implements_arbitrary = if let TerminatorKind::Call { func, .. } = + &kani_any_body.blocks[0].terminator.kind + { + if let Some((def, args)) = func.ty(body.arg_locals()).unwrap().kind().fn_def() { + Instance::resolve(def, &args).is_ok() + } else { + false + } + } else { + false + }; + + if !implements_arbitrary { + // Find the name of the argument by referencing var_debug_info. + // Note that enumerate() starts at 0, while StableMIR argument_index starts at 1, hence the idx+1. + let arg_debug_info = body + .var_debug_info + .iter() + .find(|var| { + var.argument_index.is_some_and(|arg_idx| idx + 1 == usize::from(arg_idx)) + }) + .expect("Arguments should have corresponding var debug info"); + problematic_args.push(arg_debug_info.name.to_string()) + } + } + if !problematic_args.is_empty() { + return Some(AutoHarnessSkipReason::MissingArbitraryImpl(problematic_args)); + } + None + }; + + let mut chosen = vec![]; + let mut skipped = BTreeMap::new(); + + // FIXME: ideally, this filter would be matches!(item.kind(), ItemKind::Fn), since that would allow us to generate harnesses for top-level closures, + // c.f. https://github.com/model-checking/kani/issues/3832#issuecomment-2701671798. + // Note that filtering closures out here is a UX choice: we could instead call skip_reason() on closures, + // but the limitations in the linked issue would cause the user to be flooded with reports of "skipping" Kani instrumentation functions. + // Instead, we just pretend closures don't exist in our reporting of what we did and did not verify, which has the downside of also ignoring the user's top-level closures, but those are rare. + let crate_fns = + stable_mir::all_local_items().into_iter().filter(|item| item.ty().kind().is_fn()); + + for func in crate_fns { + if let Some(reason) = skip_reason(func) { + skipped.insert(func.name(), reason); + } else { + chosen.push(Instance::try_from(func).unwrap()); + } + } + + (chosen, skipped) } diff --git a/kani-driver/src/args/autoharness_args.rs b/kani-driver/src/args/autoharness_args.rs index d7a69070bf41..1e778e1f3646 100644 --- a/kani-driver/src/args/autoharness_args.rs +++ b/kani-driver/src/args/autoharness_args.rs @@ -61,17 +61,12 @@ pub struct StandaloneAutoharnessArgs { impl ValidateArgs for CargoAutoharnessArgs { fn validate(&self) -> Result<(), Error> { self.verify_opts.validate()?; - if !self - .verify_opts - .common_args - .unstable_features - .contains(UnstableFeature::UnstableOptions) - { + if !self.verify_opts.common_args.unstable_features.contains(UnstableFeature::Autoharness) { return Err(Error::raw( ErrorKind::MissingRequiredArgument, format!( "The `autoharness` subcommand is unstable and requires -Z {}", - UnstableFeature::UnstableOptions + UnstableFeature::Autoharness ), )); } @@ -95,17 +90,12 @@ impl ValidateArgs for CargoAutoharnessArgs { impl ValidateArgs for StandaloneAutoharnessArgs { fn validate(&self) -> Result<(), Error> { self.verify_opts.validate()?; - if !self - .verify_opts - .common_args - .unstable_features - .contains(UnstableFeature::UnstableOptions) - { + if !self.verify_opts.common_args.unstable_features.contains(UnstableFeature::Autoharness) { return Err(Error::raw( ErrorKind::MissingRequiredArgument, format!( "The `autoharness` subcommand is unstable and requires -Z {}", - UnstableFeature::UnstableOptions + UnstableFeature::Autoharness ), )); } diff --git a/kani-driver/src/autoharness/mod.rs b/kani-driver/src/autoharness/mod.rs index 69c49580cd77..4faef3a69071 100644 --- a/kani-driver/src/autoharness/mod.rs +++ b/kani-driver/src/autoharness/mod.rs @@ -1,11 +1,91 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT +use std::str::FromStr; + +use crate::args::Timeout; +use crate::args::autoharness_args::{CargoAutoharnessArgs, StandaloneAutoharnessArgs}; use crate::call_cbmc::VerificationStatus; use crate::call_single_file::to_rustc_arg; use crate::harness_runner::HarnessResult; use crate::session::KaniSession; +use crate::{InvocationType, print_kani_version, project, verify_project}; use anyhow::Result; +use comfy_table::Table as PrettyTable; +use kani_metadata::{AutoHarnessSkipReason, KaniMetadata}; + +const AUTOHARNESS_TIMEOUT: &str = "30s"; +const LOOP_UNWIND_DEFAULT: u32 = 20; + +pub fn autoharness_cargo(args: CargoAutoharnessArgs) -> Result<()> { + let mut session = KaniSession::new(args.verify_opts)?; + session.enable_autoharness(); + session.add_default_bounds(); + session.add_auto_harness_args( + args.common_autoharness_args.include_function, + args.common_autoharness_args.exclude_function, + ); + let project = project::cargo_project(&mut session, false)?; + let metadata = project.metadata.clone(); + let res = verify_project(project, session); + print_skipped_fns(metadata); + res +} + +pub fn autoharness_standalone(args: StandaloneAutoharnessArgs) -> Result<()> { + let mut session = KaniSession::new(args.verify_opts)?; + session.enable_autoharness(); + session.add_default_bounds(); + session.add_auto_harness_args( + args.common_autoharness_args.include_function, + args.common_autoharness_args.exclude_function, + ); + + if !session.args.common_args.quiet { + print_kani_version(InvocationType::Standalone); + } + + let project = project::standalone_project(&args.input, args.crate_name, &session)?; + let metadata = project.metadata.clone(); + let res = verify_project(project, session); + print_skipped_fns(metadata); + res +} + +/// Print a table of the functions that we skipped and why. +fn print_skipped_fns(metadata: Vec) { + let mut skipped_fns = PrettyTable::new(); + skipped_fns.set_header(vec!["Skipped Function", "Reason for Skipping"]); + + for md in metadata { + let skipped_md = md.autoharness_skipped_fns.unwrap(); + skipped_fns.add_rows(skipped_md.into_iter().filter_map(|(func, reason)| match reason { + AutoHarnessSkipReason::MissingArbitraryImpl(ref args) => { + Some(vec![func, format!("{}: {}", reason, args.join(", "))]) + } + AutoHarnessSkipReason::GenericFn + | AutoHarnessSkipReason::NoBody + | AutoHarnessSkipReason::UserFilter => Some(vec![func, reason.to_string()]), + // We don't report Kani implementations to the user to avoid exposing Kani functions we insert during instrumentation. + // For those we don't insert during instrumentation that are in this category (manual harnesses or Kani trait implementations), + // it should be obvious that we wouldn't generate harnesses, so reporting those functions as "skipped" is unlikely to be useful. + AutoHarnessSkipReason::KaniImpl => None, + })); + } + + if skipped_fns.is_empty() { + println!( + "\nSkipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s)." + ); + return; + } + + println!("\nKani did not generate automatic harnesses for the following functions."); + println!( + "If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832" + ); + println!("{skipped_fns}"); +} impl KaniSession { /// Enable autoharness mode. @@ -25,6 +105,18 @@ impl KaniSession { } } + /// Add global harness timeout and loop unwinding bounds if not provided. + /// These prevent automatic harnesses from hanging. + pub fn add_default_bounds(&mut self) { + if self.args.harness_timeout.is_none() { + let timeout = Timeout::from_str(AUTOHARNESS_TIMEOUT).unwrap(); + self.args.harness_timeout = Some(timeout); + } + if self.args.default_unwind.is_none() { + self.args.default_unwind = Some(LOOP_UNWIND_DEFAULT); + } + } + /// Prints the results from running the `autoharness` subcommand. pub fn print_autoharness_summary(&self, automatic: Vec<&HarnessResult<'_>>) -> Result<()> { let (successes, failures): (Vec<_>, Vec<_>) = @@ -34,40 +126,50 @@ impl KaniSession { let failing = failures.len(); let total = succeeding + failing; - // TODO: it would be nice if we had access to which functions the user included/excluded here - // so that we could print a comparison for them of any of the included functions that we skipped. - println!("Autoharness Summary:"); - println!( - "Note that Kani will only generate an automatic harness for a function if it determines that each of its arguments implement the Arbitrary trait." - ); - println!( - "Examine the summary closely to determine which functions were automatically verified." - ); + println!("\nAutoharness Summary:"); + + let mut verified_fns = PrettyTable::new(); + verified_fns.set_header(vec![ + "Selected Function", + "Kind of Automatic Harness", + "Verification Result", + ]); - // Since autoverification skips over some functions, print the successes to make it easier to see what we verified in one place. for success in successes { - println!("Verification succeeded for - {}", success.harness.pretty_name); + verified_fns.add_row(vec![ + success.harness.pretty_name.clone(), + success.harness.attributes.kind.to_string(), + success.result.status.to_string(), + ]); } for failure in failures { - println!("Verification failed for - {}", failure.harness.pretty_name); + verified_fns.add_row(vec![ + failure.harness.pretty_name.clone(), + failure.harness.attributes.kind.to_string(), + failure.result.status.to_string(), + ]); } - if total > 0 { + println!("{verified_fns}"); + + if failing > 0 { println!( - "Complete - {succeeding} successfully verified functions, {failing} failures, {total} total." + "Note that `kani autoharness` sets default --harness-timeout of {AUTOHARNESS_TIMEOUT} and --default-unwind of {LOOP_UNWIND_DEFAULT}." ); - } else { println!( - "No functions were eligible for automatic verification. Functions can only be automatically verified if each of their arguments implement kani::Arbitrary." + "If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract)." ); + } + + if total > 0 { println!( - "If you specified --include-function or --exclude-function, make sure that your filters were not overly restrictive." + "Complete - {succeeding} successfully verified functions, {failing} failures, {total} total." ); + } else { + println!("No functions were eligible for automatic verification."); } - // Manual harness summary may come afterward, so separate them with a new line. - println!(); Ok(()) } } diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index 92c82f25888d..96351a4095ee 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -12,6 +12,7 @@ use std::fmt::Write; use std::path::Path; use std::sync::OnceLock; use std::time::{Duration, Instant}; +use strum_macros::Display; use tokio::process::Command as TokioCommand; use crate::args::common::Verbosity; @@ -29,7 +30,7 @@ use crate::util::render_command; /// Note: Kissat was marginally better, but it is an external solver which could be more unstable. static DEFAULT_SOLVER: CbmcSolver = CbmcSolver::Cadical; -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, Display, PartialEq, Eq)] pub enum VerificationStatus { Success, Failure, diff --git a/kani-driver/src/harness_runner.rs b/kani-driver/src/harness_runner.rs index 56e3f4c09ed2..2b52aa78d0e6 100644 --- a/kani-driver/src/harness_runner.rs +++ b/kani-driver/src/harness_runner.rs @@ -246,10 +246,6 @@ impl KaniSession { let (automatic, manual): (Vec<_>, Vec<_>) = results.iter().partition(|r| r.harness.is_automatically_generated); - if self.auto_harness { - self.print_autoharness_summary(automatic)?; - } - let (successes, failures): (Vec<_>, Vec<_>) = manual.into_iter().partition(|r| r.result.status == VerificationStatus::Success); @@ -269,10 +265,8 @@ impl KaniSession { } } - // We currently omit a summary if there was just 1 harness - if failing > 0 { - println!("Manual Harness Summary:"); - } + println!("Manual Harness Summary:"); + for failure in failures.iter() { println!("Verification failed for - {}", failure.harness.pretty_name); } @@ -304,7 +298,11 @@ impl KaniSession { self.show_coverage_summary()?; } - if failing > 0 { + if self.auto_harness { + self.print_autoharness_summary(automatic)?; + } + + if failing > 0 && !self.auto_harness { // Failure exit code without additional error message drop(self); std::process::exit(1); diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 9b82c8467e26..5e32e458fa0a 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -5,6 +5,7 @@ use std::ffi::OsString; use std::process::ExitCode; use anyhow::Result; +use autoharness::{autoharness_cargo, autoharness_standalone}; use time::{OffsetDateTime, format_description}; use args::{CargoKaniSubcommand, check_is_valid}; @@ -73,25 +74,21 @@ fn cargokani_main(input_args: Vec) -> Result<()> { return list_cargo(*list_args, args.verify_opts); } + if let Some(CargoKaniSubcommand::Autoharness(autoharness_args)) = args.command { + return autoharness_cargo(*autoharness_args); + } + let mut session = match args.command { Some(CargoKaniSubcommand::Assess(assess_args)) => { let sess = session::KaniSession::new(args.verify_opts)?; return assess::run_assess(sess, *assess_args); } - Some(CargoKaniSubcommand::Autoharness(args)) => { - let mut sess = session::KaniSession::new(args.verify_opts)?; - sess.enable_autoharness(); - sess.add_auto_harness_args( - args.common_autoharness_args.include_function, - args.common_autoharness_args.exclude_function, - ); - - sess - } Some(CargoKaniSubcommand::Playback(args)) => { return playback_cargo(*args); } - Some(CargoKaniSubcommand::List(_)) => unreachable!(), + Some(CargoKaniSubcommand::Autoharness(_)) | Some(CargoKaniSubcommand::List(_)) => { + unreachable!() + } None => session::KaniSession::new(args.verify_opts)?, }; @@ -114,19 +111,7 @@ fn standalone_main() -> Result<()> { let (session, project) = match args.command { Some(StandaloneSubcommand::Autoharness(args)) => { - let mut session = KaniSession::new(args.verify_opts)?; - session.enable_autoharness(); - session.add_auto_harness_args( - args.common_autoharness_args.include_function, - args.common_autoharness_args.exclude_function, - ); - - if !session.args.common_args.quiet { - print_kani_version(InvocationType::Standalone); - } - - let project = project::standalone_project(&args.input, args.crate_name, &session)?; - (session, project) + return autoharness_standalone(*args); } Some(StandaloneSubcommand::Playback(args)) => return playback_standalone(*args), Some(StandaloneSubcommand::List(list_args)) => { diff --git a/kani-driver/src/metadata.rs b/kani-driver/src/metadata.rs index d112591c6532..8a7ee8690fc8 100644 --- a/kani-driver/src/metadata.rs +++ b/kani-driver/src/metadata.rs @@ -97,6 +97,7 @@ pub fn merge_kani_metadata(files: Vec) -> KaniMetadata { unsupported_features: vec![], test_harnesses: vec![], contracted_functions: vec![], + autoharness_skipped_fns: None, }; for md in files { // Note that we're taking ownership of the original vec, and so we can move the data into the new data structure. @@ -106,6 +107,7 @@ pub fn merge_kani_metadata(files: Vec) -> KaniMetadata { result.unsupported_features.extend(md.unsupported_features); result.test_harnesses.extend(md.test_harnesses); result.contracted_functions.extend(md.contracted_functions); + // We do not handle autoharness metadata here, since this function is not reachable from the autoharness subcommand. } result } diff --git a/kani_metadata/src/harness.rs b/kani_metadata/src/harness.rs index 227fe8df09d4..7e945b51d67c 100644 --- a/kani_metadata/src/harness.rs +++ b/kani_metadata/src/harness.rs @@ -4,6 +4,7 @@ use crate::CbmcSolver; use serde::{Deserialize, Serialize}; use std::path::PathBuf; +use strum_macros::Display; /// A CBMC-level `assigns` contract that needs to be enforced on a function. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] @@ -59,13 +60,16 @@ pub struct HarnessAttributes { pub verified_stubs: Vec, } -#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Clone, Eq, PartialEq, Debug, Display, Serialize, Deserialize)] pub enum HarnessKind { /// Function was annotated with `#[kani::proof]`. + #[strum(serialize = "#[kani::proof]")] Proof, /// Function was annotated with `#[kani::proof_for_contract(target_fn)]`. + #[strum(serialize = "#[kani::proof_for_contract]")] ProofForContract { target_fn: String }, /// This is a test harness annotated with `#[test]`. + #[strum(serialize = "#[test]")] Test, } diff --git a/kani_metadata/src/lib.rs b/kani_metadata/src/lib.rs index 96caf92133e9..2fe1038647c1 100644 --- a/kani_metadata/src/lib.rs +++ b/kani_metadata/src/lib.rs @@ -4,7 +4,11 @@ extern crate clap; use serde::{Deserialize, Serialize}; -use std::{collections::HashSet, path::PathBuf}; +use std::{ + collections::{BTreeMap, HashSet}, + path::PathBuf, +}; +use strum_macros::{Display, EnumString}; pub use artifact::ArtifactType; pub use cbmc_solver::CbmcSolver; @@ -33,8 +37,35 @@ pub struct KaniMetadata { pub test_harnesses: Vec, /// The functions with contracts in this crate pub contracted_functions: Vec, + /// Metadata for the `autoharness` subcommand + pub autoharness_skipped_fns: Option, } +/// Reasons that Kani does not generate an automatic harness for a function. +#[derive(Debug, Clone, Serialize, Deserialize, Display, EnumString)] +pub enum AutoHarnessSkipReason { + /// The function is generic. + #[strum(serialize = "Generic Function")] + GenericFn, + /// A Kani-internal function: already a harness, implementation of a Kani associated item or Kani contract instrumentation functions). + #[strum(serialize = "Kani implementation")] + KaniImpl, + /// At least one of the function's arguments does not implement kani::Arbitrary + /// (The Vec contains the list of argument names that do not implement it) + #[strum(serialize = "Missing Arbitrary implementation for argument(s)")] + MissingArbitraryImpl(Vec), + /// The function does not have a body. + #[strum(serialize = "The function does not have a body")] + NoBody, + /// The function doesn't match the user's provided filters. + #[strum(serialize = "Did not match provided filters")] + UserFilter, +} + +/// For the autoharness subcommand: map function names to the reason why we did not generate an automatic harness for that function. +/// We use an ordered map so that when we print the data, it is ordered alphabetically by function name. +pub type AutoHarnessSkippedFns = BTreeMap; + #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] pub struct ContractedFunction { /// The fully qualified name the user gave to the function (i.e. includes the module path). diff --git a/kani_metadata/src/unstable.rs b/kani_metadata/src/unstable.rs index cb7aff27fc68..a901f11b357d 100644 --- a/kani_metadata/src/unstable.rs +++ b/kani_metadata/src/unstable.rs @@ -104,6 +104,8 @@ pub enum UnstableFeature { FloatLib, /// Enable vtable restriction. RestrictVtable, + /// Enable the autoharness subcommand. + Autoharness, } impl UnstableFeature { diff --git a/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected b/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected index 1e25913b6765..fd3fd419a73f 100644 --- a/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected +++ b/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected @@ -3,7 +3,7 @@ assertion\ - Status: FAILURE\ - Description: "|result : &u32| *result == x" -Autoharness: Checking function should_pass::has_loop_contract's contract against all possible inputs... +Autoharness: Checking function should_pass::has_loop_contract against all possible inputs... should_pass::has_loop_contract.assertion\ - Status: SUCCESS\ - Description: "assertion failed: x == 2" @@ -20,9 +20,25 @@ arithmetic_overflow\ - Status: SUCCESS\ - Description: "attempt to compute `unchecked_mul` which would overflow" -Verification succeeded for - should_pass::unchecked_mul -Verification succeeded for - should_pass::has_loop_contract -Verification succeeded for - should_pass::has_recursion_gcd -Verification succeeded for - should_pass::div -Verification failed for - should_fail::max +Manual Harness Summary: +No proof harnesses (functions with #[kani::proof]) were found to verify. + +Autoharness Summary: ++--------------------------------+-----------------------------+---------------------+ +| Selected Function | Kind of Automatic Harness | Verification Result | ++====================================================================================+ +| should_pass::unchecked_mul | #[kani::proof_for_contract] | Success | +|--------------------------------+-----------------------------+---------------------| +| should_pass::has_loop_contract | #[kani::proof] | Success | +|--------------------------------+-----------------------------+---------------------| +| should_pass::has_recursion_gcd | #[kani::proof_for_contract] | Success | +|--------------------------------+-----------------------------+---------------------| +| should_pass::div | #[kani::proof_for_contract] | Success | +|--------------------------------+-----------------------------+---------------------| +| should_fail::max | #[kani::proof_for_contract] | Failure | ++--------------------------------+-----------------------------+---------------------+ +Note that `kani autoharness` sets default --harness-timeout of 30s and --default-unwind of 20. +If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). Complete - 4 successfully verified functions, 1 failures, 5 total. + +Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). diff --git a/tests/script-based-pre/cargo_autoharness_contracts/contracts.sh b/tests/script-based-pre/cargo_autoharness_contracts/contracts.sh index caad53eb8ac4..d4c93fcd828d 100755 --- a/tests/script-based-pre/cargo_autoharness_contracts/contracts.sh +++ b/tests/script-based-pre/cargo_autoharness_contracts/contracts.sh @@ -2,7 +2,7 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -cargo kani autoharness -Z unstable-options -Z function-contracts -Z loop-contracts +cargo kani autoharness -Z autoharness -Z function-contracts -Z loop-contracts # We expect verification to fail, so the above command will produce an exit status of 1 # However, we don't want the test to fail because of that exit status; we only want it to fail if the expected file doesn't match # So, exit with a status code of 0 explicitly. diff --git a/tests/script-based-pre/cargo_autoharness_dependencies/Cargo.toml b/tests/script-based-pre/cargo_autoharness_dependencies/Cargo.toml new file mode 100644 index 000000000000..9adf34fb0ce6 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_dependencies/Cargo.toml @@ -0,0 +1,12 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +[package] +name = "cargo_autoharness_dependencies" +version = "0.1.0" +edition = "2021" + +[dependencies] +other_crate = { path = "other_crate" } + +[package.metadata.kani.unstable] +stubbing = true diff --git a/tests/script-based-pre/cargo_autoharness_dependencies/config.yml b/tests/script-based-pre/cargo_autoharness_dependencies/config.yml new file mode 100644 index 000000000000..4f5debb6f2dd --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_dependencies/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: dependencies.sh +expected: dependencies.expected diff --git a/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected b/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected new file mode 100644 index 000000000000..5a03fa9b8808 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected @@ -0,0 +1,14 @@ +Autoharness: Checking function yes_harness against all possible inputs... + +Manual Harness Summary: +No proof harnesses (functions with #[kani::proof]) were found to verify. + +Autoharness Summary: ++-------------------+---------------------------+---------------------+ +| Selected Function | Kind of Automatic Harness | Verification Result | ++=====================================================================+ +| yes_harness | #[kani::proof] | Success | ++-------------------+---------------------------+---------------------+ +Complete - 1 successfully verified functions, 0 failures, 1 total. + +Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). diff --git a/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.sh b/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.sh new file mode 100755 index 000000000000..34e177be9134 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +cargo kani autoharness -Z autoharness +# We expect verification to fail, so the above command will produce an exit status of 1 +# However, we don't want the test to fail because of that exit status; we only want it to fail if the expected file doesn't match +# So, exit with a status code of 0 explicitly. +exit 0; diff --git a/tests/script-based-pre/cargo_autoharness_dependencies/other_crate/Cargo.toml b/tests/script-based-pre/cargo_autoharness_dependencies/other_crate/Cargo.toml new file mode 100644 index 000000000000..b8b0efc5463d --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_dependencies/other_crate/Cargo.toml @@ -0,0 +1,8 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +[package] +name = "other_crate" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/tests/script-based-pre/cargo_autoharness_dependencies/other_crate/src/lib.rs b/tests/script-based-pre/cargo_autoharness_dependencies/other_crate/src/lib.rs new file mode 100644 index 000000000000..23b04d665d1f --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_dependencies/other_crate/src/lib.rs @@ -0,0 +1,6 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +fn no_harness(x: u8) -> u8 { + x + 1 +} diff --git a/tests/script-based-pre/cargo_autoharness_dependencies/src/lib.rs b/tests/script-based-pre/cargo_autoharness_dependencies/src/lib.rs new file mode 100644 index 000000000000..6f559b9779d6 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_dependencies/src/lib.rs @@ -0,0 +1,10 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This tests that the autoharness feature doesn't generate harnesses for functions outside the local crate. + +use other_crate; + +fn yes_harness(x: u8) -> u8 { + x +} diff --git a/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected b/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected index 91d9eb4c399a..4c0192e92448 100644 --- a/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected +++ b/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected @@ -1,4 +1,23 @@ Autoharness: Checking function include::simple against all possible inputs... VERIFICATION:- SUCCESSFUL -Verification succeeded for - include::simple -Complete - 1 successfully verified functions, 0 failures, 1 total. \ No newline at end of file + +Manual Harness Summary: +No proof harnesses (functions with #[kani::proof]) were found to verify. + +Autoharness Summary: ++-------------------+---------------------------+---------------------+ +| Selected Function | Kind of Automatic Harness | Verification Result | ++=====================================================================+ +| include::simple | #[kani::proof] | Success | ++-------------------+---------------------------+---------------------+ +Complete - 1 successfully verified functions, 0 failures, 1 total. + +Kani did not generate automatic harnesses for the following functions. +If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832 ++------------------+--------------------------------+ +| Skipped Function | Reason for Skipping | ++===================================================+ +| excluded::simple | Did not match provided filters | +|------------------+--------------------------------| +| include::generic | Generic Function | ++------------------+--------------------------------+ diff --git a/tests/script-based-pre/cargo_autoharness_exclude/exclude.sh b/tests/script-based-pre/cargo_autoharness_exclude/exclude.sh index 492325dc5c81..481a8b73f5ee 100755 --- a/tests/script-based-pre/cargo_autoharness_exclude/exclude.sh +++ b/tests/script-based-pre/cargo_autoharness_exclude/exclude.sh @@ -2,4 +2,4 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -cargo kani autoharness -Z unstable-options --exclude-function exclude +cargo kani autoharness -Z autoharness --exclude-function exclude diff --git a/tests/script-based-pre/cargo_autoharness_filter/filter.expected b/tests/script-based-pre/cargo_autoharness_filter/filter.expected index 9db37edf8232..5e60f3a426be 100644 --- a/tests/script-based-pre/cargo_autoharness_filter/filter.expected +++ b/tests/script-based-pre/cargo_autoharness_filter/filter.expected @@ -40,4 +40,116 @@ Autoharness: Checking function yes_harness::f_manually_implements_arbitrary agai Autoharness: Checking function yes_harness::f_phantom_data against all possible inputs... Autoharness: Checking function yes_harness::f_phantom_pinned against all possible inputs... Autoharness: Checking function yes_harness::empty_body against all possible inputs... -Complete - 42 successfully verified functions, 0 failures, 42 total. \ No newline at end of file + +Manual Harness Summary: +No proof harnesses (functions with #[kani::proof]) were found to verify. + +Autoharness Summary: ++----------------------------------------------+---------------------------+---------------------+ +| Selected Function | Kind of Automatic Harness | Verification Result | ++================================================================================================+ +| yes_harness::empty_body | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_phantom_pinned | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_phantom_data | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_manually_implements_arbitrary | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_derives_arbitrary | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_multiple_args | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_unsupported_return_type | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_tuple | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_maybe_uninit | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_result | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_option | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_array | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_nonzero_isize | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_nonzero_i128 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_nonzero_i64 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_nonzero_i32 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_nonzero_i16 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_nonzero_i8 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_nonzero_usize | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_nonzero_u128 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_nonzero_u64 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_nonzero_u32 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_nonzero_u16 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_nonzero_u8 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_f128 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_f16 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_f64 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_f32 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_char | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_bool | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_isize | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_i128 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_i64 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_i32 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_i16 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_i8 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_usize | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_u128 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_u64 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_u32 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_u16 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_u8 | #[kani::proof] | Success | ++----------------------------------------------+---------------------------+---------------------+ +Complete - 42 successfully verified functions, 0 failures, 42 total. + +Kani did not generate automatic harnesses for the following functions. +If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832 ++----------------------------------------+------------------------------------------------------+ +| Skipped Function | Reason for Skipping | ++===============================================================================================+ +| no_harness::doesnt_implement_arbitrary | Missing Arbitrary implementation for argument(s): x | +|----------------------------------------+------------------------------------------------------| +| no_harness::unsupported_const_pointer | Missing Arbitrary implementation for argument(s): _y | +|----------------------------------------+------------------------------------------------------| +| no_harness::unsupported_generic | Generic Function | +|----------------------------------------+------------------------------------------------------| +| no_harness::unsupported_mut_pointer | Missing Arbitrary implementation for argument(s): _y | +|----------------------------------------+------------------------------------------------------| +| no_harness::unsupported_ref | Missing Arbitrary implementation for argument(s): _y | +|----------------------------------------+------------------------------------------------------| +| no_harness::unsupported_slice | Missing Arbitrary implementation for argument(s): _y | +|----------------------------------------+------------------------------------------------------| +| no_harness::unsupported_vec | Missing Arbitrary implementation for argument(s): _y | ++----------------------------------------+------------------------------------------------------+ diff --git a/tests/script-based-pre/cargo_autoharness_filter/filter.sh b/tests/script-based-pre/cargo_autoharness_filter/filter.sh index cec2f2e2a10b..db4a99f8be09 100755 --- a/tests/script-based-pre/cargo_autoharness_filter/filter.sh +++ b/tests/script-based-pre/cargo_autoharness_filter/filter.sh @@ -2,4 +2,4 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -cargo kani autoharness -Z unstable-options +cargo kani autoharness -Z autoharness diff --git a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected index 7ceed3cc019a..3aa5d017a7db 100644 --- a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected +++ b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected @@ -23,13 +23,6 @@ arithmetic_overflow\ - Status: FAILURE\ - Description: "attempt to compute `unchecked_mul` which would overflow" -Verification failed for - panic -Verification failed for - integer_overflow -Verification failed for - oob_unsafe_array_access -Verification failed for - oob_safe_array_access -Verification failed for - unchecked_mul -Complete - 0 successfully verified functions, 5 failures, 5 total. - Checking harness panic_harness... panic.assertion\ - Status: FAILURE\ @@ -54,4 +47,30 @@ arithmetic_overflow\ - Status: FAILURE\ - Description: "attempt to compute `unchecked_mul` which would overflow" +Manual Harness Summary: +Verification failed for - unchecked_mul_harness +Verification failed for - panic_harness +Verification failed for - integer_overflow_harness +Verification failed for - oob_unsafe_array_access_harness +Verification failed for - oob_safe_array_access_harness Complete - 0 successfully verified harnesses, 5 failures, 5 total. + +Autoharness Summary: ++-------------------------+---------------------------+---------------------+ +| Selected Function | Kind of Automatic Harness | Verification Result | ++===========================================================================+ +| unchecked_mul | #[kani::proof] | Failure | +|-------------------------+---------------------------+---------------------| +| panic | #[kani::proof] | Failure | +|-------------------------+---------------------------+---------------------| +| integer_overflow | #[kani::proof] | Failure | +|-------------------------+---------------------------+---------------------| +| oob_unsafe_array_access | #[kani::proof] | Failure | +|-------------------------+---------------------------+---------------------| +| oob_safe_array_access | #[kani::proof] | Failure | ++-------------------------+---------------------------+---------------------+ +Note that `kani autoharness` sets default --harness-timeout of 30s and --default-unwind of 20. +If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). +Complete - 0 successfully verified functions, 5 failures, 5 total. + +Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). diff --git a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.sh b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.sh index b34d3203311f..34e177be9134 100755 --- a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.sh +++ b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.sh @@ -2,7 +2,7 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -cargo kani autoharness -Z unstable-options +cargo kani autoharness -Z autoharness # We expect verification to fail, so the above command will produce an exit status of 1 # However, we don't want the test to fail because of that exit status; we only want it to fail if the expected file doesn't match # So, exit with a status code of 0 explicitly. diff --git a/tests/script-based-pre/cargo_autoharness_include/include.expected b/tests/script-based-pre/cargo_autoharness_include/include.expected index 91d9eb4c399a..4c0192e92448 100644 --- a/tests/script-based-pre/cargo_autoharness_include/include.expected +++ b/tests/script-based-pre/cargo_autoharness_include/include.expected @@ -1,4 +1,23 @@ Autoharness: Checking function include::simple against all possible inputs... VERIFICATION:- SUCCESSFUL -Verification succeeded for - include::simple -Complete - 1 successfully verified functions, 0 failures, 1 total. \ No newline at end of file + +Manual Harness Summary: +No proof harnesses (functions with #[kani::proof]) were found to verify. + +Autoharness Summary: ++-------------------+---------------------------+---------------------+ +| Selected Function | Kind of Automatic Harness | Verification Result | ++=====================================================================+ +| include::simple | #[kani::proof] | Success | ++-------------------+---------------------------+---------------------+ +Complete - 1 successfully verified functions, 0 failures, 1 total. + +Kani did not generate automatic harnesses for the following functions. +If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832 ++------------------+--------------------------------+ +| Skipped Function | Reason for Skipping | ++===================================================+ +| excluded::simple | Did not match provided filters | +|------------------+--------------------------------| +| include::generic | Generic Function | ++------------------+--------------------------------+ diff --git a/tests/script-based-pre/cargo_autoharness_include/include.sh b/tests/script-based-pre/cargo_autoharness_include/include.sh index 1b3fcc285c88..7879013c5717 100755 --- a/tests/script-based-pre/cargo_autoharness_include/include.sh +++ b/tests/script-based-pre/cargo_autoharness_include/include.sh @@ -2,4 +2,4 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -cargo kani autoharness -Z unstable-options --include-function include +cargo kani autoharness -Z autoharness --include-function include diff --git a/tests/script-based-pre/cargo_autoharness_loops_fixme/loops.expected b/tests/script-based-pre/cargo_autoharness_loops_fixme/loops.expected deleted file mode 100644 index 4c43332f6db0..000000000000 --- a/tests/script-based-pre/cargo_autoharness_loops_fixme/loops.expected +++ /dev/null @@ -1 +0,0 @@ -It's not yet clear what the ideal output is for this test, so this expected file is just a placeholder. \ No newline at end of file diff --git a/tests/script-based-pre/cargo_autoharness_loops_fixme/src/lib.rs b/tests/script-based-pre/cargo_autoharness_loops_fixme/src/lib.rs deleted file mode 100644 index 99ba72445b6d..000000000000 --- a/tests/script-based-pre/cargo_autoharness_loops_fixme/src/lib.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT - -// Test that automatic harnesses terminate on functions with loops. - -// Since foo()'s arguments implement Arbitrary, we will attempt to verify it, -// and enter an infinite loop. -// Unclear what the best solution to this problem is; perhaps this is just a known limitation -// and the user needs to specify some command line flag to skip this function, -// or we can conservatively skip functions with loops that don't have loop contracts. -fn infinite_loop() { - loop {} -} - -/// Euclid's algorithm for calculating the GCD of two numbers -#[kani::requires(x != 0 && y != 0)] -#[kani::ensures(|result : &u8| *result != 0 && x % *result == 0 && y % *result == 0)] -fn gcd(mut x: u8, mut y: u8) -> u8 { - (x, y) = (if x > y { x } else { y }, if x > y { y } else { x }); - loop { - let res = x % y; - if res == 0 { - return y; - } - - x = y; - y = res; - } -} - -// Since we can specify an unwinding bound in a manual harness, -// the proof will terminate. -// Automatic harnesses, however, do not support unwinding bounds, -// so the proof does not terminate. -#[kani::proof_for_contract(gcd)] -#[kani::unwind(12)] -fn gcd_harness() { - gcd(kani::any(), kani::any()); -} diff --git a/tests/script-based-pre/cargo_autoharness_loops_fixme/Cargo.toml b/tests/script-based-pre/cargo_autoharness_termination/Cargo.toml similarity index 83% rename from tests/script-based-pre/cargo_autoharness_loops_fixme/Cargo.toml rename to tests/script-based-pre/cargo_autoharness_termination/Cargo.toml index 57d8cd0f0541..0a677a6f88cd 100644 --- a/tests/script-based-pre/cargo_autoharness_loops_fixme/Cargo.toml +++ b/tests/script-based-pre/cargo_autoharness_termination/Cargo.toml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [package] -name = "cargo_autoharness_loops_fixme" +name = "cargo_autoharness_termination" version = "0.1.0" edition = "2024" diff --git a/tests/script-based-pre/cargo_autoharness_loops_fixme/config.yml b/tests/script-based-pre/cargo_autoharness_termination/config.yml similarity index 58% rename from tests/script-based-pre/cargo_autoharness_loops_fixme/config.yml rename to tests/script-based-pre/cargo_autoharness_termination/config.yml index fefb50875c7a..14e4df6b5fc2 100644 --- a/tests/script-based-pre/cargo_autoharness_loops_fixme/config.yml +++ b/tests/script-based-pre/cargo_autoharness_termination/config.yml @@ -1,4 +1,4 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -script: loops.sh -expected: loops.expected +script: termination.sh +expected: termination.expected diff --git a/tests/script-based-pre/cargo_autoharness_termination/src/lib.rs b/tests/script-based-pre/cargo_autoharness_termination/src/lib.rs new file mode 100644 index 000000000000..ab30e8e79eb7 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_termination/src/lib.rs @@ -0,0 +1,38 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Test that the default bounds for automatic harnesses takes effect +// to terminate harnesses that would otherwise run forever. + +// Test that the --default-unwind makes harnesses terminate. +// (Technically, the harness timeout could take effect before the unwind bound, +// but in practice the unwind bound is low enough that it always takes effect first.) +mod unwind_bound { + fn infinite_loop() { + loop {} + } + + fn gcd_recursion(x: u64, y: u64) -> u64 { + let mut max = x; + let mut min = y; + if min > max { + let val = max; + max = min; + min = val; + } + let res = max % min; + if res == 0 { min } else { gcd_recursion(min, res + 1) } + } +} + +// Test that when there is no loop/recursion unwinding, the default harness timeout terminates the harness eventually. +mod timeout { + fn check_harness_timeout() { + // construct a problem that requires a long time to solve + let (a1, b1, c1): (u64, u64, u64) = kani::any(); + let (a2, b2, c2): (u64, u64, u64) = kani::any(); + let p1 = a1.saturating_mul(b1).saturating_mul(c1); + let p2 = a2.saturating_mul(b2).saturating_mul(c2); + assert!(a1 != a2 || b1 != b2 || c1 != c2 || p1 == p2) + } +} diff --git a/tests/script-based-pre/cargo_autoharness_termination/termination.expected b/tests/script-based-pre/cargo_autoharness_termination/termination.expected new file mode 100644 index 000000000000..e95ed9825be4 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_termination/termination.expected @@ -0,0 +1,42 @@ +Autoharness: Checking function timeout::check_harness_timeout against all possible inputs... +CBMC failed +VERIFICATION:- FAILED +CBMC timed out. You may want to rerun your proof with a larger timeout or use stubbing to reduce the size of the code the verifier reasons about. + +Autoharness: Checking function unwind_bound::gcd_recursion against all possible inputs... +Not unwinding recursion unwind_bound::gcd_recursion iteration 21 + +unwind_bound::gcd_recursion.assertion\ + - Status: FAILURE\ + - Description: "attempt to calculate the remainder with a divisor of zero" + +unwind_bound::gcd_recursion.recursion\ + - Status: FAILURE\ + - Description: "recursion unwinding assertion" + +VERIFICATION:- FAILED +[Kani] info: Verification output shows one or more unwinding failures. + +Autoharness: Checking function unwind_bound::infinite_loop against all possible inputs... +unwind_bound::infinite_loop.unwind\ + - Status: FAILURE\ + - Description: "unwinding assertion loop 0" + +VERIFICATION:- FAILED +[Kani] info: Verification output shows one or more unwinding failures. + +Autoharness Summary: ++--------------------------------+---------------------------+---------------------+ +| Selected Function | Kind of Automatic Harness | Verification Result | ++==================================================================================+ +| timeout::check_harness_timeout | #[kani::proof] | Failure | +|--------------------------------+---------------------------+---------------------| +| unwind_bound::gcd_recursion | #[kani::proof] | Failure | +|--------------------------------+---------------------------+---------------------| +| unwind_bound::infinite_loop | #[kani::proof] | Failure | ++--------------------------------+---------------------------+---------------------+ +Note that `kani autoharness` sets default --harness-timeout of 30s and --default-unwind of 20. +If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). +Complete - 0 successfully verified functions, 3 failures, 3 total. + +Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). diff --git a/tests/script-based-pre/cargo_autoharness_loops_fixme/loops.sh b/tests/script-based-pre/cargo_autoharness_termination/termination.sh similarity index 61% rename from tests/script-based-pre/cargo_autoharness_loops_fixme/loops.sh rename to tests/script-based-pre/cargo_autoharness_termination/termination.sh index cec2f2e2a10b..65f949b65ad3 100755 --- a/tests/script-based-pre/cargo_autoharness_loops_fixme/loops.sh +++ b/tests/script-based-pre/cargo_autoharness_termination/termination.sh @@ -2,4 +2,4 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -cargo kani autoharness -Z unstable-options +cargo kani autoharness -Z autoharness -Z function-contracts From 67cd1e6b074bfe91b8a4fde4c146a5ce6479276d Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Thu, 6 Mar 2025 11:45:00 -0600 Subject: [PATCH 041/161] Bump Kani version to 0.60.0 (#3923) ## What's Changed * Automatic cargo update to 2025-02-10 by @github-actions in https://github.com/model-checking/kani/pull/3880 * Bump tests/perf/s2n-quic from `82dd0b5` to `a5d8422` by @dependabot in https://github.com/model-checking/kani/pull/3882 * Fast fail feature - Stops verification process as soon as one failure is observed - Use case : CI speed up by @rajath-mk in https://github.com/model-checking/kani/pull/3879 * Autoharness Subcommand by @carolynzech in https://github.com/model-checking/kani/pull/3874 * Upgrade toolchain to 2/10 by @carolynzech in https://github.com/model-checking/kani/pull/3883 * Add loop-contracts doc to SUMMARY by @qinheping in https://github.com/model-checking/kani/pull/3886 * Support concrete playback for arrays of length 65 or greater by @carolynzech in https://github.com/model-checking/kani/pull/3888 * Automatic cargo update to 2025-02-17 by @github-actions in https://github.com/model-checking/kani/pull/3889 * Bump tests/perf/s2n-quic from `a5d8422` to `00e3371` by @dependabot in https://github.com/model-checking/kani/pull/3894 * Adjust PropertyClass of assertions to identify UB by @tautschnig in https://github.com/model-checking/kani/pull/3860 * Fix: regression test from #3888 has version control change by @carolynzech in https://github.com/model-checking/kani/pull/3892 * Upgrade toolchain to 2025-02-11 by @thanhnguyen-aws in https://github.com/model-checking/kani/pull/3887 * Remove isize overflow check for zst offsets by @carolynzech in https://github.com/model-checking/kani/pull/3897 * Automatic toolchain upgrade to nightly-2025-02-12 by @github-actions in https://github.com/model-checking/kani/pull/3898 * Upgrade the toolchain to 2025-02-21 by @zhassan-aws in https://github.com/model-checking/kani/pull/3899 * Automatic cargo update to 2025-02-24 by @github-actions in https://github.com/model-checking/kani/pull/3901 * Bump ncipollo/release-action from 1.15.0 to 1.16.0 by @dependabot in https://github.com/model-checking/kani/pull/3902 * Bump tests/perf/s2n-quic from `00e3371` to `cfb314b` by @dependabot in https://github.com/model-checking/kani/pull/3903 * Convert raw URL to link by @flba-eb in https://github.com/model-checking/kani/pull/3907 * Automatic cargo update to 2025-03-03 by @github-actions in https://github.com/model-checking/kani/pull/3913 * Install toolchain with rustup >= 1.28.0 by @tautschnig in https://github.com/model-checking/kani/pull/3917 * Bump tests/perf/s2n-quic from `cfb314b` to `d88faa4` by @dependabot in https://github.com/model-checking/kani/pull/3916 * Remove Ubuntu 20.04 CI usage by @tautschnig in https://github.com/model-checking/kani/pull/3918 * Move standard-library metrics script to verify-rust-std repo by @tautschnig in https://github.com/model-checking/kani/pull/3914 * scanner: Fix loop stats in overall function stats summary by @tautschnig in https://github.com/model-checking/kani/pull/3915 * Update toolchain to 2025-03-02 by @remi-delmas-3000 in https://github.com/model-checking/kani/pull/3911 ## New Contributors * @flba-eb made their first contribution in https://github.com/model-checking/kani/pull/3907 **Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.59.0...kani-0.60.0 --------- Co-authored-by: Carolyn Zech --- CHANGELOG.md | 17 ++++++ Cargo.lock | 104 ++++++++++++++++----------------- Cargo.toml | 2 +- cprover_bindings/Cargo.toml | 2 +- kani-compiler/Cargo.toml | 2 +- kani-driver/Cargo.toml | 2 +- kani_metadata/Cargo.toml | 2 +- library/kani/Cargo.toml | 2 +- library/kani_core/Cargo.toml | 2 +- library/kani_macros/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- tools/build-kani/Cargo.toml | 2 +- 12 files changed, 79 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ae95fe7f52c..8c3f736e6a72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,23 @@ This file contains notable changes (e.g. breaking changes, major changes, etc.) This file was introduced starting Kani 0.23.0, so it only contains changes from version 0.23.0 onwards. +## [0.60.0] + +### Breaking Changes +* Autoharness Subcommand by @carolynzech in https://github.com/model-checking/kani/pull/3874 +* Remove Ubuntu 20.04 CI usage by @tautschnig in https://github.com/model-checking/kani/pull/3918 + +### What's Changed +* Fast fail option - Stop verification process as soon as one failure is observed by @rajath-mk in https://github.com/model-checking/kani/pull/3879 +* Fail verification for UB regardless of whether `#[should_panic]` is enabled by @tautschnig in https://github.com/model-checking/kani/pull/3860 +* Support concrete playback for arrays of length 65 or greater by @carolynzech in https://github.com/model-checking/kani/pull/3888 +* Remove isize overflow check for zst offsets by @carolynzech in https://github.com/model-checking/kani/pull/3897 +* Support concrete playback for arrays of length 65 or greater by @carolynzech in https://github.com/model-checking/kani/pull/3888 +* Autoharness Misc. Improvements by @carolynzech in https://github.com/model-checking/kani/pull/3922 +* Update toolchain to 2025-03-02 by @remi-delmas-3000 @carolynzech @thanhnguyen-aws @zhassan-aws and @tautschnig + +**Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.59.0...kani-0.60.0 + ## [0.59.0] ### Breaking Changes diff --git a/Cargo.lock b/Cargo.lock index 69990084ed31..258ee00c0568 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,9 +101,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "arrayvec" @@ -176,7 +176,7 @@ dependencies = [ [[package]] name = "build-kani" -version = "0.59.0" +version = "0.60.0" dependencies = [ "anyhow", "cargo_metadata", @@ -192,9 +192,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "camino" @@ -225,7 +225,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] @@ -397,7 +397,7 @@ dependencies = [ [[package]] name = "cprover_bindings" -version = "0.59.0" +version = "0.60.0" dependencies = [ "lazy_static", "linear-map", @@ -559,9 +559,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "encode_unicode" @@ -775,9 +775,9 @@ dependencies = [ [[package]] name = "indoc" -version = "2.0.5" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" [[package]] name = "is_terminal_polyfill" @@ -796,9 +796,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "joinery" @@ -808,7 +808,7 @@ checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5" [[package]] name = "kani" -version = "0.59.0" +version = "0.60.0" dependencies = [ "kani_core", "kani_macros", @@ -816,7 +816,7 @@ dependencies = [ [[package]] name = "kani-compiler" -version = "0.59.0" +version = "0.60.0" dependencies = [ "charon", "clap", @@ -855,7 +855,7 @@ dependencies = [ [[package]] name = "kani-driver" -version = "0.59.0" +version = "0.60.0" dependencies = [ "anyhow", "cargo_metadata", @@ -887,7 +887,7 @@ dependencies = [ [[package]] name = "kani-verifier" -version = "0.59.0" +version = "0.60.0" dependencies = [ "anyhow", "home", @@ -896,14 +896,14 @@ dependencies = [ [[package]] name = "kani_core" -version = "0.59.0" +version = "0.60.0" dependencies = [ "kani_macros", ] [[package]] name = "kani_macros" -version = "0.59.0" +version = "0.60.0" dependencies = [ "proc-macro-error2", "proc-macro2", @@ -913,7 +913,7 @@ dependencies = [ [[package]] name = "kani_metadata" -version = "0.59.0" +version = "0.60.0" dependencies = [ "clap", "cprover_bindings", @@ -1294,9 +1294,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] @@ -1312,9 +1312,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" dependencies = [ "proc-macro2", ] @@ -1371,9 +1371,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" dependencies = [ "bitflags", ] @@ -1452,15 +1452,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -1491,9 +1491,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" dependencies = [ "serde", ] @@ -1529,9 +1529,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.139" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "indexmap", "itoa", @@ -1551,9 +1551,9 @@ dependencies = [ [[package]] name = "serde_stacker" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "babfccff5773ff80657f0ecf553c7c516bdc2eb16389c0918b36b73e7015276e" +checksum = "69c8defe6c780725cce4ec6ad3bd91e321baf6fa4e255df1f31e345d507ef01a" dependencies = [ "serde", "stacker", @@ -1632,7 +1632,7 @@ dependencies = [ [[package]] name = "std" -version = "0.59.0" +version = "0.60.0" dependencies = [ "kani", ] @@ -1680,9 +1680,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.98" +version = "2.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" dependencies = [ "proc-macro2", "quote", @@ -1726,11 +1726,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.11", + "thiserror-impl 2.0.12", ] [[package]] @@ -1746,9 +1746,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", @@ -1767,9 +1767,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.37" +version = "0.3.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "bb041120f25f8fbe8fd2dbe4671c7c2ed74d83be2e7a77529bf7e0790ae3f472" dependencies = [ "deranged", "itoa", @@ -1784,15 +1784,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" dependencies = [ "num-conv", "time-core", @@ -1986,9 +1986,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-segmentation" diff --git a/Cargo.toml b/Cargo.toml index 4948246d5110..beabf68f6e53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-verifier" -version = "0.59.0" +version = "0.60.0" edition = "2021" description = "A bit-precise model checker for Rust." readme = "README.md" diff --git a/cprover_bindings/Cargo.toml b/cprover_bindings/Cargo.toml index b35626e3d5f4..8c8cf77fd411 100644 --- a/cprover_bindings/Cargo.toml +++ b/cprover_bindings/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "cprover_bindings" -version = "0.59.0" +version = "0.60.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index e2636f5fcd64..f11932a7387e 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-compiler" -version = "0.59.0" +version = "0.60.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index ec39fbf67dec..e6efa9460afb 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-driver" -version = "0.59.0" +version = "0.60.0" edition = "2021" description = "Build a project with Kani and run all proof harnesses" license = "MIT OR Apache-2.0" diff --git a/kani_metadata/Cargo.toml b/kani_metadata/Cargo.toml index b4db86ebed1e..038b9e235663 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_metadata" -version = "0.59.0" +version = "0.60.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index d559be65a88f..4b32b92485e8 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.59.0" +version = "0.60.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani_core/Cargo.toml b/library/kani_core/Cargo.toml index b474e15d7d0c..17338d03f12f 100644 --- a/library/kani_core/Cargo.toml +++ b/library/kani_core/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_core" -version = "0.59.0" +version = "0.60.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani_macros/Cargo.toml b/library/kani_macros/Cargo.toml index 85e3ee2ed48c..8a68cd8cc124 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_macros" -version = "0.59.0" +version = "0.60.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 223310d52726..b4ca6fee7822 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -5,7 +5,7 @@ # Note: this package is intentionally named std to make sure the names of # standard library symbols are preserved name = "std" -version = "0.59.0" +version = "0.60.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/tools/build-kani/Cargo.toml b/tools/build-kani/Cargo.toml index 8d837a9b0e3f..23632adc48ec 100644 --- a/tools/build-kani/Cargo.toml +++ b/tools/build-kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "build-kani" -version = "0.59.0" +version = "0.60.0" edition = "2021" description = "Builds Kani, Sysroot and release bundle." license = "MIT OR Apache-2.0" From cb01c7b885a47052bff14a8f5af2dde3e184c18d Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Thu, 6 Mar 2025 14:37:02 -0600 Subject: [PATCH 042/161] Fix CHANGELOG of 0.60.0 (#3925) The change `* Autoharness Subcommand by @carolynzech in https://github.com/model-checking/kani/pull/3874` should be of Major Changes instead of Breaking Changes. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c3f736e6a72..d205f8af8e31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,11 @@ This file was introduced starting Kani 0.23.0, so it only contains changes from ## [0.60.0] ### Breaking Changes -* Autoharness Subcommand by @carolynzech in https://github.com/model-checking/kani/pull/3874 * Remove Ubuntu 20.04 CI usage by @tautschnig in https://github.com/model-checking/kani/pull/3918 +### Major Changes +* Autoharness Subcommand by @carolynzech in https://github.com/model-checking/kani/pull/3874 + ### What's Changed * Fast fail option - Stop verification process as soon as one failure is observed by @rajath-mk in https://github.com/model-checking/kani/pull/3879 * Fail verification for UB regardless of whether `#[should_panic]` is enabled by @tautschnig in https://github.com/model-checking/kani/pull/3860 From fcc5c4b5709d756282fdde3f68508bfa83b1b8ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 17:36:19 +0000 Subject: [PATCH 043/161] Bump tests/perf/s2n-quic from `d88faa4` to `8670e83` (#3928) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `d88faa4` to `8670e83`.
Commits
  • 8670e83 feat(s2n-quic-dc): Support storing ApplicationData in Entry (#2515)
  • c82daf7 fix(s2n-quic-transport): allow migrations even when disable_active_migration ...
  • a9e7673 feat(s2n-quic-dc): implement recv path packet pool (#2483)
  • 7d935aa refactor(s2n-quic-dc): rename stream_id to queue_id (#2507)
  • 1a1dfa8 feat(s2n-quic-dc): Add subscriber event for dc::Path creation (#2510)
  • 1605dc7 feat(s2n-quic): implement updatable connection limits (#2508)
  • fbcc8fd fix: typo for debugging book (#2509)
  • 70e8de8 feat: add stream recv buffer trait and impls (#2505)
  • 4d60027 feat(s2n-quic-dc): add mpsc channel (#2503)
  • See full diff in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index d88faa482544..8670e83da211 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit d88faa482544f916c90b3d65f754591edd099667 +Subproject commit 8670e83da211151a35ad72bc22b6e5eeb3331e13 From 9ee764fa95f46a8542c59fef9ec4644b557f4172 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 11 Mar 2025 05:22:40 -0500 Subject: [PATCH 044/161] Update toolchain to 2025-03-04 (#3927) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves: #3924 Related commits: https://github.com/rust-lang/rust/commit/aac65f562b rename BackendRepr::Vector → SimdVector By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../src/irep/goto_binary_serde.rs | 68 +++++++------------ .../codegen_cprover_gotoc/codegen/place.rs | 30 ++++---- .../src/codegen_cprover_gotoc/codegen/typ.rs | 4 +- .../compiler_interface.rs | 2 +- .../check_uninit/ptr_uninit/uninit_visitor.rs | 2 +- rust-toolchain.toml | 2 +- 6 files changed, 43 insertions(+), 65 deletions(-) diff --git a/cprover_bindings/src/irep/goto_binary_serde.rs b/cprover_bindings/src/irep/goto_binary_serde.rs index 15cc0afa8902..56578cc87aa4 100644 --- a/cprover_bindings/src/irep/goto_binary_serde.rs +++ b/cprover_bindings/src/irep/goto_binary_serde.rs @@ -84,7 +84,7 @@ use std::collections::HashMap; use std::fs::File; use std::hash::Hash; use std::io::{self, BufReader}; -use std::io::{BufWriter, Bytes, Error, ErrorKind, Read, Write}; +use std::io::{BufWriter, Bytes, Error, Read, Write}; use std::path::Path; /// Writes a symbol table to a file in goto binary format in version 6. @@ -557,7 +557,7 @@ where R: Read, { /// Stream of bytes from which GOTO objects are read. - bytes: Bytes, + bytes: Bytes>, /// Numbering for ireps numbering: IrepNumbering, @@ -584,8 +584,9 @@ where /// Constructor. The reader is moved into this object and cannot be used /// afterwards. fn new(reader: R) -> Self { + let buffered_reader = BufReader::new(reader); GotoBinaryDeserializer { - bytes: reader.bytes(), + bytes: buffered_reader.bytes(), numbering: IrepNumbering::new(), string_count: Vec::new(), string_map: Vec::new(), @@ -597,12 +598,9 @@ where /// Returns Err if the found value is not the expected value. fn expect(found: T, expected: T) -> io::Result { if found != expected { - return Err(Error::new( - ErrorKind::Other, - format!( - "Invalid goto binary input: expected {expected} in byte stream, found {found} instead)" - ), - )); + return Err(Error::other(format!( + "Invalid goto binary input: expected {expected} in byte stream, found {found} instead)" + ))); } Ok(found) } @@ -660,10 +658,7 @@ where match self.bytes.next() { Some(Ok(u)) => Ok(u), Some(Err(error)) => Err(error), - None => Err(Error::new( - ErrorKind::Other, - "Invalid goto binary input: unexpected end of input", - )), + None => Err(Error::other("Invalid goto binary input: unexpected end of input")), } } @@ -677,8 +672,7 @@ where match self.bytes.next() { Some(Ok(u)) => { if shift >= max_shift { - return Err(Error::new( - ErrorKind::Other, + return Err(Error::other( "Invalid goto binary input: serialized value is too large to fit in usize", )); }; @@ -692,10 +686,7 @@ where return Err(error); } None => { - return Err(Error::new( - ErrorKind::Other, - "Invalid goto binary input: unexpected end of input", - )); + return Err(Error::other("Invalid goto binary input: unexpected end of input")); } } } @@ -721,10 +712,7 @@ where return Ok(numbered); } Err(error) => { - return Err(Error::new( - ErrorKind::Other, - error.to_string(), - )); + return Err(Error::other(error.to_string())); } } } @@ -738,8 +726,7 @@ where return Err(error); } None => { - return Err(Error::new( - ErrorKind::Other, + return Err(Error::other( "Invalid goto binary input: unexpected end of input", )); } @@ -757,8 +744,7 @@ where } None => { // No more bytes left - return Err(Error::new( - ErrorKind::Other, + return Err(Error::other( "Invalid goto binary input: unexpected end of input", )); } @@ -789,8 +775,7 @@ where match c { b'S' => { if sub_done { - return Err(Error::new( - ErrorKind::Other, + return Err(Error::other( "Invalid goto binary input: incorrect binary structure", )); } @@ -816,13 +801,10 @@ where return Ok(numbered); } other => { - return Err(Error::new( - ErrorKind::Other, - format!( - "Invalid goto binary input: unexpected character in input stream {}", - other as char - ), - )); + return Err(Error::other(format!( + "Invalid goto binary input: unexpected character in input stream {}", + other as char + ))); } } } @@ -877,8 +859,7 @@ where let shifted_flags = flags >> 16; if shifted_flags != 0 { - return Err(Error::new( - ErrorKind::Other, + return Err(Error::other( "incorrect binary format: true bits remain in decoded symbol flags", )); } @@ -916,13 +897,10 @@ where // Read goto binary version let goto_binary_version = self.read_usize_varenc()?; if goto_binary_version != 6 { - return Err(Error::new( - ErrorKind::Other, - format!( - "Unsupported GOTO binary version: {}. Supported version: {}", - goto_binary_version, 6 - ), - )); + return Err(Error::other(format!( + "Unsupported GOTO binary version: {}. Supported version: {}", + goto_binary_version, 6 + ))); } Ok(()) } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs index 01796a832b78..721082c20bd6 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs @@ -150,7 +150,7 @@ impl ProjectedPlace { goto_expr: Expr, ty: Ty, ctx: &mut GotocCtx, - ) -> Result { + ) -> Result> { Self::try_new(goto_expr, TypeOrVariant::Type(ty), None, None, ctx) } @@ -160,7 +160,7 @@ impl ProjectedPlace { fat_ptr_goto_expr: Option, fat_ptr_mir_typ: Option, ctx: &mut GotocCtx, - ) -> Result { + ) -> Result> { if let Some(fat_ptr) = &fat_ptr_goto_expr { assert!( fat_ptr.typ().is_rust_fat_ptr(&ctx.symbol_table), @@ -183,12 +183,12 @@ impl ProjectedPlace { if !(expr_ty.is_integer() && ty_from_mir.is_struct_tag()) { debug_assert!(false, "{}", msg); } - return Err(UnimplementedData::new( + return Err(Box::new(UnimplementedData::new( "Projection mismatch", "https://github.com/model-checking/kani/issues/277", ty_from_mir, *goto_expr.location(), - )); + ))); } assert!( @@ -236,7 +236,7 @@ impl GotocCtx<'_> { parent_ty_or_var: TypeOrVariant, field_idx: FieldIdx, field_ty_or_var: TypeOrVariant, - ) -> Result { + ) -> Result> { match parent_ty_or_var { TypeOrVariant::Type(parent_ty) => { match parent_ty.kind() { @@ -286,12 +286,12 @@ impl GotocCtx<'_> { TyKind::RigidTy(RigidTy::CoroutineClosure(def, args)) => { let typ = Ty::new_coroutine_closure(def, args); let goto_typ = self.codegen_ty_stable(typ); - Err(UnimplementedData::new( + Err(Box::new(UnimplementedData::new( "Coroutine closures", "https://github.com/model-checking/kani/issues/3783", goto_typ, *parent_expr.location(), - )) + ))) } TyKind::RigidTy(RigidTy::Str) | TyKind::RigidTy(RigidTy::Array(_, _)) @@ -414,10 +414,10 @@ impl GotocCtx<'_> { /// the return value is the expression after. fn codegen_projection( &mut self, - before: Result, + before: Result>, proj: &ProjectionElem, loc: Location, - ) -> Result { + ) -> Result> { let before = before?; trace!(?before, ?proj, "codegen_projection"); match proj { @@ -550,12 +550,12 @@ impl GotocCtx<'_> { let typ = Ty::try_new_array(ty, subarray_len).unwrap(); let goto_typ = self.codegen_ty_stable(typ); // unimplemented - Err(UnimplementedData::new( + Err(Box::new(UnimplementedData::new( "Sub-array binding", "https://github.com/model-checking/kani/issues/707", goto_typ, *before.goto_expr.location(), - )) + ))) } TyKind::RigidTy(RigidTy::Slice(_)) => { let len = if *from_end { @@ -722,7 +722,7 @@ impl GotocCtx<'_> { &mut self, place: &Place, loc: Location, - ) -> Result { + ) -> Result> { debug!(?place, "codegen_place"); let initial_expr = self.codegen_local(place.local, loc); let initial_typ = TypeOrVariant::Type(self.local_ty_stable(place.local)); @@ -734,12 +734,12 @@ impl GotocCtx<'_> { .iter() .fold(initial_projection, |accum, proj| self.codegen_projection(accum, proj, loc)); match result { - Err(data) => Err(UnimplementedData::new( + Err(data) => Err(Box::new(UnimplementedData::new( &data.operation, &data.bug_url, self.codegen_ty_stable(self.place_ty_stable(place)), data.loc, - )), + ))), _ => result, } } @@ -770,7 +770,7 @@ impl GotocCtx<'_> { offset: u64, min_length: u64, from_end: bool, - ) -> Result { + ) -> Result> { match before.mir_typ().kind() { //TODO, ask on zulip if we can ever have from_end here? TyKind::RigidTy(RigidTy::Array(elemt, length)) => { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index efbcbe2e06e5..48ce2ac7043d 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -5,7 +5,7 @@ use cbmc::goto_program::{DatatypeComponent, Expr, Location, Parameter, Symbol, S use cbmc::utils::aggr_tag; use cbmc::{InternString, InternedString}; use rustc_abi::{ - BackendRepr::Vector, FieldIdx, FieldsShape, Float, Integer, LayoutData, Primitive, Size, + BackendRepr::SimdVector, FieldIdx, FieldsShape, Float, Integer, LayoutData, Primitive, Size, TagEncoding, TyAndLayout, VariantIdx, Variants, }; use rustc_ast::ast::Mutability; @@ -1473,7 +1473,7 @@ impl<'tcx> GotocCtx<'tcx> { debug! {"handling simd with layout {:?}", layout}; let (element, size) = match layout { - Vector { element, count } => (element, count), + SimdVector { element, count } => (element, count), _ => unreachable!(), }; diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index 57b149c2bc98..70c146489030 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -420,7 +420,7 @@ impl CodegenBackend for GotocCodegenBackend { let requested_crate_types = &codegen_results.crate_info.crate_types.clone(); let local_crate_name = codegen_results.crate_info.local_crate_name; // Create the rlib if one was requested. - if requested_crate_types.iter().any(|crate_type| *crate_type == CrateType::Rlib) { + if requested_crate_types.contains(&CrateType::Rlib) { link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, outputs); } diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs b/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs index 02d928eff75e..9ae4ffd79ad6 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs @@ -468,7 +468,7 @@ impl MirVisitor for CheckUninitVisitor { && !ptx.is_mutating() { let contains_deref_projection = - { place.projection.iter().any(|elem| *elem == ProjectionElem::Deref) }; + { place.projection.contains(&ProjectionElem::Deref) }; if contains_deref_projection { // We do not currently support having a deref projection in the same // place as union field access. diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 4167255ebd78..0a02e10299f5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-03-02" +channel = "nightly-2025-03-04" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 931c40ea77c7363ac938a3bcb63b867d9fdb04c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Delmas?= Date: Tue, 11 Mar 2025 07:39:37 -0400 Subject: [PATCH 045/161] Install the right toolchain for HEAD and BASE checks in `verify-std-check.yml` (#3920) The versions of the toolchain and deps can differ between BASE and HEAD versions and we build twice, so we ensure the right versions are installed. Co-authored-by: Remi Delmas --- .github/workflows/verify-std-check.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/verify-std-check.yml b/.github/workflows/verify-std-check.yml index 3efdacc2cb85..33e424065f56 100644 --- a/.github/workflows/verify-std-check.yml +++ b/.github/workflows/verify-std-check.yml @@ -62,12 +62,24 @@ jobs: -Z mem-predicates -Z loop-contracts --enable-unstable --cbmc-args --object-bits 12 # If the head failed, check if it's a new failure. - - name: Checkout base + - name: Checkout BASE working-directory: kani if: steps.check-head.outcome != 'success' && github.event_name == 'pull_request' run: | BASE_REF=${{ github.event.pull_request.base.sha }} git checkout ${BASE_REF} + + - name: Setup Kani Dependencies BASE + uses: ./kani/.github/actions/setup + if: steps.check-head.outcome != 'success' && github.event_name == 'pull_request' + with: + os: ${{ matrix.os }} + kani_dir: kani + + - name: Build Kani BASE + working-directory: kani + if: steps.check-head.outcome != 'success' && github.event_name == 'pull_request' + run: | cargo build-dev - name: Run verification with BASE From 7f8d8e4bb880edaf9bef28699d3e5b6109a090d3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 13:22:29 +0000 Subject: [PATCH 046/161] Automatic cargo update to 2025-03-10 (#3926) Dependency upgrade resulting from `cargo update`. --------- Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> Co-authored-by: Michael Tautschnig --- Cargo.lock | 88 +++++++++++++------ kani-driver/src/autoharness/mod.rs | 2 +- .../contracts.expected | 2 +- .../harnesses_fail.expected | 2 +- .../termination.expected | 2 +- 5 files changed, 64 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 258ee00c0568..f93dccce4716 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,7 +27,7 @@ dependencies = [ "getrandom 0.2.15", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -184,12 +184,6 @@ dependencies = [ "which", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" version = "1.10.1" @@ -444,7 +438,7 @@ dependencies = [ "bitflags", "crossterm_winapi", "parking_lot", - "rustix", + "rustix 0.38.44", "winapi", ] @@ -950,6 +944,12 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "linux-raw-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9" + [[package]] name = "lock_api" version = "0.4.12" @@ -1236,11 +1236,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy", + "zerocopy 0.8.23", ] [[package]] @@ -1446,7 +1446,20 @@ dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dade4812df5c384711475be5fcd8c162555352945401aed22a35bffeab61f657" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys 0.9.2", "windows-sys 0.59.0", ] @@ -1500,9 +1513,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -1518,9 +1531,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -1680,9 +1693,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.99" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -1697,15 +1710,15 @@ checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "tempfile" -version = "3.17.1" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" +checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567" dependencies = [ "cfg-if", "fastrand", "getrandom 0.3.1", "once_cell", - "rustix", + "rustix 1.0.1", "windows-sys 0.59.0", ] @@ -1767,9 +1780,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.38" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb041120f25f8fbe8fd2dbe4671c7c2ed74d83be2e7a77529bf7e0790ae3f472" +checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" dependencies = [ "deranged", "itoa", @@ -1809,9 +1822,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.43.0" +version = "1.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +checksum = "9975ea0f48b5aa3972bf2d888c238182458437cc2a19374b81b25cdf1023fb3a" dependencies = [ "backtrace", "bytes", @@ -2074,7 +2087,7 @@ checksum = "2774c861e1f072b3aadc02f8ba886c26ad6321567ecc294c935434cad06f1283" dependencies = [ "either", "env_home", - "rustix", + "rustix 0.38.44", "winsafe", ] @@ -2221,8 +2234,16 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +dependencies = [ + "zerocopy-derive 0.8.23", ] [[package]] @@ -2235,3 +2256,14 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zerocopy-derive" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/kani-driver/src/autoharness/mod.rs b/kani-driver/src/autoharness/mod.rs index 4faef3a69071..69dd8b561103 100644 --- a/kani-driver/src/autoharness/mod.rs +++ b/kani-driver/src/autoharness/mod.rs @@ -14,7 +14,7 @@ use anyhow::Result; use comfy_table::Table as PrettyTable; use kani_metadata::{AutoHarnessSkipReason, KaniMetadata}; -const AUTOHARNESS_TIMEOUT: &str = "30s"; +const AUTOHARNESS_TIMEOUT: &str = "60s"; const LOOP_UNWIND_DEFAULT: u32 = 20; pub fn autoharness_cargo(args: CargoAutoharnessArgs) -> Result<()> { diff --git a/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected b/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected index fd3fd419a73f..9babfe015117 100644 --- a/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected +++ b/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected @@ -37,7 +37,7 @@ Autoharness Summary: |--------------------------------+-----------------------------+---------------------| | should_fail::max | #[kani::proof_for_contract] | Failure | +--------------------------------+-----------------------------+---------------------+ -Note that `kani autoharness` sets default --harness-timeout of 30s and --default-unwind of 20. +Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). Complete - 4 successfully verified functions, 1 failures, 5 total. diff --git a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected index 3aa5d017a7db..8d1e15f4d92e 100644 --- a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected +++ b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected @@ -69,7 +69,7 @@ Autoharness Summary: |-------------------------+---------------------------+---------------------| | oob_safe_array_access | #[kani::proof] | Failure | +-------------------------+---------------------------+---------------------+ -Note that `kani autoharness` sets default --harness-timeout of 30s and --default-unwind of 20. +Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). Complete - 0 successfully verified functions, 5 failures, 5 total. diff --git a/tests/script-based-pre/cargo_autoharness_termination/termination.expected b/tests/script-based-pre/cargo_autoharness_termination/termination.expected index e95ed9825be4..0b36a4dbe67b 100644 --- a/tests/script-based-pre/cargo_autoharness_termination/termination.expected +++ b/tests/script-based-pre/cargo_autoharness_termination/termination.expected @@ -35,7 +35,7 @@ Autoharness Summary: |--------------------------------+---------------------------+---------------------| | unwind_bound::infinite_loop | #[kani::proof] | Failure | +--------------------------------+---------------------------+---------------------+ -Note that `kani autoharness` sets default --harness-timeout of 30s and --default-unwind of 20. +Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). Complete - 0 successfully verified functions, 3 failures, 3 total. From b032310e7697e6a4c92929f31d34ad0387e7ad14 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 14:15:06 +0000 Subject: [PATCH 047/161] Automatic toolchain upgrade to nightly-2025-03-05 (#3929) Update Rust toolchain from nightly-2025-03-04 to nightly-2025-03-05 without any other source changes. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 0a02e10299f5..a17e433c3901 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-03-04" +channel = "nightly-2025-03-05" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From ad996ac1464df26229331cb08ad1daf464575af9 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 11 Mar 2025 22:03:39 +0100 Subject: [PATCH 048/161] Upgrade toolchain to nightly-2025-03-07 (#3931) Changes required due to - rust-lang/rust#138026: Make CrateItem::body() function return an option - rust-lang/rust#137728: Remove unsizing coercions for tuples Resolves: #3930 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- kani-compiler/src/kani_middle/stubbing/mod.rs | 2 +- rust-toolchain.toml | 2 +- tests/kani/SizeAndAlignOfDst/unsized_tuple.rs | 18 ------------------ tools/scanner/src/analysis.rs | 10 +++++----- 4 files changed, 7 insertions(+), 25 deletions(-) delete mode 100644 tests/kani/SizeAndAlignOfDst/unsized_tuple.rs diff --git a/kani-compiler/src/kani_middle/stubbing/mod.rs b/kani-compiler/src/kani_middle/stubbing/mod.rs index d70e884400bb..9235287852e1 100644 --- a/kani-compiler/src/kani_middle/stubbing/mod.rs +++ b/kani-compiler/src/kani_middle/stubbing/mod.rs @@ -130,7 +130,7 @@ pub fn validate_stub_const(tcx: TyCtxt, instance: Instance) -> bool { let item = CrateItem::try_from(instance).unwrap(); let internal_instance = rustc_internal::internal(tcx, instance); let mut checker = StubConstChecker::new(tcx, internal_instance, item); - checker.visit_body(&item.body()); + checker.visit_body(&item.expect_body()); checker.is_valid() } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index a17e433c3901..f0c822cd484f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-03-05" +channel = "nightly-2025-03-07" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/kani/SizeAndAlignOfDst/unsized_tuple.rs b/tests/kani/SizeAndAlignOfDst/unsized_tuple.rs deleted file mode 100644 index 0edae52aa0ac..000000000000 --- a/tests/kani/SizeAndAlignOfDst/unsized_tuple.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT -// -//! Ensure we compute the size correctly including padding for unsized tuple. -#![feature(unsized_tuple_coercion)] - -use std::fmt::Debug; - -#[kani::proof] -fn check_adjusted_tup_size() { - let tup: (u32, [u8; 9]) = kani::any(); - let size = std::mem::size_of_val(&tup); - - let unsized_tup: *const (u32, dyn Debug) = &tup as *const _ as *const _; - let adjusted_size = std::mem::size_of_val(unsafe { &*unsized_tup }); - - assert_eq!(size, adjusted_size); -} diff --git a/tools/scanner/src/analysis.rs b/tools/scanner/src/analysis.rs index 89459de72520..1c3907ae1918 100644 --- a/tools/scanner/src/analysis.rs +++ b/tools/scanner/src/analysis.rs @@ -138,7 +138,7 @@ impl OverallStats { if !kind.is_fn() { return None; }; - let unsafe_ops = FnUnsafeOperations::new(item.name()).collect(&item.body()); + let unsafe_ops = FnUnsafeOperations::new(item.name()).collect(&item.expect_body()); let fn_sig = kind.fn_sig().unwrap(); let is_unsafe = fn_sig.skip_binder().safety == Safety::Unsafe; self.fn_stats.get_mut(&item).unwrap().has_unsafe_ops = @@ -167,7 +167,7 @@ impl OverallStats { if !kind.is_fn() { return None; }; - Some(FnLoops::new(item.name()).collect(&item.body())) + Some(FnLoops::new(item.name()).collect(&item.expect_body())) }) .partition::, _>(|props| props.has_loops()); @@ -179,7 +179,7 @@ impl OverallStats { if !kind.is_fn() { return None; }; - Some(FnLoops::new(item.name()).collect(&item.body())) + Some(FnLoops::new(item.name()).collect(&item.expect_body())) }) .partition::, _>(|props| props.has_iterators()); @@ -190,7 +190,7 @@ impl OverallStats { if !kind.is_fn() { return None; }; - let fn_props = FnLoops::new(item.name()).collect(&item.body()); + let fn_props = FnLoops::new(item.name()).collect(&item.expect_body()); self.fn_stats.get_mut(&item).unwrap().has_loop_or_iterator = Some(fn_props.has_iterators() || fn_props.has_loops()); Some(fn_props) @@ -601,7 +601,7 @@ impl Recursion { .into_iter() .filter_map(|item| { if let TyKind::RigidTy(RigidTy::FnDef(def, _)) = item.ty().kind() { - let body = item.body(); + let body = item.expect_body(); let mut visitor = FnCallVisitor { body: &body, fns: vec![] }; visitor.visit_body(&body); Some((def, visitor.fns)) From 44a44e55d0e97ed894ae94331be8a1978cd51db4 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 12 Mar 2025 22:39:44 +0100 Subject: [PATCH 049/161] Upgrade toolchain to nightly-2025-03-12 (#3933) Changes required due to - rust-lang/rust#137977: Reduce `kw::Empty` usage, part 1 Resolves: #3932 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs | 6 +++--- rust-toolchain.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs b/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs index 546a3aa4f127..219bc48c4d91 100644 --- a/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs +++ b/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs @@ -813,7 +813,7 @@ impl<'a, 'tcx> Context<'a, 'tcx> { let disambiguator = CharonDisambiguator::new(data.disambiguator as usize); use rustc_hir::definitions::DefPathData; match &data.data { - DefPathData::TypeNs(symbol) => { + DefPathData::TypeNs(Some(symbol)) => { error_assert!(self, span, data.disambiguator == 0); // Sanity check name.push(CharonPathElem::Ident(symbol.to_string(), disambiguator)); } @@ -956,7 +956,7 @@ impl<'a, 'tcx> Context<'a, 'tcx> { let disambiguator = CharonDisambiguator::new(data.disambiguator as usize); use rustc_hir::definitions::DefPathData; match &data.data { - DefPathData::TypeNs(symbol) => { + DefPathData::TypeNs(Some(symbol)) => { error_assert!(self, span, data.disambiguator == 0); // Sanity check name.push(CharonPathElem::Ident(symbol.to_string(), disambiguator)); } @@ -1063,7 +1063,7 @@ impl<'a, 'tcx> Context<'a, 'tcx> { let disambiguator = CharonDisambiguator::new(data.disambiguator as usize); use rustc_hir::definitions::DefPathData; match &data.data { - DefPathData::TypeNs(symbol) => { + DefPathData::TypeNs(Some(symbol)) => { error_assert!(self, span, data.disambiguator == 0); // Sanity check name.push(CharonPathElem::Ident(symbol.to_string(), disambiguator)); } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f0c822cd484f..aa921b3d6b2f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-03-07" +channel = "nightly-2025-03-12" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 3cf17066c0981f2482cb0aff44eca4def5c5c470 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 13 Mar 2025 00:45:48 -0700 Subject: [PATCH 050/161] Automatic toolchain upgrade to nightly-2025-03-13 (#3934) Update Rust toolchain from nightly-2025-03-12 to nightly-2025-03-13 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index aa921b3d6b2f..73642d50304f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-03-12" +channel = "nightly-2025-03-13" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From e48888716ac16e2ce098c00355a52205c683f679 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Thu, 13 Mar 2025 23:38:51 +0100 Subject: [PATCH 051/161] Update CBMC dependency to 6.5.0 (#3936) Kani now uses CBMC's latest release. Notably, this includes necessary features to eventually support quantifiers in Kani. Resolves: #3796 Resolves: #3935 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- cprover_bindings/src/goto_program/builtin.rs | 11 ------ cprover_bindings/src/goto_program/expr.rs | 12 +++++++ cprover_bindings/src/irep/irep_id.rs | 2 ++ cprover_bindings/src/irep/to_irep.rs | 1 + .../codegen/intrinsic.rs | 35 ++++++------------- kani-dependencies | 4 +-- scripts/setup/macos/install_cbmc.sh | 2 +- 7 files changed, 28 insertions(+), 39 deletions(-) diff --git a/cprover_bindings/src/goto_program/builtin.rs b/cprover_bindings/src/goto_program/builtin.rs index 0ff2296a1024..f925bfcfe58e 100644 --- a/cprover_bindings/src/goto_program/builtin.rs +++ b/cprover_bindings/src/goto_program/builtin.rs @@ -59,8 +59,6 @@ pub enum BuiltinFn { Rintf, Round, Roundf, - RoundToIntegralF, - RoundToIntegral, Sin, Sinf, Sqrt, @@ -125,9 +123,6 @@ impl Display for BuiltinFn { Rintf => "rintf", Round => "round", Roundf => "roundf", - // TODO remove the sort_of prefix once we move up from CBMC 6.4.1 - RoundToIntegralF => "__sort_of_CPROVER_round_to_integralf", - RoundToIntegral => "__sort_of_CPROVER_round_to_integral", Sin => "sin", Sinf => "sinf", Sqrt => "sqrt", @@ -193,8 +188,6 @@ impl BuiltinFn { Rintf => vec![Type::float()], Round => vec![Type::double()], Roundf => vec![Type::float()], - RoundToIntegralF => vec![Type::c_int(), Type::float()], - RoundToIntegral => vec![Type::c_int(), Type::double()], Sin => vec![Type::double()], Sinf => vec![Type::float()], Sqrt => vec![Type::double()], @@ -259,8 +252,6 @@ impl BuiltinFn { Rintf => Type::float(), Round => Type::double(), Roundf => Type::float(), - RoundToIntegralF => Type::float(), - RoundToIntegral => Type::double(), Sin => Type::double(), Sinf => Type::float(), Sqrt => Type::double(), @@ -325,8 +316,6 @@ impl BuiltinFn { Rintf, Round, Roundf, - RoundToIntegralF, - RoundToIntegral, Sin, Sinf, Sqrt, diff --git a/cprover_bindings/src/goto_program/expr.rs b/cprover_bindings/src/goto_program/expr.rs index 5805dc4f7280..6f140b67ec84 100644 --- a/cprover_bindings/src/goto_program/expr.rs +++ b/cprover_bindings/src/goto_program/expr.rs @@ -190,6 +190,7 @@ pub enum BinaryOperator { Bitxor, Div, Equal, + FloatbvRoundToIntegral, Ge, Gt, IeeeFloatEqual, @@ -1029,6 +1030,7 @@ impl Expr { "vector comparison operators must be typechecked by `typecheck_vector_cmp_expr`" ) } + FloatbvRoundToIntegral => lhs.typ.is_floating_point() && rhs.typ.is_integer(), } } @@ -1069,6 +1071,11 @@ impl Expr { "return type for vector comparison operators depends on the place type" ) } + FloatbvRoundToIntegral => { + unreachable!( + "return type for float-to-integer rounding operator depends on the place type" + ) + } } } @@ -1311,6 +1318,11 @@ impl Expr { let cmp = self.clone().gt(e.clone()); cmp.ternary(self, e) } + + /// floating-point to integer rounding + pub fn floatbv_round_to_integral(f: Expr, rm: Expr, ret_typ: Type) -> Expr { + expr!(BinOp { op: FloatbvRoundToIntegral, lhs: f, rhs: rm }, ret_typ) + } } /// Constructors for self operations diff --git a/cprover_bindings/src/irep/irep_id.rs b/cprover_bindings/src/irep/irep_id.rs index 69962edf150e..b3ca7a49ecab 100644 --- a/cprover_bindings/src/irep/irep_id.rs +++ b/cprover_bindings/src/irep/irep_id.rs @@ -835,6 +835,7 @@ pub enum IrepId { VectorLe, VectorGt, VectorLt, + FloatbvRoundToIntegral, } impl IrepId { @@ -1717,6 +1718,7 @@ impl Display for IrepId { IrepId::VectorLe => "vector-<=", IrepId::VectorGt => "vector->", IrepId::VectorLt => "vector-<", + IrepId::FloatbvRoundToIntegral => "floatbv_round_to_integral", }; write!(f, "{s}") } diff --git a/cprover_bindings/src/irep/to_irep.rs b/cprover_bindings/src/irep/to_irep.rs index 7fd364e5d725..b9cc6978ea85 100644 --- a/cprover_bindings/src/irep/to_irep.rs +++ b/cprover_bindings/src/irep/to_irep.rs @@ -60,6 +60,7 @@ impl ToIrepId for BinaryOperator { BinaryOperator::Bitxor => IrepId::Bitxor, BinaryOperator::Div => IrepId::Div, BinaryOperator::Equal => IrepId::Equal, + BinaryOperator::FloatbvRoundToIntegral => IrepId::FloatbvRoundToIntegral, BinaryOperator::Ge => IrepId::Ge, BinaryOperator::Gt => IrepId::Gt, BinaryOperator::IeeeFloatEqual => IrepId::IeeeFloatEqual, diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index ac5b44ec4163..88f4763be49e 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -427,20 +427,9 @@ impl GotocCtx<'_> { Intrinsic::RotateRight => codegen_intrinsic_binop!(ror), Intrinsic::RoundF32 => codegen_simple_intrinsic!(Roundf), Intrinsic::RoundF64 => codegen_simple_intrinsic!(Round), - Intrinsic::RoundTiesEvenF32 => self.codegen_round_to_integral( - BuiltinFn::RoundToIntegralF, - cbmc::RoundingMode::ToNearest, - fargs, - place, - loc, - ), - Intrinsic::RoundTiesEvenF64 => self.codegen_round_to_integral( - BuiltinFn::RoundToIntegral, - cbmc::RoundingMode::ToNearest, - fargs, - place, - loc, - ), + Intrinsic::RoundTiesEvenF32 | Intrinsic::RoundTiesEvenF64 => { + self.codegen_round_to_integral(cbmc::RoundingMode::ToNearest, fargs, place, loc) + } Intrinsic::SaturatingAdd => codegen_intrinsic_binop_with_mm!(saturating_add), Intrinsic::SaturatingSub => codegen_intrinsic_binop_with_mm!(saturating_sub), Intrinsic::SinF32 => codegen_simple_intrinsic!(Sinf), @@ -648,24 +637,20 @@ impl GotocCtx<'_> { dividend_is_int_min.and(divisor_is_minus_one).not() } - // Builds a call to the round_to_integral CPROVER function with specified cbmc::RoundingMode. + // Builds a floatbv_round_to_integral expression with specified cbmc::RoundingMode. fn codegen_round_to_integral( &mut self, - function: BuiltinFn, rounding_mode: cbmc::RoundingMode, mut fargs: Vec, place: &Place, loc: Location, ) -> Stmt { - assert!(function == BuiltinFn::RoundToIntegralF || function == BuiltinFn::RoundToIntegral); - let mm = self.symbol_table.machine_model(); - fargs.insert(0, Expr::int_constant(rounding_mode, Type::c_int())); - let casted_fargs = Expr::cast_arguments_to_target_equivalent_function_parameter_types( - &function.as_expr(), - fargs, - mm, - ); - let expr = function.call(casted_fargs, loc); + let place_ty = self.place_ty_stable(place); + let result_type = self.codegen_ty_stable(place_ty); + let f = fargs.remove(0); + assert!(fargs.is_empty()); + let rm = Expr::int_constant(rounding_mode, Type::c_int()); + let expr = Expr::floatbv_round_to_integral(f, rm, result_type); self.codegen_expr_to_place_stable(place, expr, loc) } diff --git a/kani-dependencies b/kani-dependencies index ed8e32faca90..f620835102de 100644 --- a/kani-dependencies +++ b/kani-dependencies @@ -1,5 +1,5 @@ CBMC_MAJOR="6" -CBMC_MINOR="4" -CBMC_VERSION="6.4.1" +CBMC_MINOR="5" +CBMC_VERSION="6.5.0" KISSAT_VERSION="4.0.1" diff --git a/scripts/setup/macos/install_cbmc.sh b/scripts/setup/macos/install_cbmc.sh index dbef341c9e01..6cd65dac1ac9 100755 --- a/scripts/setup/macos/install_cbmc.sh +++ b/scripts/setup/macos/install_cbmc.sh @@ -15,4 +15,4 @@ fi # Install CBMC for macOS from CBMC tap # https://github.com/diffblue/cbmc/blob/develop/doc/ADR/homebrew_tap.md brew tap diffblue/cbmc -brew install diffblue/cbmc/cbmc@${CBMC_VERSION} +brew install --overwrite diffblue/cbmc/cbmc@${CBMC_VERSION} From 953ff35304469544f15f60255013ad726eced46b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 09:42:18 +0100 Subject: [PATCH 052/161] Automatic toolchain upgrade to nightly-2025-03-14 (#3937) Update Rust toolchain from nightly-2025-03-13 to nightly-2025-03-14 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 73642d50304f..1efacefde41c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-03-13" +channel = "nightly-2025-03-14" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 1e94e64d73cd7fcdcb27a792f8dc2485e4a14c53 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 15 Mar 2025 12:04:27 +0100 Subject: [PATCH 053/161] Automatic toolchain upgrade to nightly-2025-03-15 (#3938) Update Rust toolchain from nightly-2025-03-14 to nightly-2025-03-15 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 1efacefde41c..284b6abdbeec 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-03-14" +channel = "nightly-2025-03-15" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 453c1cd92ca3d3876ff72c870a8db71bc2a7f14b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 16 Mar 2025 09:31:40 +0100 Subject: [PATCH 054/161] Automatic toolchain upgrade to nightly-2025-03-16 (#3939) Update Rust toolchain from nightly-2025-03-15 to nightly-2025-03-16 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 284b6abdbeec..feb4577e70fa 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-03-15" +channel = "nightly-2025-03-16" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 3f2fd03b30d8d9a19720e0484c08e63d631cb11d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 09:20:37 -0400 Subject: [PATCH 055/161] Automatic toolchain upgrade to nightly-2025-03-17 (#3940) Update Rust toolchain from nightly-2025-03-16 to nightly-2025-03-17 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index feb4577e70fa..8bd21a773249 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-03-16" +channel = "nightly-2025-03-17" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 30aa50ef6284c21c063e1c4955e712e3c6092023 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 09:22:01 -0400 Subject: [PATCH 056/161] Automatic cargo update to 2025-03-17 (#3941) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 108 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 70 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f93dccce4716..c20da8f17156 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -276,9 +276,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.31" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" +checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" dependencies = [ "clap_builder", "clap_derive", @@ -286,9 +286,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.31" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" +checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" dependencies = [ "anstream", "anstyle", @@ -298,9 +298,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", @@ -581,14 +581,14 @@ checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" [[package]] name = "env_logger" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" +checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697" dependencies = [ "anstream", "anstyle", "env_filter", - "humantime", + "jiff", "log", ] @@ -628,9 +628,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "getopts" @@ -730,12 +730,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "ident_case" version = "1.0.1" @@ -759,9 +753,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -794,6 +788,30 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jiff" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "joinery" version = "2.1.0" @@ -924,9 +942,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.170" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "linear-map" @@ -946,9 +964,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" [[package]] name = "lock_api" @@ -1163,9 +1181,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" [[package]] name = "os_info" @@ -1228,6 +1246,21 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1312,9 +1345,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -1452,14 +1485,14 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dade4812df5c384711475be5fcd8c162555352945401aed22a35bffeab61f657" +checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.9.2", + "linux-raw-sys 0.9.3", "windows-sys 0.59.0", ] @@ -1710,15 +1743,14 @@ checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "tempfile" -version = "3.18.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567" +checksum = "488960f40a3fd53d72c2a29a58722561dee8afdd175bd88e3db4677d7b2ba600" dependencies = [ - "cfg-if", "fastrand", "getrandom 0.3.1", "once_cell", - "rustix 1.0.1", + "rustix 1.0.2", "windows-sys 0.59.0", ] @@ -1822,9 +1854,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.0" +version = "1.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9975ea0f48b5aa3972bf2d888c238182458437cc2a19374b81b25cdf1023fb3a" +checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" dependencies = [ "backtrace", "bytes", @@ -2206,9 +2238,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" dependencies = [ "memchr", ] From ce89f616e4e9de6746a1095d7e873c3f2404dff2 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Mon, 17 Mar 2025 09:53:03 -0700 Subject: [PATCH 057/161] Autoharness: Don't panic on `_` argument and add `_autoharness` suffix to GOTO files (#3942) Turns out that if an argument is just `_`, it doesn't have `var_debug_info` in StableMIR. So this PR changes autoharness behavior to provide `_` for the argument name when `var_debug_info` is None (instead of panicking). I don't currently know of any other cases where there wouldn't be `var_debug_info`, but we should pay extra attention to the cases where autoharness reports `_` as an argument name and update this logic if need be. I also added an `_autoharness` suffix to the generated GOTO files so that it's easier to tell which files are from autoharness and which aren't. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- kani-compiler/src/kani_middle/codegen_units.rs | 6 +++--- kani-compiler/src/kani_middle/metadata.rs | 3 ++- .../cargo_autoharness_filter/filter.expected | 2 ++ tests/script-based-pre/cargo_autoharness_filter/src/lib.rs | 3 +++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/kani-compiler/src/kani_middle/codegen_units.rs b/kani-compiler/src/kani_middle/codegen_units.rs index 189c14b246b3..a0f63463053d 100644 --- a/kani-compiler/src/kani_middle/codegen_units.rs +++ b/kani-compiler/src/kani_middle/codegen_units.rs @@ -425,14 +425,14 @@ fn automatic_harness_partition( if !implements_arbitrary { // Find the name of the argument by referencing var_debug_info. // Note that enumerate() starts at 0, while StableMIR argument_index starts at 1, hence the idx+1. - let arg_debug_info = body + let arg_name = body .var_debug_info .iter() .find(|var| { var.argument_index.is_some_and(|arg_idx| idx + 1 == usize::from(arg_idx)) }) - .expect("Arguments should have corresponding var debug info"); - problematic_args.push(arg_debug_info.name.to_string()) + .map_or("_".to_string(), |debug_info| debug_info.name.to_string()); + problematic_args.push(arg_name) } } if !problematic_args.is_empty() { diff --git a/kani-compiler/src/kani_middle/metadata.rs b/kani-compiler/src/kani_middle/metadata.rs index f8de975802e1..b24b39578df5 100644 --- a/kani-compiler/src/kani_middle/metadata.rs +++ b/kani-compiler/src/kani_middle/metadata.rs @@ -101,7 +101,8 @@ pub fn gen_automatic_proof_metadata( // Leave the concrete playback instrumentation for now, but this feature does not actually support concrete playback. let loc = SourceLocation::new(fn_to_verify.body().unwrap().span); - let file_stem = format!("{}_{mangled_name}", base_name.file_stem().unwrap().to_str().unwrap()); + let file_stem = + format!("{}_{mangled_name}_autoharness", base_name.file_stem().unwrap().to_str().unwrap()); let model_file = base_name.with_file_name(file_stem).with_extension(ArtifactType::SymTabGoto); let kani_attributes = KaniAttributes::for_instance(tcx, *fn_to_verify); diff --git a/tests/script-based-pre/cargo_autoharness_filter/filter.expected b/tests/script-based-pre/cargo_autoharness_filter/filter.expected index 5e60f3a426be..f324effe3d9b 100644 --- a/tests/script-based-pre/cargo_autoharness_filter/filter.expected +++ b/tests/script-based-pre/cargo_autoharness_filter/filter.expected @@ -147,6 +147,8 @@ If you believe that the provided reason is incorrect and Kani should have genera |----------------------------------------+------------------------------------------------------| | no_harness::unsupported_mut_pointer | Missing Arbitrary implementation for argument(s): _y | |----------------------------------------+------------------------------------------------------| +| no_harness::unsupported_no_arg_name | Missing Arbitrary implementation for argument(s): _ | +|----------------------------------------+------------------------------------------------------| | no_harness::unsupported_ref | Missing Arbitrary implementation for argument(s): _y | |----------------------------------------+------------------------------------------------------| | no_harness::unsupported_slice | Missing Arbitrary implementation for argument(s): _y | diff --git a/tests/script-based-pre/cargo_autoharness_filter/src/lib.rs b/tests/script-based-pre/cargo_autoharness_filter/src/lib.rs index bb278c73a2e7..0237606e2931 100644 --- a/tests/script-based-pre/cargo_autoharness_filter/src/lib.rs +++ b/tests/script-based-pre/cargo_autoharness_filter/src/lib.rs @@ -207,4 +207,7 @@ mod no_harness { ) -> DoesntImplementArbitrary { x } + // Test that we correctly render the name of the argument "_" in the table of skipped functions + // (this argument will have no var_debug_info from StableMIR, unlike arguments with names) + fn unsupported_no_arg_name(_: &()) {} } From 707309bd9eaf21c4f9f4cae490f4b673d0f2410a Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Mon, 17 Mar 2025 12:38:37 -0700 Subject: [PATCH 058/161] Implement `f16` and `f128` cases in `codegen_float_type` (#3943) See https://github.com/model-checking/kani/issues/3069#issuecomment-2730501056 for a description of the issue. Resolves #3069 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../src/codegen_cprover_gotoc/codegen/typ.rs | 5 ++--- tests/kani/FloatingPoint/main.rs | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index 48ce2ac7043d..84d2c52afba4 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -1400,11 +1400,10 @@ impl<'tcx> GotocCtx<'tcx> { pub fn codegen_float_type(&self, f: Float) -> Ty<'tcx> { match f { + Float::F16 => self.tcx.types.f16, Float::F32 => self.tcx.types.f32, Float::F64 => self.tcx.types.f64, - // `F16` and `F128` are not yet handled. - // Tracked here: - Float::F16 | Float::F128 => unimplemented!(), + Float::F128 => self.tcx.types.f128, } } diff --git a/tests/kani/FloatingPoint/main.rs b/tests/kani/FloatingPoint/main.rs index f8ebccdac02a..38d801207594 100644 --- a/tests/kani/FloatingPoint/main.rs +++ b/tests/kani/FloatingPoint/main.rs @@ -3,6 +3,7 @@ #![feature(f16)] #![feature(f128)] +#![feature(repr_simd)] macro_rules! test_floats { ($ty:ty) => { @@ -35,3 +36,17 @@ fn main() { test_floats!(f64); test_floats!(f128); } + +// Test that we can codegen floats when we hit them in codegen_float_type, +// c.f. https://github.com/model-checking/kani/issues/3069#issuecomment-2730501056 +#[repr(simd)] +struct f16x16([f16; 16]); + +fn make_float_array() -> f16x16 { + f16x16([1.0; 16]) +} + +#[kani::proof] +fn make_float_array_harness() { + let _ = make_float_array(); +} From 62dd13c914191b91ffd8244f70508791e6270a52 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 18 Mar 2025 20:57:45 +0100 Subject: [PATCH 059/161] Support function implementations of known built-ins (#3945) Kani should produce valid symbol tables in GOTO binaries when Rust code provides an implementation for a built-in known to Kani. Without this fix, goto-cc fails the following invariant: ``` --- begin invariant violation report --- Invariant check failed File: ../src/ansi-c/goto-conversion/goto_convert_functions.cpp:164 function: convert_function Condition: parameter identifier should not be empty Reason: !p.empty() ``` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../codegen_cprover_gotoc/codegen/function.rs | 25 ++++++++++++------- tests/kani/FunctionSymbols/builtin_name.rs | 16 ++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 tests/kani/FunctionSymbols/builtin_name.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs index 15a4e6357349..7def214fc5a8 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs @@ -204,15 +204,22 @@ impl GotocCtx<'_> { let body = self.transformer.body(self.tcx, instance); self.set_current_fn(instance, &body); debug!(krate=?instance.def.krate(), is_std=self.current_fn().is_std(), "declare_function"); - self.ensure(instance.mangled_name(), |ctx, fname| { - Symbol::function( - fname, - ctx.fn_typ(instance, &body), - None, - instance.name(), - ctx.codegen_span_stable(instance.def.span()), - ) - }); + let fname = instance.mangled_name(); + let sym = Symbol::function( + &fname, + self.fn_typ(instance, &body), + None, + instance.name(), + self.codegen_span_stable(instance.def.span()), + ); + if !self.symbol_table.contains((&fname).into()) { + self.symbol_table.insert(sym); + } else { + let old_sym = self.symbol_table.lookup(fname).unwrap(); + if old_sym.location.is_builtin() { + self.symbol_table.replace(|_| true, sym); + } + } self.reset_current_fn(); } } diff --git a/tests/kani/FunctionSymbols/builtin_name.rs b/tests/kani/FunctionSymbols/builtin_name.rs new file mode 100644 index 000000000000..d954e464e5fb --- /dev/null +++ b/tests/kani/FunctionSymbols/builtin_name.rs @@ -0,0 +1,16 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Function with the same name as a known built-in, but we provide an alternative implementation +// instead of using the built-in. +#[no_mangle] +fn copysign(a: f64, _b: f64) -> f64 { + a +} + +#[kani::proof] +pub fn harness() { + let a: f64 = kani::any(); + let b: f64 = kani::any(); + copysign(a, b); +} From 5d0ccca88bfc7c4c60791dd26ececa3dcb1a055a Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Wed, 19 Mar 2025 22:39:02 -0400 Subject: [PATCH 060/161] Autoharness: metadata improvements and enable standard library application (#3948) Update the autoharness subcommand to: - Print the types of the arguments that don't implement Arbitrary - Print a table of the functions we chose to verify after codegen finishes, and move the printing of the functions we skipped to be after codegen as well, instead of after verification. This lets us iterate faster to debug because we can pass `--only-codegen` and see pretty-printed data instead of waiting for verification to finish. - Add a `--std` flag which, when provided, runs autoharness against the provided path to the standard library. Towards #3832 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../compiler_interface.rs | 2 +- .../src/kani_middle/codegen_units.rs | 37 ++--- kani-driver/src/args/autoharness_args.rs | 12 +- kani-driver/src/args/list_args.rs | 36 +---- kani-driver/src/args/mod.rs | 38 ++++- kani-driver/src/args/std_args.rs | 36 +---- kani-driver/src/autoharness/mod.rs | 95 +++++++++---- kani-driver/src/metadata.rs | 2 +- kani_metadata/src/lib.rs | 22 +-- .../contracts.expected | 19 ++- .../cargo_autoharness_contracts/src/lib.rs | 2 +- .../dependencies.expected | 12 +- .../exclude.expected | 27 ++-- .../cargo_autoharness_filter/filter.expected | 133 +++++++++++++++--- .../harnesses_fail.expected | 19 ++- .../include.expected | 27 ++-- .../termination.expected | 15 +- 17 files changed, 353 insertions(+), 181 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index 70c146489030..8ed90b2c975e 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -613,7 +613,7 @@ impl GotoCodegenResults { // removes any contracts logic for ReachabilityType::Test or ReachabilityType::PubFns, // which are the two ReachabilityTypes under which the compiler calls this function. contracted_functions: vec![], - autoharness_skipped_fns: None, + autoharness_md: None, } } diff --git a/kani-compiler/src/kani_middle/codegen_units.rs b/kani-compiler/src/kani_middle/codegen_units.rs index a0f63463053d..cf8b33232f40 100644 --- a/kani-compiler/src/kani_middle/codegen_units.rs +++ b/kani-compiler/src/kani_middle/codegen_units.rs @@ -18,7 +18,7 @@ use crate::kani_middle::resolve::expect_resolve_fn; use crate::kani_middle::stubbing::{check_compatibility, harness_stub_map}; use crate::kani_queries::QueryDb; use kani_metadata::{ - ArtifactType, AssignsContract, AutoHarnessSkipReason, AutoHarnessSkippedFns, HarnessKind, + ArtifactType, AssignsContract, AutoHarnessMetadata, AutoHarnessSkipReason, HarnessKind, HarnessMetadata, KaniMetadata, }; use rustc_hir::def_id::DefId; @@ -32,6 +32,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::fs::File; use std::io::BufWriter; use std::path::{Path, PathBuf}; +use std::sync::OnceLock; use tracing::debug; /// An identifier for the harness function. @@ -40,6 +41,8 @@ type Harness = Instance; /// A set of stubs. pub type Stubs = HashMap; +static AUTOHARNESS_MD: OnceLock = OnceLock::new(); + /// Store some relevant information about the crate compilation. #[derive(Clone, Debug)] struct CrateInfo { @@ -49,7 +52,6 @@ struct CrateInfo { /// We group the harnesses that have the same stubs. pub struct CodegenUnits { - autoharness_skipped_fns: Option, crate_info: CrateInfo, harness_info: HashMap, units: Vec, @@ -74,12 +76,7 @@ impl CodegenUnits { let units = group_by_stubs(tcx, &all_harnesses); validate_units(tcx, &units); debug!(?units, "CodegenUnits::new"); - CodegenUnits { - units, - harness_info: all_harnesses, - crate_info, - autoharness_skipped_fns: None, - } + CodegenUnits { units, harness_info: all_harnesses, crate_info } } ReachabilityType::AllFns => { let mut all_harnesses = get_all_manual_harnesses(tcx, base_filename); @@ -95,6 +92,11 @@ impl CodegenUnits { args, *kani_fns.get(&KaniModel::Any.into()).unwrap(), ); + let chosen_fn_names = + chosen.iter().map(|func| func.name().clone()).collect::>(); + AUTOHARNESS_MD + .set(AutoHarnessMetadata { chosen: chosen_fn_names, skipped }) + .expect("Initializing the autoharness metdata failed"); let automatic_harnesses = get_all_automatic_harnesses( tcx, @@ -117,21 +119,11 @@ impl CodegenUnits { // No need to validate the units again because validation only checks stubs, and we haven't added any stubs. debug!(?units, "CodegenUnits::new"); - CodegenUnits { - units, - harness_info: all_harnesses, - crate_info, - autoharness_skipped_fns: Some(skipped), - } + CodegenUnits { units, harness_info: all_harnesses, crate_info } } _ => { // Leave other reachability type handling as is for now. - CodegenUnits { - units: vec![], - harness_info: HashMap::default(), - crate_info, - autoharness_skipped_fns: None, - } + CodegenUnits { units: vec![], harness_info: HashMap::default(), crate_info } } } } @@ -176,7 +168,7 @@ impl CodegenUnits { unsupported_features: vec![], test_harnesses, contracted_functions: gen_contracts_metadata(tcx), - autoharness_skipped_fns: self.autoharness_skipped_fns.clone(), + autoharness_md: AUTOHARNESS_MD.get().cloned(), } } } @@ -432,7 +424,8 @@ fn automatic_harness_partition( var.argument_index.is_some_and(|arg_idx| idx + 1 == usize::from(arg_idx)) }) .map_or("_".to_string(), |debug_info| debug_info.name.to_string()); - problematic_args.push(arg_name) + let arg_type = format!("{}", arg.ty); + problematic_args.push((arg_name, arg_type)) } } if !problematic_args.is_empty() { diff --git a/kani-driver/src/args/autoharness_args.rs b/kani-driver/src/args/autoharness_args.rs index 1e778e1f3646..bdba0e340d8d 100644 --- a/kani-driver/src/args/autoharness_args.rs +++ b/kani-driver/src/args/autoharness_args.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; -use crate::args::{ValidateArgs, VerificationArgs}; +use crate::args::{ValidateArgs, VerificationArgs, validate_std_path}; use clap::{Error, Parser, error::ErrorKind}; use kani_metadata::UnstableFeature; @@ -54,6 +54,11 @@ pub struct StandaloneAutoharnessArgs { #[command(flatten)] pub common_autoharness_args: CommonAutoharnessArgs, + /// Pass this flag to run the `autoharness` subcommand on the standard library. + /// Ensure that the provided `input` is the `library` folder. + #[arg(long)] + pub std: bool, + #[command(flatten)] pub verify_opts: VerificationArgs, } @@ -99,7 +104,10 @@ impl ValidateArgs for StandaloneAutoharnessArgs { ), )); } - if !self.input.is_file() { + + if self.std { + validate_std_path(&self.input)?; + } else if !self.input.is_file() { return Err(Error::raw( ErrorKind::InvalidValue, format!( diff --git a/kani-driver/src/args/list_args.rs b/kani-driver/src/args/list_args.rs index ebd97d01f2fb..35d26f22eb36 100644 --- a/kani-driver/src/args/list_args.rs +++ b/kani-driver/src/args/list_args.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; -use crate::args::{CommonArgs, ValidateArgs}; +use crate::args::{CommonArgs, ValidateArgs, validate_std_path}; use clap::{Error, Parser, ValueEnum, error::ErrorKind}; use kani_metadata::UnstableFeature; @@ -79,39 +79,7 @@ impl ValidateArgs for StandaloneListArgs { } if self.std { - if !self.input.exists() { - Err(Error::raw( - ErrorKind::InvalidValue, - format!( - "Invalid argument: `` argument `{}` does not exist", - self.input.display() - ), - )) - } else if !self.input.is_dir() { - Err(Error::raw( - ErrorKind::InvalidValue, - format!( - "Invalid argument: `` argument `{}` is not a directory", - self.input.display() - ), - )) - } else { - let full_path = self.input.canonicalize()?; - let dir = full_path.file_stem().unwrap(); - if dir != "library" { - Err(Error::raw( - ErrorKind::InvalidValue, - format!( - "Invalid argument: Expected `` to point to the `library` folder \ - containing the standard library crates.\n\ - Found `{}` folder instead", - dir.to_string_lossy() - ), - )) - } else { - Ok(()) - } - } + validate_std_path(&self.input) } else if self.input.is_file() { Ok(()) } else { diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index 26e9927cf67c..fb18504c6182 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -20,7 +20,7 @@ use clap::builder::{PossibleValue, TypedValueParser}; use clap::{ValueEnum, error::ContextKind, error::ContextValue, error::Error, error::ErrorKind}; use kani_metadata::CbmcSolver; use std::ffi::OsString; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::str::FromStr; use std::time::Duration; use strum::VariantNames; @@ -789,6 +789,42 @@ impl ValidateArgs for VerificationArgs { } } +pub(crate) fn validate_std_path(std_path: &Path) -> Result<(), Error> { + if !std_path.exists() { + Err(Error::raw( + ErrorKind::InvalidValue, + format!( + "Invalid argument: `` argument `{}` does not exist", + std_path.display() + ), + )) + } else if !std_path.is_dir() { + Err(Error::raw( + ErrorKind::InvalidValue, + format!( + "Invalid argument: `` argument `{}` is not a directory", + std_path.display() + ), + )) + } else { + let full_path = std_path.canonicalize()?; + let dir = full_path.file_stem().unwrap(); + if dir != "library" { + Err(Error::raw( + ErrorKind::InvalidValue, + format!( + "Invalid argument: Expected `` to point to the `library` folder \ + containing the standard library crates.\n\ + Found `{}` folder instead", + dir.to_string_lossy() + ), + )) + } else { + Ok(()) + } + } +} + /// clap parser for `CbmcSolver` #[derive(Clone, Debug)] pub struct CbmcSolverValueParser(Vec); diff --git a/kani-driver/src/args/std_args.rs b/kani-driver/src/args/std_args.rs index 3818b6261010..2968b1108cc0 100644 --- a/kani-driver/src/args/std_args.rs +++ b/kani-driver/src/args/std_args.rs @@ -3,7 +3,7 @@ //! Implements the `verify-std` subcommand handling. -use crate::args::{ValidateArgs, VerificationArgs}; +use crate::args::{ValidateArgs, VerificationArgs, validate_std_path}; use clap::error::ErrorKind; use clap::{Error, Parser}; use kani_metadata::UnstableFeature; @@ -40,38 +40,6 @@ impl ValidateArgs for VerifyStdArgs { )); } - if !self.std_path.exists() { - Err(Error::raw( - ErrorKind::InvalidValue, - format!( - "Invalid argument: `` argument `{}` does not exist", - self.std_path.display() - ), - )) - } else if !self.std_path.is_dir() { - Err(Error::raw( - ErrorKind::InvalidValue, - format!( - "Invalid argument: `` argument `{}` is not a directory", - self.std_path.display() - ), - )) - } else { - let full_path = self.std_path.canonicalize()?; - let dir = full_path.file_stem().unwrap(); - if dir != "library" { - Err(Error::raw( - ErrorKind::InvalidValue, - format!( - "Invalid argument: Expected `` to point to the `library` folder \ - containing the standard library crates.\n\ - Found `{}` folder instead", - dir.to_string_lossy() - ), - )) - } else { - Ok(()) - } - } + validate_std_path(&self.std_path) } } diff --git a/kani-driver/src/autoharness/mod.rs b/kani-driver/src/autoharness/mod.rs index 69dd8b561103..251a35570720 100644 --- a/kani-driver/src/autoharness/mod.rs +++ b/kani-driver/src/autoharness/mod.rs @@ -8,6 +8,7 @@ use crate::args::autoharness_args::{CargoAutoharnessArgs, StandaloneAutoharnessA use crate::call_cbmc::VerificationStatus; use crate::call_single_file::to_rustc_arg; use crate::harness_runner::HarnessResult; +use crate::project::{standalone_project, std_project}; use crate::session::KaniSession; use crate::{InvocationType, print_kani_version, project, verify_project}; use anyhow::Result; @@ -25,11 +26,14 @@ pub fn autoharness_cargo(args: CargoAutoharnessArgs) -> Result<()> { args.common_autoharness_args.include_function, args.common_autoharness_args.exclude_function, ); + if !session.args.common_args.quiet { + print_kani_version(InvocationType::CargoKani(vec![])); + } let project = project::cargo_project(&mut session, false)?; - let metadata = project.metadata.clone(); - let res = verify_project(project, session); - print_skipped_fns(metadata); - res + if !session.args.common_args.quiet { + print_metadata(project.metadata.clone()); + } + if session.args.only_codegen { Ok(()) } else { verify_project(project, session) } } pub fn autoharness_standalone(args: StandaloneAutoharnessArgs) -> Result<()> { @@ -45,46 +49,83 @@ pub fn autoharness_standalone(args: StandaloneAutoharnessArgs) -> Result<()> { print_kani_version(InvocationType::Standalone); } - let project = project::standalone_project(&args.input, args.crate_name, &session)?; - let metadata = project.metadata.clone(); - let res = verify_project(project, session); - print_skipped_fns(metadata); - res + let project = if args.std { + std_project(&args.input, &session)? + } else { + standalone_project(&args.input, args.crate_name, &session)? + }; + + if !session.args.common_args.quiet { + print_metadata(project.metadata.clone()); + } + if session.args.only_codegen { Ok(()) } else { verify_project(project, session) } } -/// Print a table of the functions that we skipped and why. -fn print_skipped_fns(metadata: Vec) { - let mut skipped_fns = PrettyTable::new(); - skipped_fns.set_header(vec!["Skipped Function", "Reason for Skipping"]); +/// Print automatic harness metadata to the terminal. +fn print_metadata(metadata: Vec) { + let mut chosen_table = PrettyTable::new(); + chosen_table.set_header(vec!["Chosen Function"]); + + let mut skipped_table = PrettyTable::new(); + skipped_table.set_header(vec!["Skipped Function", "Reason for Skipping"]); for md in metadata { - let skipped_md = md.autoharness_skipped_fns.unwrap(); - skipped_fns.add_rows(skipped_md.into_iter().filter_map(|(func, reason)| match reason { - AutoHarnessSkipReason::MissingArbitraryImpl(ref args) => { - Some(vec![func, format!("{}: {}", reason, args.join(", "))]) + let autoharness_md = md.autoharness_md.unwrap(); + chosen_table.add_rows(autoharness_md.chosen.into_iter().map(|func| vec![func])); + skipped_table.add_rows(autoharness_md.skipped.into_iter().filter_map(|(func, reason)| { + match reason { + AutoHarnessSkipReason::MissingArbitraryImpl(ref args) => Some(vec![ + func, + format!( + "{reason} {}", + args.iter() + .map(|(name, typ)| format!("{}: {}", name, typ)) + .collect::>() + .join(", ") + ), + ]), + AutoHarnessSkipReason::GenericFn + | AutoHarnessSkipReason::NoBody + | AutoHarnessSkipReason::UserFilter => Some(vec![func, reason.to_string()]), + // We don't report Kani implementations to the user to avoid exposing Kani functions we insert during instrumentation. + // For those we don't insert during instrumentation that are in this category (manual harnesses or Kani trait implementations), + // it should be obvious that we wouldn't generate harnesses, so reporting those functions as "skipped" is unlikely to be useful. + AutoHarnessSkipReason::KaniImpl => None, } - AutoHarnessSkipReason::GenericFn - | AutoHarnessSkipReason::NoBody - | AutoHarnessSkipReason::UserFilter => Some(vec![func, reason.to_string()]), - // We don't report Kani implementations to the user to avoid exposing Kani functions we insert during instrumentation. - // For those we don't insert during instrumentation that are in this category (manual harnesses or Kani trait implementations), - // it should be obvious that we wouldn't generate harnesses, so reporting those functions as "skipped" is unlikely to be useful. - AutoHarnessSkipReason::KaniImpl => None, })); } - if skipped_fns.is_empty() { + print_chosen_table(&mut chosen_table); + print_skipped_table(&mut skipped_table); +} + +/// Print the table of functions for which we generated automatic harnesses. +fn print_chosen_table(table: &mut PrettyTable) { + if table.is_empty() { + println!( + "\nChosen Functions: None. Kani did not generate automatic harnesses for any functions in the available crate(s)." + ); + return; + } + + println!("\nKani generated automatic harnesses for {} function(s):", table.row_count()); + println!("{table}"); +} + +/// Print the table of functions for which we did not generate automatic harnesses. +fn print_skipped_table(table: &mut PrettyTable) { + if table.is_empty() { println!( "\nSkipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s)." ); return; } - println!("\nKani did not generate automatic harnesses for the following functions."); + println!("\nKani did not generate automatic harnesses for {} function(s).", table.row_count()); println!( "If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832" ); - println!("{skipped_fns}"); + println!("{table}"); } impl KaniSession { diff --git a/kani-driver/src/metadata.rs b/kani-driver/src/metadata.rs index 8a7ee8690fc8..5f83b298b9b8 100644 --- a/kani-driver/src/metadata.rs +++ b/kani-driver/src/metadata.rs @@ -97,7 +97,7 @@ pub fn merge_kani_metadata(files: Vec) -> KaniMetadata { unsupported_features: vec![], test_harnesses: vec![], contracted_functions: vec![], - autoharness_skipped_fns: None, + autoharness_md: None, }; for md in files { // Note that we're taking ownership of the original vec, and so we can move the data into the new data structure. diff --git a/kani_metadata/src/lib.rs b/kani_metadata/src/lib.rs index 2fe1038647c1..775679329753 100644 --- a/kani_metadata/src/lib.rs +++ b/kani_metadata/src/lib.rs @@ -38,7 +38,18 @@ pub struct KaniMetadata { /// The functions with contracts in this crate pub contracted_functions: Vec, /// Metadata for the `autoharness` subcommand - pub autoharness_skipped_fns: Option, + pub autoharness_md: Option, +} + +/// For the autoharness subcommand, all of the user-defined functions we found, +/// which are "chosen" if we generated an automatic harness for them, and "skipped" otherwise. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AutoHarnessMetadata { + /// Functions we generated automatic harnesses for. + pub chosen: Vec, + /// Map function names to the reason why we did not generate an automatic harness for that function. + /// We use an ordered map so that when we print the data, it is ordered alphabetically by function name. + pub skipped: BTreeMap, } /// Reasons that Kani does not generate an automatic harness for a function. @@ -51,9 +62,9 @@ pub enum AutoHarnessSkipReason { #[strum(serialize = "Kani implementation")] KaniImpl, /// At least one of the function's arguments does not implement kani::Arbitrary - /// (The Vec contains the list of argument names that do not implement it) + /// (The Vec<(String, String)> contains the list of (name, type) tuples for each argument that does not implement it #[strum(serialize = "Missing Arbitrary implementation for argument(s)")] - MissingArbitraryImpl(Vec), + MissingArbitraryImpl(Vec<(String, String)>), /// The function does not have a body. #[strum(serialize = "The function does not have a body")] NoBody, @@ -61,11 +72,6 @@ pub enum AutoHarnessSkipReason { #[strum(serialize = "Did not match provided filters")] UserFilter, } - -/// For the autoharness subcommand: map function names to the reason why we did not generate an automatic harness for that function. -/// We use an ordered map so that when we print the data, it is ordered alphabetically by function name. -pub type AutoHarnessSkippedFns = BTreeMap; - #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] pub struct ContractedFunction { /// The fully qualified name the user gave to the function (i.e. includes the module path). diff --git a/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected b/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected index 9babfe015117..200dd56bdaba 100644 --- a/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected +++ b/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected @@ -1,3 +1,20 @@ +Kani generated automatic harnesses for 5 function(s): ++--------------------------------+ +| Chosen Function | ++================================+ +| should_pass::div | +|--------------------------------| +| should_pass::has_recursion_gcd | +|--------------------------------| +| should_pass::has_loop_contract | +|--------------------------------| +| should_pass::unchecked_mul | +|--------------------------------| +| should_fail::max | ++--------------------------------+ + +Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). + Autoharness: Checking function should_fail::max's contract against all possible inputs... assertion\ - Status: FAILURE\ @@ -40,5 +57,3 @@ Autoharness Summary: Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). Complete - 4 successfully verified functions, 1 failures, 5 total. - -Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). diff --git a/tests/script-based-pre/cargo_autoharness_contracts/src/lib.rs b/tests/script-based-pre/cargo_autoharness_contracts/src/lib.rs index c9f9dd3fd1b0..c66b3dab4fbe 100644 --- a/tests/script-based-pre/cargo_autoharness_contracts/src/lib.rs +++ b/tests/script-based-pre/cargo_autoharness_contracts/src/lib.rs @@ -44,7 +44,7 @@ mod should_pass { // Test that we can autoharness functions for unsafe functions with contracts #[kani::requires(!left.overflowing_mul(rhs).1)] unsafe fn unchecked_mul(left: u8, rhs: u8) -> u8 { - left.unchecked_mul(rhs) + unsafe { left.unchecked_mul(rhs) } } } diff --git a/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected b/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected index 5a03fa9b8808..aee1a3675f06 100644 --- a/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected +++ b/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected @@ -1,4 +1,14 @@ +Kani generated automatic harnesses for 1 function(s): ++-----------------+ +| Chosen Function | ++=================+ +| yes_harness | ++-----------------+ + +Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). + Autoharness: Checking function yes_harness against all possible inputs... +VERIFICATION:- SUCCESSFUL Manual Harness Summary: No proof harnesses (functions with #[kani::proof]) were found to verify. @@ -10,5 +20,3 @@ Autoharness Summary: | yes_harness | #[kani::proof] | Success | +-------------------+---------------------------+---------------------+ Complete - 1 successfully verified functions, 0 failures, 1 total. - -Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). diff --git a/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected b/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected index 4c0192e92448..2f5e75ef5a3c 100644 --- a/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected +++ b/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected @@ -1,3 +1,20 @@ +Kani generated automatic harnesses for 1 function(s): ++-----------------+ +| Chosen Function | ++=================+ +| include::simple | ++-----------------+ + +Kani did not generate automatic harnesses for 2 function(s). +If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832 ++------------------+--------------------------------+ +| Skipped Function | Reason for Skipping | ++===================================================+ +| excluded::simple | Did not match provided filters | +|------------------+--------------------------------| +| include::generic | Generic Function | ++------------------+--------------------------------+ + Autoharness: Checking function include::simple against all possible inputs... VERIFICATION:- SUCCESSFUL @@ -11,13 +28,3 @@ Autoharness Summary: | include::simple | #[kani::proof] | Success | +-------------------+---------------------------+---------------------+ Complete - 1 successfully verified functions, 0 failures, 1 total. - -Kani did not generate automatic harnesses for the following functions. -If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832 -+------------------+--------------------------------+ -| Skipped Function | Reason for Skipping | -+===================================================+ -| excluded::simple | Did not match provided filters | -|------------------+--------------------------------| -| include::generic | Generic Function | -+------------------+--------------------------------+ diff --git a/tests/script-based-pre/cargo_autoharness_filter/filter.expected b/tests/script-based-pre/cargo_autoharness_filter/filter.expected index f324effe3d9b..8be532a63419 100644 --- a/tests/script-based-pre/cargo_autoharness_filter/filter.expected +++ b/tests/script-based-pre/cargo_autoharness_filter/filter.expected @@ -1,3 +1,114 @@ +Kani generated automatic harnesses for 42 function(s): ++----------------------------------------------+ +| Chosen Function | ++==============================================+ +| yes_harness::f_u8 | +|----------------------------------------------| +| yes_harness::f_u16 | +|----------------------------------------------| +| yes_harness::f_u32 | +|----------------------------------------------| +| yes_harness::f_u64 | +|----------------------------------------------| +| yes_harness::f_u128 | +|----------------------------------------------| +| yes_harness::f_usize | +|----------------------------------------------| +| yes_harness::f_i8 | +|----------------------------------------------| +| yes_harness::f_i16 | +|----------------------------------------------| +| yes_harness::f_i32 | +|----------------------------------------------| +| yes_harness::f_i64 | +|----------------------------------------------| +| yes_harness::f_i128 | +|----------------------------------------------| +| yes_harness::f_isize | +|----------------------------------------------| +| yes_harness::f_bool | +|----------------------------------------------| +| yes_harness::f_char | +|----------------------------------------------| +| yes_harness::f_f32 | +|----------------------------------------------| +| yes_harness::f_f64 | +|----------------------------------------------| +| yes_harness::f_f16 | +|----------------------------------------------| +| yes_harness::f_f128 | +|----------------------------------------------| +| yes_harness::f_nonzero_u8 | +|----------------------------------------------| +| yes_harness::f_nonzero_u16 | +|----------------------------------------------| +| yes_harness::f_nonzero_u32 | +|----------------------------------------------| +| yes_harness::f_nonzero_u64 | +|----------------------------------------------| +| yes_harness::f_nonzero_u128 | +|----------------------------------------------| +| yes_harness::f_nonzero_usize | +|----------------------------------------------| +| yes_harness::f_nonzero_i8 | +|----------------------------------------------| +| yes_harness::f_nonzero_i16 | +|----------------------------------------------| +| yes_harness::f_nonzero_i32 | +|----------------------------------------------| +| yes_harness::f_nonzero_i64 | +|----------------------------------------------| +| yes_harness::f_nonzero_i128 | +|----------------------------------------------| +| yes_harness::f_nonzero_isize | +|----------------------------------------------| +| yes_harness::f_array | +|----------------------------------------------| +| yes_harness::f_option | +|----------------------------------------------| +| yes_harness::f_result | +|----------------------------------------------| +| yes_harness::f_maybe_uninit | +|----------------------------------------------| +| yes_harness::f_tuple | +|----------------------------------------------| +| yes_harness::f_unsupported_return_type | +|----------------------------------------------| +| yes_harness::f_multiple_args | +|----------------------------------------------| +| yes_harness::f_derives_arbitrary | +|----------------------------------------------| +| yes_harness::f_manually_implements_arbitrary | +|----------------------------------------------| +| yes_harness::f_phantom_data | +|----------------------------------------------| +| yes_harness::f_phantom_pinned | +|----------------------------------------------| +| yes_harness::empty_body | ++----------------------------------------------+ + +Kani did not generate automatic harnesses for 8 function(s). +If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832 ++----------------------------------------+------------------------------------------------------------------------------+ +| Skipped Function | Reason for Skipping | ++=======================================================================================================================+ +| no_harness::doesnt_implement_arbitrary | Missing Arbitrary implementation for argument(s) x: DoesntImplementArbitrary | +|----------------------------------------+------------------------------------------------------------------------------| +| no_harness::unsupported_const_pointer | Missing Arbitrary implementation for argument(s) _y: *const i32 | +|----------------------------------------+------------------------------------------------------------------------------| +| no_harness::unsupported_generic | Generic Function | +|----------------------------------------+------------------------------------------------------------------------------| +| no_harness::unsupported_mut_pointer | Missing Arbitrary implementation for argument(s) _y: *mut i32 | +|----------------------------------------+------------------------------------------------------------------------------| +| no_harness::unsupported_no_arg_name | Missing Arbitrary implementation for argument(s) _: &() | +|----------------------------------------+------------------------------------------------------------------------------| +| no_harness::unsupported_ref | Missing Arbitrary implementation for argument(s) _y: &i32 | +|----------------------------------------+------------------------------------------------------------------------------| +| no_harness::unsupported_slice | Missing Arbitrary implementation for argument(s) _y: &[u8] | +|----------------------------------------+------------------------------------------------------------------------------| +| no_harness::unsupported_vec | Missing Arbitrary implementation for argument(s) _y: std::vec::Vec | ++----------------------------------------+------------------------------------------------------------------------------+ + Autoharness: Checking function yes_harness::f_tuple against all possible inputs... Autoharness: Checking function yes_harness::f_maybe_uninit against all possible inputs... Autoharness: Checking function yes_harness::f_result against all possible inputs... @@ -133,25 +244,3 @@ Autoharness Summary: | yes_harness::f_u8 | #[kani::proof] | Success | +----------------------------------------------+---------------------------+---------------------+ Complete - 42 successfully verified functions, 0 failures, 42 total. - -Kani did not generate automatic harnesses for the following functions. -If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832 -+----------------------------------------+------------------------------------------------------+ -| Skipped Function | Reason for Skipping | -+===============================================================================================+ -| no_harness::doesnt_implement_arbitrary | Missing Arbitrary implementation for argument(s): x | -|----------------------------------------+------------------------------------------------------| -| no_harness::unsupported_const_pointer | Missing Arbitrary implementation for argument(s): _y | -|----------------------------------------+------------------------------------------------------| -| no_harness::unsupported_generic | Generic Function | -|----------------------------------------+------------------------------------------------------| -| no_harness::unsupported_mut_pointer | Missing Arbitrary implementation for argument(s): _y | -|----------------------------------------+------------------------------------------------------| -| no_harness::unsupported_no_arg_name | Missing Arbitrary implementation for argument(s): _ | -|----------------------------------------+------------------------------------------------------| -| no_harness::unsupported_ref | Missing Arbitrary implementation for argument(s): _y | -|----------------------------------------+------------------------------------------------------| -| no_harness::unsupported_slice | Missing Arbitrary implementation for argument(s): _y | -|----------------------------------------+------------------------------------------------------| -| no_harness::unsupported_vec | Missing Arbitrary implementation for argument(s): _y | -+----------------------------------------+------------------------------------------------------+ diff --git a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected index 8d1e15f4d92e..d1727ed86d03 100644 --- a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected +++ b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected @@ -1,3 +1,20 @@ +Kani generated automatic harnesses for 5 function(s): ++-------------------------+ +| Chosen Function | ++=========================+ +| oob_safe_array_access | +|-------------------------| +| oob_unsafe_array_access | +|-------------------------| +| integer_overflow | +|-------------------------| +| panic | +|-------------------------| +| unchecked_mul | ++-------------------------+ + +Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). + Autoharness: Checking function panic against all possible inputs... panic.assertion\ - Status: FAILURE\ @@ -72,5 +89,3 @@ Autoharness Summary: Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). Complete - 0 successfully verified functions, 5 failures, 5 total. - -Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). diff --git a/tests/script-based-pre/cargo_autoharness_include/include.expected b/tests/script-based-pre/cargo_autoharness_include/include.expected index 4c0192e92448..2f5e75ef5a3c 100644 --- a/tests/script-based-pre/cargo_autoharness_include/include.expected +++ b/tests/script-based-pre/cargo_autoharness_include/include.expected @@ -1,3 +1,20 @@ +Kani generated automatic harnesses for 1 function(s): ++-----------------+ +| Chosen Function | ++=================+ +| include::simple | ++-----------------+ + +Kani did not generate automatic harnesses for 2 function(s). +If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832 ++------------------+--------------------------------+ +| Skipped Function | Reason for Skipping | ++===================================================+ +| excluded::simple | Did not match provided filters | +|------------------+--------------------------------| +| include::generic | Generic Function | ++------------------+--------------------------------+ + Autoharness: Checking function include::simple against all possible inputs... VERIFICATION:- SUCCESSFUL @@ -11,13 +28,3 @@ Autoharness Summary: | include::simple | #[kani::proof] | Success | +-------------------+---------------------------+---------------------+ Complete - 1 successfully verified functions, 0 failures, 1 total. - -Kani did not generate automatic harnesses for the following functions. -If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832 -+------------------+--------------------------------+ -| Skipped Function | Reason for Skipping | -+===================================================+ -| excluded::simple | Did not match provided filters | -|------------------+--------------------------------| -| include::generic | Generic Function | -+------------------+--------------------------------+ diff --git a/tests/script-based-pre/cargo_autoharness_termination/termination.expected b/tests/script-based-pre/cargo_autoharness_termination/termination.expected index 0b36a4dbe67b..62aaf328f3d5 100644 --- a/tests/script-based-pre/cargo_autoharness_termination/termination.expected +++ b/tests/script-based-pre/cargo_autoharness_termination/termination.expected @@ -1,3 +1,16 @@ +Kani generated automatic harnesses for 3 function(s): ++--------------------------------+ +| Chosen Function | ++================================+ +| unwind_bound::infinite_loop | +|--------------------------------| +| unwind_bound::gcd_recursion | +|--------------------------------| +| timeout::check_harness_timeout | ++--------------------------------+ + +Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). + Autoharness: Checking function timeout::check_harness_timeout against all possible inputs... CBMC failed VERIFICATION:- FAILED @@ -38,5 +51,3 @@ Autoharness Summary: Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). Complete - 0 successfully verified functions, 3 failures, 3 total. - -Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). From 319040b8cd2cb72ec0603653fad7a8d934857d57 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Thu, 20 Mar 2025 12:01:47 -0400 Subject: [PATCH 061/161] Autoharness: `--list` option (#3952) Add `--list` option to the autoharness subcommand, which invokes the driver logic from the list subcommand to list automatic and manual harness metadata together. Towards #3832 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../src/kani_middle/codegen_units.rs | 7 +- kani-compiler/src/kani_middle/metadata.rs | 32 +++++++-- kani-driver/src/args/autoharness_args.rs | 44 +++++++++++++ kani-driver/src/args/list_args.rs | 14 ++++ kani-driver/src/autoharness/mod.rs | 61 +++++++++++------ kani-driver/src/list/collect_metadata.rs | 2 +- kani-driver/src/list/mod.rs | 4 +- .../cargo_autoharness_list/Cargo.toml | 10 +++ .../cargo_autoharness_list/config.yml | 4 ++ .../cargo_autoharness_list/list.expected | 26 ++++++++ .../cargo_autoharness_list/list.sh | 5 ++ .../cargo_autoharness_list/src/lib.rs | 65 +++++++++++++++++++ 12 files changed, 242 insertions(+), 32 deletions(-) create mode 100644 tests/script-based-pre/cargo_autoharness_list/Cargo.toml create mode 100644 tests/script-based-pre/cargo_autoharness_list/config.yml create mode 100644 tests/script-based-pre/cargo_autoharness_list/list.expected create mode 100755 tests/script-based-pre/cargo_autoharness_list/list.sh create mode 100644 tests/script-based-pre/cargo_autoharness_list/src/lib.rs diff --git a/kani-compiler/src/kani_middle/codegen_units.rs b/kani-compiler/src/kani_middle/codegen_units.rs index cf8b33232f40..e60455ed8bc2 100644 --- a/kani-compiler/src/kani_middle/codegen_units.rs +++ b/kani-compiler/src/kani_middle/codegen_units.rs @@ -36,7 +36,7 @@ use std::sync::OnceLock; use tracing::debug; /// An identifier for the harness function. -type Harness = Instance; +pub type Harness = Instance; /// A set of stubs. pub type Stubs = HashMap; @@ -92,8 +92,7 @@ impl CodegenUnits { args, *kani_fns.get(&KaniModel::Any.into()).unwrap(), ); - let chosen_fn_names = - chosen.iter().map(|func| func.name().clone()).collect::>(); + let chosen_fn_names = chosen.iter().map(|func| func.name()).collect::>(); AUTOHARNESS_MD .set(AutoHarnessMetadata { chosen: chosen_fn_names, skipped }) .expect("Initializing the autoharness metdata failed"); @@ -167,7 +166,7 @@ impl CodegenUnits { proof_harnesses, unsupported_features: vec![], test_harnesses, - contracted_functions: gen_contracts_metadata(tcx), + contracted_functions: gen_contracts_metadata(tcx, &self.harness_info), autoharness_md: AUTOHARNESS_MD.get().cloned(), } } diff --git a/kani-compiler/src/kani_middle/metadata.rs b/kani-compiler/src/kani_middle/metadata.rs index b24b39578df5..50487ce45714 100644 --- a/kani-compiler/src/kani_middle/metadata.rs +++ b/kani-compiler/src/kani_middle/metadata.rs @@ -7,6 +7,7 @@ use std::collections::HashMap; use std::path::Path; use crate::kani_middle::attributes::{KaniAttributes, test_harness_name}; +use crate::kani_middle::codegen_units::Harness; use crate::kani_middle::{SourceLocation, stable_fn_def}; use kani_metadata::ContractedFunction; use kani_metadata::{ArtifactType, HarnessAttributes, HarnessKind, HarnessMetadata}; @@ -47,7 +48,10 @@ pub fn gen_proof_metadata(tcx: TyCtxt, instance: Instance, base_name: &Path) -> /// /// For each function with contracts (or that is a target of a contract harness), /// construct a `ContractedFunction` object for it. -pub fn gen_contracts_metadata(tcx: TyCtxt) -> Vec { +pub fn gen_contracts_metadata( + tcx: TyCtxt, + harness_info: &HashMap, +) -> Vec { // We work with `stable_mir::CrateItem` instead of `stable_mir::Instance` to include generic items let crate_items: CrateItems = stable_mir::all_local_items(); @@ -61,8 +65,8 @@ pub fn gen_contracts_metadata(tcx: TyCtxt) -> Vec { if attributes.has_contract() { fn_to_data .insert(item.def_id(), ContractedFunction { function, file, harnesses: vec![] }); - } else if let Some((target_name, internal_def_id, _)) = - attributes.interpret_for_contract_attribute() + // This logic finds manual contract harnesses only (automatic harnesses are a Kani intrinsic, not crate items annotated with the proof_for_contract attribute). + } else if let Some((_, internal_def_id, _)) = attributes.interpret_for_contract_attribute() { let target_def_id = stable_fn_def(tcx, internal_def_id) .expect("The target of a proof for contract should be a function definition") @@ -73,7 +77,9 @@ pub fn gen_contracts_metadata(tcx: TyCtxt) -> Vec { fn_to_data.insert( target_def_id, ContractedFunction { - function: target_name.to_string(), + // Note that we use the item's fully qualified-name, rather than the target name specified in the attribute. + // This is necessary for the automatic contract harness lookup, see below. + function: item.name(), file, harnesses: vec![function], }, @@ -82,6 +88,24 @@ pub fn gen_contracts_metadata(tcx: TyCtxt) -> Vec { } } + // Find automatically generated contract harnesses (if the `autoharness` subcommand is running) + for (harness, metadata) in harness_info { + if !metadata.is_automatically_generated { + continue; + } + if let HarnessKind::ProofForContract { target_fn } = &metadata.attributes.kind { + // FIXME: This is a bit hacky. We can't resolve the target_fn to a DefId because we need somewhere to start the name resolution from. + // For a manual harness, we could just start from the harness, but since automatic harnesses are Kani intrinsics, we can't resolve the target starting from them. + // Instead, we rely on the fact that the ContractedFunction objects store the function's fully qualified name, + // and that `gen_automatic_proof_metadata` uses the fully qualified name as well. + // Once we implement multiple automatic harnesses for a single function, we will have to revise the HarnessMetadata anyway, + // and then we can revisit the idea of storing the target_fn's DefId somewhere. + let (_, target_cf) = + fn_to_data.iter_mut().find(|(_, cf)| &cf.function == target_fn).unwrap(); + target_cf.harnesses.push(harness.name()); + } + } + fn_to_data.into_values().collect() } diff --git a/kani-driver/src/args/autoharness_args.rs b/kani-driver/src/args/autoharness_args.rs index bdba0e340d8d..3dc8f6973703 100644 --- a/kani-driver/src/args/autoharness_args.rs +++ b/kani-driver/src/args/autoharness_args.rs @@ -4,6 +4,7 @@ use std::path::PathBuf; +use crate::args::list_args::Format; use crate::args::{ValidateArgs, VerificationArgs, validate_std_path}; use clap::{Error, Parser, error::ErrorKind}; use kani_metadata::UnstableFeature; @@ -29,6 +30,13 @@ pub struct CommonAutoharnessArgs { pub exclude_function: Vec, // TODO: It would be nice if we could borrow --exact here from VerificationArgs to differentiate between partial/exact matches, // like --harnesses does. Sharing arguments with VerificationArgs doesn't work with our current structure, though. + /// Run the `list` subcommand after generating the automatic harnesses. Requires -Z list. Note that this option implies --only-codegen. + #[arg(long)] + pub list: bool, + + /// The format of the `list` output. Requires --list. + #[arg(long, default_value = "pretty", requires = "list")] + pub format: Format, } /// Automatically verify functions in a crate. @@ -76,6 +84,24 @@ impl ValidateArgs for CargoAutoharnessArgs { )); } + if self.common_autoharness_args.list + && !self.verify_opts.common_args.unstable_features.contains(UnstableFeature::List) + { + return Err(Error::raw( + ErrorKind::MissingRequiredArgument, + format!("The `list` feature is unstable and requires -Z {}", UnstableFeature::List), + )); + } + + if self.common_autoharness_args.format == Format::Pretty + && self.verify_opts.common_args.quiet + { + return Err(Error::raw( + ErrorKind::ArgumentConflict, + "The `--quiet` flag is not compatible with the `pretty` format, since `pretty` prints to the terminal. Either specify a different format or don't pass `--quiet`.", + )); + } + if self .verify_opts .common_args @@ -105,6 +131,24 @@ impl ValidateArgs for StandaloneAutoharnessArgs { )); } + if self.common_autoharness_args.list + && !self.verify_opts.common_args.unstable_features.contains(UnstableFeature::List) + { + return Err(Error::raw( + ErrorKind::MissingRequiredArgument, + format!("The `list` feature is unstable and requires -Z {}", UnstableFeature::List), + )); + } + + if self.common_autoharness_args.format == Format::Pretty + && self.verify_opts.common_args.quiet + { + return Err(Error::raw( + ErrorKind::ArgumentConflict, + "The `--quiet` flag is not compatible with the `pretty` format, since `pretty` prints to the terminal. Either specify a different format or don't pass `--quiet`.", + )); + } + if self.std { validate_std_path(&self.input)?; } else if !self.input.is_file() { diff --git a/kani-driver/src/args/list_args.rs b/kani-driver/src/args/list_args.rs index 35d26f22eb36..1d84f5706812 100644 --- a/kani-driver/src/args/list_args.rs +++ b/kani-driver/src/args/list_args.rs @@ -64,6 +64,13 @@ impl ValidateArgs for CargoListArgs { )); } + if self.format == Format::Pretty && self.common_args.quiet { + return Err(Error::raw( + ErrorKind::ArgumentConflict, + "The `--quiet` flag is not compatible with the `pretty` format, since `pretty` prints to the terminal. Either specify a different format or don't pass `--quiet`.", + )); + } + Ok(()) } } @@ -78,6 +85,13 @@ impl ValidateArgs for StandaloneListArgs { )); } + if self.format == Format::Pretty && self.common_args.quiet { + return Err(Error::raw( + ErrorKind::ArgumentConflict, + "The `--quiet` flag is not compatible with the `pretty` format, since `pretty` prints to the terminal. Either specify a different format or don't pass `--quiet`.", + )); + } + if self.std { validate_std_path(&self.input) } else if self.input.is_file() { diff --git a/kani-driver/src/autoharness/mod.rs b/kani-driver/src/autoharness/mod.rs index 251a35570720..83162621dd1b 100644 --- a/kani-driver/src/autoharness/mod.rs +++ b/kani-driver/src/autoharness/mod.rs @@ -4,11 +4,15 @@ use std::str::FromStr; use crate::args::Timeout; -use crate::args::autoharness_args::{CargoAutoharnessArgs, StandaloneAutoharnessArgs}; +use crate::args::autoharness_args::{ + CargoAutoharnessArgs, CommonAutoharnessArgs, StandaloneAutoharnessArgs, +}; use crate::call_cbmc::VerificationStatus; use crate::call_single_file::to_rustc_arg; use crate::harness_runner::HarnessResult; -use crate::project::{standalone_project, std_project}; +use crate::list::collect_metadata::process_metadata; +use crate::list::output::output_list_results; +use crate::project::{Project, standalone_project, std_project}; use crate::session::KaniSession; use crate::{InvocationType, print_kani_version, project, verify_project}; use anyhow::Result; @@ -20,30 +24,18 @@ const LOOP_UNWIND_DEFAULT: u32 = 20; pub fn autoharness_cargo(args: CargoAutoharnessArgs) -> Result<()> { let mut session = KaniSession::new(args.verify_opts)?; - session.enable_autoharness(); - session.add_default_bounds(); - session.add_auto_harness_args( - args.common_autoharness_args.include_function, - args.common_autoharness_args.exclude_function, - ); + setup_session(&mut session, &args.common_autoharness_args); + if !session.args.common_args.quiet { print_kani_version(InvocationType::CargoKani(vec![])); } let project = project::cargo_project(&mut session, false)?; - if !session.args.common_args.quiet { - print_metadata(project.metadata.clone()); - } - if session.args.only_codegen { Ok(()) } else { verify_project(project, session) } + postprocess_project(project, session, args.common_autoharness_args) } pub fn autoharness_standalone(args: StandaloneAutoharnessArgs) -> Result<()> { let mut session = KaniSession::new(args.verify_opts)?; - session.enable_autoharness(); - session.add_default_bounds(); - session.add_auto_harness_args( - args.common_autoharness_args.include_function, - args.common_autoharness_args.exclude_function, - ); + setup_session(&mut session, &args.common_autoharness_args); if !session.args.common_args.quiet { print_kani_version(InvocationType::Standalone); @@ -55,14 +47,41 @@ pub fn autoharness_standalone(args: StandaloneAutoharnessArgs) -> Result<()> { standalone_project(&args.input, args.crate_name, &session)? }; + postprocess_project(project, session, args.common_autoharness_args) +} + +/// Execute autoharness-specific KaniSession configuration. +fn setup_session(session: &mut KaniSession, common_autoharness_args: &CommonAutoharnessArgs) { + session.enable_autoharness(); + session.add_default_bounds(); + session.add_auto_harness_args( + &common_autoharness_args.include_function, + &common_autoharness_args.exclude_function, + ); +} + +/// After generating the automatic harnesses, postprocess metadata and run verification. +fn postprocess_project( + project: Project, + session: KaniSession, + common_autoharness_args: CommonAutoharnessArgs, +) -> Result<()> { if !session.args.common_args.quiet { - print_metadata(project.metadata.clone()); + print_autoharness_metadata(project.metadata.clone()); + } + if common_autoharness_args.list { + let list_metadata = process_metadata(project.metadata.clone()); + return output_list_results( + list_metadata, + common_autoharness_args.format, + session.args.common_args.quiet, + ); } if session.args.only_codegen { Ok(()) } else { verify_project(project, session) } } /// Print automatic harness metadata to the terminal. -fn print_metadata(metadata: Vec) { +fn print_autoharness_metadata(metadata: Vec) { let mut chosen_table = PrettyTable::new(); chosen_table.set_header(vec!["Chosen Function"]); @@ -135,7 +154,7 @@ impl KaniSession { } /// Add the compiler arguments specific to the `autoharness` subcommand. - pub fn add_auto_harness_args(&mut self, included: Vec, excluded: Vec) { + pub fn add_auto_harness_args(&mut self, included: &[String], excluded: &[String]) { for func in included { self.pkg_args .push(to_rustc_arg(vec![format!("--autoharness-include-function {}", func)])); diff --git a/kani-driver/src/list/collect_metadata.rs b/kani-driver/src/list/collect_metadata.rs index 588cab0b63af..5a5ed290dcad 100644 --- a/kani-driver/src/list/collect_metadata.rs +++ b/kani-driver/src/list/collect_metadata.rs @@ -20,7 +20,7 @@ use anyhow::Result; use kani_metadata::{ContractedFunction, HarnessKind, KaniMetadata}; /// Process the KaniMetadata output from kani-compiler and output the list subcommand results -fn process_metadata(metadata: Vec) -> ListMetadata { +pub fn process_metadata(metadata: Vec) -> ListMetadata { // We use ordered maps and sets so that the output is in lexicographic order (and consistent across invocations). // Map each file to a vector of its harnesses. diff --git a/kani-driver/src/list/mod.rs b/kani-driver/src/list/mod.rs index 982c71d0120f..0a5aa523ea6a 100644 --- a/kani-driver/src/list/mod.rs +++ b/kani-driver/src/list/mod.rs @@ -6,9 +6,9 @@ use kani_metadata::ContractedFunction; use std::collections::{BTreeMap, BTreeSet}; pub mod collect_metadata; -mod output; +pub mod output; -struct ListMetadata { +pub struct ListMetadata { // Files mapped to their #[kani::proof] harnesses standard_harnesses: BTreeMap>, // Total number of #[kani::proof] harnesses diff --git a/tests/script-based-pre/cargo_autoharness_list/Cargo.toml b/tests/script-based-pre/cargo_autoharness_list/Cargo.toml new file mode 100644 index 000000000000..6c32a94cc3a3 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_list/Cargo.toml @@ -0,0 +1,10 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +[package] +name = "cargo_autoharness_list" +version = "0.1.0" +edition = "2024" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tests/script-based-pre/cargo_autoharness_list/config.yml b/tests/script-based-pre/cargo_autoharness_list/config.yml new file mode 100644 index 000000000000..4eac6f79588c --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_list/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: list.sh +expected: list.expected diff --git a/tests/script-based-pre/cargo_autoharness_list/list.expected b/tests/script-based-pre/cargo_autoharness_list/list.expected new file mode 100644 index 000000000000..bd20978a480e --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_list/list.expected @@ -0,0 +1,26 @@ +Kani generated automatic harnesses for 3 function(s): ++---------------------------+ +| Chosen Function | ++===========================+ +| f_u8 | +|---------------------------| +| has_recursion_gcd | +|---------------------------| +| verify::has_recursion_gcd | ++---------------------------+ + +Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). + +Contracts: ++-------+---------------------------+-----------------------------------------------------------------------------+ +| | Function | Contract Harnesses (#[kani::proof_for_contract]) | ++=================================================================================================================+ +| | has_recursion_gcd | my_harness, my_harness_2, kani::internal::automatic_harness | +|-------+---------------------------+-----------------------------------------------------------------------------| +| | verify::has_recursion_gcd | verify::my_harness, verify::my_harness_2, kani::internal::automatic_harness | +|-------+---------------------------+-----------------------------------------------------------------------------| +| Total | 2 | 6 | ++-------+---------------------------+-----------------------------------------------------------------------------+ + +Standard Harnesses (#[kani::proof]): +1. f_u8 diff --git a/tests/script-based-pre/cargo_autoharness_list/list.sh b/tests/script-based-pre/cargo_autoharness_list/list.sh new file mode 100755 index 000000000000..7deb9a0667cc --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_list/list.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +cargo kani autoharness -Z autoharness --list -Z list -Z function-contracts diff --git a/tests/script-based-pre/cargo_autoharness_list/src/lib.rs b/tests/script-based-pre/cargo_autoharness_list/src/lib.rs new file mode 100644 index 000000000000..47901cab8739 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_list/src/lib.rs @@ -0,0 +1,65 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Test that `kani autoharness --list` finds all of the manual and automatic harnesses +// and correctly matches them to their target function. +// Note that the proof_for_contract attributes use different, but equivalent, paths to their target functions; +// this tests that we can group the harnesses under the same target function even when the attribute value strings differ. + +fn f_u8(x: u8) -> u8 { + x +} + +#[kani::requires(x != 0 && y != 0)] +#[kani::ensures(|result : &u8| *result != 0 && x % *result == 0 && y % *result == 0)] +#[kani::recursion] +fn has_recursion_gcd(x: u8, y: u8) -> u8 { + let mut max = x; + let mut min = y; + if min > max { + let val = max; + max = min; + min = val; + } + + let res = max % min; + if res == 0 { min } else { has_recursion_gcd(min, res) } +} + +#[kani::proof_for_contract(crate::has_recursion_gcd)] +fn my_harness() { + has_recursion_gcd(kani::any(), kani::any()); +} + +#[kani::proof_for_contract(has_recursion_gcd)] +fn my_harness_2() { + has_recursion_gcd(kani::any(), kani::any()); +} + +mod verify { + #[kani::requires(x != 0 && y != 0)] + #[kani::ensures(|result : &u8| *result != 0 && x % *result == 0 && y % *result == 0)] + #[kani::recursion] + fn has_recursion_gcd(x: u8, y: u8) -> u8 { + let mut max = x; + let mut min = y; + if min > max { + let val = max; + max = min; + min = val; + } + + let res = max % min; + if res == 0 { min } else { has_recursion_gcd(min, res) } + } + + #[kani::proof_for_contract(crate::verify::has_recursion_gcd)] + fn my_harness() { + has_recursion_gcd(kani::any(), kani::any()); + } + + #[kani::proof_for_contract(has_recursion_gcd)] + fn my_harness_2() { + has_recursion_gcd(kani::any(), kani::any()); + } +} From 2c972fbddc8eec997a93969e74db103e83fa2485 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Fri, 21 Mar 2025 20:23:35 -0400 Subject: [PATCH 062/161] Add support for anonymous nested statics (#3953) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/rust-lang/rust/pull/121644 added support for anonymous nested allocations to statics. This PR adds support for such statics to Kani. The idea is to treat an anonymous `GlobalAlloc::Static` the same as we would treat a `GlobalAlloc::Memory`, since an anonymous static is a nested memory allocation. To frame this change in terms of the tests: `pointer_to_const_alloc.rs` contains a test for the `GlobalAlloc::Memory` case, which we could already handle prior to this PR. The MIR looks like: ``` alloc3 (size: 4, align: 4) { 2a 00 00 00 │ *... } alloc1 (static: FOO, size: 16, align: 8) { ╾─────alloc3─────╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ } ``` meaning that `FOO` contains a pointer to the *immutable* allocation alloc3 (note the `alloc3`, imm standing for "immutable"). `anon_static.rs` tests the code introduced in this PR. The MIR from `example_1` looks almost identical: ``` alloc2 (static: FOO::{constant#0}, size: 4, align: 4) { 2a 00 00 00 │ *... } alloc1 (static: FOO, size: 16, align: 8) { ╾───────alloc2────────╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ } ``` Note, however, that `alloc2` is mutable, and is thus an anonymous nested static rather than a constant allocation. But we can just call `codegen_const_allocation` anyway, since it ends up checking if the allocation is indeed constant before declaring the global variable in the symbol table: https://github.com/model-checking/kani/blob/319040b8cd2cb72ec0603653fad7a8d934857d57/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs#L556 Resolves #3904 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../codegen_cprover_gotoc/codegen/operand.rs | 24 +++++++- kani-compiler/src/kani_middle/mod.rs | 11 +++- kani-compiler/src/kani_middle/reachability.rs | 59 ++++++++++++------- tests/kani/Static/anon_static.rs | 57 ++++++++++++++++++ tests/kani/Static/pointer_to_const_alloc.rs | 25 ++++++++ 5 files changed, 153 insertions(+), 23 deletions(-) create mode 100644 tests/kani/Static/anon_static.rs create mode 100644 tests/kani/Static/pointer_to_const_alloc.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs index 3c67741667c2..1a110d8dab24 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use crate::codegen_cprover_gotoc::GotocCtx; use crate::codegen_cprover_gotoc::utils::slice_fat_ptr; +use crate::kani_middle::is_anon_static; use crate::unwrap_or_return_codegen_unimplemented; use cbmc::goto_program::{DatatypeComponent, Expr, ExprValue, Location, Symbol, Type}; use rustc_middle::ty::Const as ConstInternal; @@ -372,7 +373,15 @@ impl<'tcx> GotocCtx<'tcx> { // We want to return the function pointer (not to be confused with function item) self.codegen_func_expr(instance, loc).address_of() } - GlobalAlloc::Static(def) => self.codegen_static_pointer(def), + GlobalAlloc::Static(def) => { + if is_anon_static(self.tcx, def.def_id()) { + let alloc = def.eval_initializer().unwrap(); + let name = format!("{}::{alloc_id:?}", self.full_crate_name()); + self.codegen_nested_static_allocation(&alloc, Some(name), loc) + } else { + self.codegen_static_pointer(def) + } + } GlobalAlloc::Memory(alloc) => { // Full (mangled) crate name added so that allocations from different // crates do not conflict. The name alone is insufficient because Rust @@ -490,6 +499,19 @@ impl<'tcx> GotocCtx<'tcx> { mem_place.address_of() } + /// Generate an expression that represents the address of a nested static allocation. + fn codegen_nested_static_allocation( + &mut self, + alloc: &Allocation, + name: Option, + loc: Location, + ) -> Expr { + // The memory behind this allocation isn't constant, but codegen_alloc_in_memory (which codegen_const_allocation calls) + // uses alloc's mutability field to set the const-ness of the allocation in CBMC's symbol table, + // so we can reuse the code and without worrying that the allocation is set as immutable. + self.codegen_const_allocation(alloc, name, loc) + } + /// Insert an allocation into the goto symbol table, and generate an init value. /// /// This function is ultimately responsible for creating new statically initialized global diff --git a/kani-compiler/src/kani_middle/mod.rs b/kani-compiler/src/kani_middle/mod.rs index 690ff03058bc..e6839487c046 100644 --- a/kani-compiler/src/kani_middle/mod.rs +++ b/kani-compiler/src/kani_middle/mod.rs @@ -9,10 +9,10 @@ use crate::kani_queries::QueryDb; use rustc_hir::{def::DefKind, def_id::DefId as InternalDefId, def_id::LOCAL_CRATE}; use rustc_middle::ty::TyCtxt; use rustc_smir::rustc_internal; -use stable_mir::CrateDef; use stable_mir::mir::mono::MonoItem; use stable_mir::ty::{FnDef, RigidTy, Span as SpanStable, Ty, TyKind}; use stable_mir::visitor::{Visitable, Visitor as TyVisitor}; +use stable_mir::{CrateDef, DefId}; use std::ops::ControlFlow; use self::attributes::KaniAttributes; @@ -146,6 +146,15 @@ impl SourceLocation { } } +/// Return whether `def_id` refers to a nested static allocation. +pub fn is_anon_static(tcx: TyCtxt, def_id: DefId) -> bool { + let int_def_id = rustc_internal::internal(tcx, def_id); + match tcx.def_kind(int_def_id) { + rustc_hir::def::DefKind::Static { nested, .. } => nested, + _ => false, + } +} + /// Try to convert an internal `DefId` to a `FnDef`. pub fn stable_fn_def(tcx: TyCtxt, def_id: InternalDefId) -> Option { if let TyKind::RigidTy(RigidTy::FnDef(def, _)) = diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index e8bff532f1e7..237fbf5f8b41 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -42,6 +42,7 @@ use std::{ use crate::kani_middle::coercion; use crate::kani_middle::coercion::CoercionBase; +use crate::kani_middle::is_anon_static; use crate::kani_middle::transform::BodyTransformation; /// Collect all reachable items starting from the given starting points. @@ -219,20 +220,25 @@ impl<'tcx, 'a> MonoItemsCollector<'tcx, 'a> { let _guard = debug_span!("visit_static", ?def).entered(); let mut next_items = vec![]; - // Collect drop function. - let static_ty = def.ty(); - let instance = Instance::resolve_drop_in_place(static_ty); - next_items - .push(CollectedItem { item: instance.into(), reason: CollectionReason::StaticDrop }); + // Collect drop function, unless it's an anonymous static. + if !is_anon_static(self.tcx, def.def_id()) { + let static_ty = def.ty(); + let instance = Instance::resolve_drop_in_place(static_ty); + next_items.push(CollectedItem { + item: instance.into(), + reason: CollectionReason::StaticDrop, + }); - // Collect initialization. - let alloc = def.eval_initializer().unwrap(); - for (_, prov) in alloc.provenance.ptrs { - next_items.extend( - collect_alloc_items(prov.0) - .into_iter() - .map(|item| CollectedItem { item, reason: CollectionReason::Static }), - ); + // Collect initialization. + let alloc = def.eval_initializer().unwrap(); + debug!(?alloc, "visit_static: initializer"); + for (_, prov) in alloc.provenance.ptrs { + next_items.extend( + collect_alloc_items(self.tcx, prov.0) + .into_iter() + .map(|item| CollectedItem { item, reason: CollectionReason::Static }), + ); + } } next_items @@ -331,7 +337,7 @@ impl MonoItemsFnCollector<'_, '_> { debug!(?alloc, "collect_allocation"); for (_, id) in &alloc.provenance.ptrs { self.collected.extend( - collect_alloc_items(id.0) + collect_alloc_items(self.tcx, id.0) .into_iter() .map(|item| CollectedItem { item, reason: CollectionReason::Static }), ) @@ -520,27 +526,38 @@ fn should_codegen_locally(instance: &Instance) -> bool { !instance.is_foreign_item() } -fn collect_alloc_items(alloc_id: AllocId) -> Vec { +fn collect_alloc_items(tcx: TyCtxt, alloc_id: AllocId) -> Vec { trace!(?alloc_id, "collect_alloc_items"); let mut items = vec![]; match GlobalAlloc::from(alloc_id) { GlobalAlloc::Static(def) => { - // This differ from rustc's collector since rustc does not include static from - // upstream crates. - let instance = Instance::try_from(CrateItem::from(def)).unwrap(); - should_codegen_locally(&instance).then(|| items.push(MonoItem::from(def))); + if is_anon_static(tcx, def.def_id()) { + let alloc = def.eval_initializer().unwrap(); + items.extend( + alloc + .provenance + .ptrs + .iter() + .flat_map(|(_, prov)| collect_alloc_items(tcx, prov.0)), + ); + } else { + // This differ from rustc's collector since rustc does not include static from + // upstream crates. + let instance = Instance::try_from(CrateItem::from(def)).unwrap(); + should_codegen_locally(&instance).then(|| items.push(MonoItem::from(def))); + } } GlobalAlloc::Function(instance) => { should_codegen_locally(&instance).then(|| items.push(MonoItem::from(instance))); } GlobalAlloc::Memory(alloc) => { items.extend( - alloc.provenance.ptrs.iter().flat_map(|(_, prov)| collect_alloc_items(prov.0)), + alloc.provenance.ptrs.iter().flat_map(|(_, prov)| collect_alloc_items(tcx, prov.0)), ); } vtable_alloc @ GlobalAlloc::VTable(..) => { let vtable_id = vtable_alloc.vtable_allocation().unwrap(); - items = collect_alloc_items(vtable_id); + items = collect_alloc_items(tcx, vtable_id); } }; items diff --git a/tests/kani/Static/anon_static.rs b/tests/kani/Static/anon_static.rs new file mode 100644 index 000000000000..915dd967e8ec --- /dev/null +++ b/tests/kani/Static/anon_static.rs @@ -0,0 +1,57 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Test that Kani can codegen statics that contain pointers to nested statics. +// See https://github.com/model-checking/kani/issues/3904 + +mod example_1 { + // FOO contains a pointer to the anonymous nested static alloc2. + // The MIR is: + // alloc1 (static: FOO, size: 8, align: 8) { + // ╾───────alloc2────────╼ │ ╾──────╼ + // } + + // alloc2 (static: FOO::{constant#0}, size: 4, align: 4) { + // 2a 00 00 00 │ *... + // } + static mut FOO: &mut u32 = &mut 42; + + #[kani::proof] + fn main() { + unsafe { + *FOO = 43; + } + } +} + +mod example_2 { + // FOO and BAR both point to the anonymous nested static alloc2. + // The MIR is: + // alloc3 (static: BAR, size: 8, align: 8) { + // ╾───────alloc2────────╼ │ ╾──────╼ + // } + + // alloc2 (static: FOO::{constant#0}, size: 4, align: 4) { + // 2a 00 00 00 │ *... + // } + + // alloc1 (static: FOO, size: 8, align: 8) { + // ╾───────alloc2────────╼ │ ╾──────╼ + // } + + static mut FOO: &mut i32 = &mut 12; + static mut BAR: *mut i32 = unsafe { FOO as *mut _ }; + + #[kani::proof] + fn main() { + unsafe { + // check that we see the same initial value from all aliases + assert_eq!(*FOO, 12); + assert_eq!(*BAR, 12); + *FOO = 13; + // check that we see the same mutated value from all aliases + assert_eq!(*FOO, 13); + assert_eq!(*BAR, 13); + } + } +} diff --git a/tests/kani/Static/pointer_to_const_alloc.rs b/tests/kani/Static/pointer_to_const_alloc.rs new file mode 100644 index 000000000000..d73a99e00139 --- /dev/null +++ b/tests/kani/Static/pointer_to_const_alloc.rs @@ -0,0 +1,25 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Test that Kani can codegen statics that contain pointers to constant (i.e., immutable) allocations. +// Test taken from https://github.com/rust-lang/rust/issues/79738#issuecomment-1946578159 + +// The MIR is: +// alloc4 (static: BAR, size: 16, align: 8) { +// ╾─────alloc3─────╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ +// } + +// alloc3 (size: 4, align: 4) { +// 2a 00 00 00 │ *... +// } + +// alloc1 (static: FOO, size: 16, align: 8) { +// ╾─────alloc3─────╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ +// } +pub static FOO: &[i32] = &[42]; +pub static BAR: &[i32] = &*FOO; + +#[kani::proof] +fn main() { + assert_eq!(FOO.as_ptr(), BAR.as_ptr()); +} From f348a204ae9e9d5e7ea50e665088d9a6258e4ee2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 11:48:21 +0100 Subject: [PATCH 063/161] Automatic cargo update to 2025-03-24 (#3954) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 78 +++++++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c20da8f17156..fad5eaa7b651 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.16" +version = "1.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" dependencies = [ "shlex", ] @@ -509,9 +509,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058" dependencies = [ "powerfmt", ] @@ -654,14 +654,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -790,9 +790,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e" +checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260" dependencies = [ "jiff-static", "log", @@ -803,9 +803,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" +checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c" dependencies = [ "proc-macro2", "quote", @@ -1273,7 +1273,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.23", + "zerocopy 0.8.24", ] [[package]] @@ -1352,6 +1352,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.8.5" @@ -1485,9 +1491,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" +checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" dependencies = [ "bitflags", "errno", @@ -1665,9 +1671,9 @@ checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "stacker" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9156ebd5870ef293bfb43f91c7a74528d363ec0d424afe24160ed5a4343d08a" +checksum = "601f9201feb9b09c00266478bf459952b9ef9a6b94edb2f21eba14ab681a60a9" dependencies = [ "cc", "cfg-if", @@ -1743,14 +1749,14 @@ checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "tempfile" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488960f40a3fd53d72c2a29a58722561dee8afdd175bd88e3db4677d7b2ba600" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ "fastrand", - "getrandom 0.3.1", + "getrandom 0.3.2", "once_cell", - "rustix 1.0.2", + "rustix 1.0.3", "windows-sys 0.59.0", ] @@ -1812,9 +1818,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.39" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -1829,15 +1835,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -2104,9 +2110,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -2253,9 +2259,9 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags", ] @@ -2271,11 +2277,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.23" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" dependencies = [ - "zerocopy-derive 0.8.23", + "zerocopy-derive 0.8.24", ] [[package]] @@ -2291,9 +2297,9 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.8.23" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" dependencies = [ "proc-macro2", "quote", From 7d7fca2ad57cb7e59eee6a3c3bc83713745222b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 15:03:46 -0400 Subject: [PATCH 064/161] Bump tests/perf/s2n-quic from `8670e83` to `324cf31` (#3955) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `8670e83` to `324cf31`.
Commits
  • 324cf31 feat(s2n-quic-dc): support key association in dispatch queue allocator (#2563)
  • 2cb213b test(s2n-quic-dc): wire up bach::net (#2560)
  • 8129a20 build(deps): update bach requirement from 0.0.10 to 0.0.11 (#2561)
  • 4a6b1f8 feat(s2n-quic-dc): wire up recv pool to server (#2556)
  • a9aa1c2 ci: Fixes parsing of MSRV (#2555)
  • d0c2366 fix(s2n-quic-dc): clamp send quantum to max syscall size (#2549)
  • 4b3bcef test(s2n-quic-dc): add request/response tests (#2546)
  • 7219bbc test(s2n-quic-dc): add debug assertions to descriptor free lists (#2539)
  • c591b3f fix(s2n-quic-dc): synchronize dispatch queue closure (#2553)
  • 74a4b97 build(deps): bump docker/login-action from 3.3.0 to 3.4.0 (#2551)
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 8670e83da211..324cf31b2a62 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 8670e83da211151a35ad72bc22b6e5eeb3331e13 +Subproject commit 324cf31b2a62662e0d50b06023426b7b8c9cd780 From c0b32866a892883f28f09b17627c5df8bd3a0503 Mon Sep 17 00:00:00 2001 From: Rajath Kotyal Date: Tue, 25 Mar 2025 14:37:52 -0700 Subject: [PATCH 065/161] Document behavior of checked_size_of_raw and is_inbounds (#3956) Resolves #3947 Explicitly document behavior of checked_size_of_raw and is_inbounds with respect to isize::MAX to clarify safety guarantees. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- library/kani_core/src/mem.rs | 13 +++++++++++- .../MemPredicates/ptr_size_validity.expected | 1 + .../MemPredicates/ptr_size_validity.rs | 21 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 tests/expected/MemPredicates/ptr_size_validity.expected create mode 100644 tests/expected/MemPredicates/ptr_size_validity.rs diff --git a/library/kani_core/src/mem.rs b/library/kani_core/src/mem.rs index 1ab8a5ead993..d431625a350b 100644 --- a/library/kani_core/src/mem.rs +++ b/library/kani_core/src/mem.rs @@ -129,7 +129,10 @@ macro_rules! kani_mem { /// Compute the size of the val pointed to if it is safe to do so. /// - /// Return `None` if an overflow would occur, or if alignment is not power of two. + /// Returns `None` if: + /// - An overflow occurs during the size computation. + /// - The pointer’s alignment is not a power of two. + /// - The computed size exceeds `isize::MAX` (the maximum safe Rust allocation size). /// TODO: Optimize this if T is sized. #[kanitool::fn_marker = "CheckedSizeOfIntrinsic"] pub fn checked_size_of_raw(ptr: *const T) -> Option { @@ -166,6 +169,14 @@ macro_rules! kani_mem { /// Checks that `ptr` points to an allocation that can hold data of size calculated from `T`. /// /// This will panic if `ptr` points to an invalid `non_null` + /// Returns `false` if: + /// - The computed size overflows. + /// - The computed size exceeds `isize::MAX`. + /// - The pointer is null (except for zero-sized types). + /// - The pointer references unallocated memory. + /// + /// This function aligns with Rust's memory safety requirements, which restrict valid allocations + /// to sizes no larger than `isize::MAX`. fn is_inbounds(ptr: *const T) -> bool { // If size overflows, then pointer cannot be inbounds. let Some(sz) = checked_size_of_raw(ptr) else { return false }; diff --git a/tests/expected/MemPredicates/ptr_size_validity.expected b/tests/expected/MemPredicates/ptr_size_validity.expected new file mode 100644 index 000000000000..aa71ac4c4795 --- /dev/null +++ b/tests/expected/MemPredicates/ptr_size_validity.expected @@ -0,0 +1 @@ +Complete - 1 successfully verified harnesses, 0 failures, 1 total. \ No newline at end of file diff --git a/tests/expected/MemPredicates/ptr_size_validity.rs b/tests/expected/MemPredicates/ptr_size_validity.rs new file mode 100644 index 000000000000..dabca4186b4f --- /dev/null +++ b/tests/expected/MemPredicates/ptr_size_validity.rs @@ -0,0 +1,21 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z mem-predicates +#![feature(ptr_metadata)] + +extern crate kani; + +mod size { + use super::*; + + #[kani::proof] + fn verify_checked_size_of_raw_exceeds_isize_max() { + let len_exceeding_isize_max = (isize::MAX as usize) + 1; + let data_ptr: *const [u8] = + core::ptr::from_raw_parts(core::ptr::null::(), len_exceeding_isize_max); + + let size = kani::mem::checked_size_of_raw(data_ptr); + + assert!(size.is_none()); + } +} From 49c4b6f128947af7fa3e5fc045d2bca456c6acc1 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Wed, 26 Mar 2025 00:58:56 -0700 Subject: [PATCH 066/161] Upgrade toolchain to 2025-03-18 (#3959) The main change to get the toolchain upgrade to 2025-03-18 to work is to make this change in `kani-regression.sh`: ```diff -RUSTFLAGS="-D warnings" cargo build --target-dir /tmp/kani_build_warnings +RUSTFLAGS="-D warnings" cargo build --target-dir /tmp/kani_build_warnings --no-default-features --features cprover ``` Explanation: 1. `cargo build` suppresses warnings from dependencies unless they're local dependencies (e.g. included with `path = ...`) (see https://github.com/rust-lang/cargo/issues/8546) 2. Since we integrate `charon` as a git submodule and add it as a dependency using `path = ...`, its warnings are not suppressed 3. The toolchain upgrade includes adding a `#[must_use]` attribute on an enum used in charon: https://github.com/rust-lang/rust/commit/2439623278. This results in a warning that causes the `RUSTFLAGS="-D warnings" cargo build` to fail: https://github.com/model-checking/kani/actions/runs/13914318184/job/38934470822#step:4:2983 The short-term solution in this PR is to exclude the Charon feature when building with `-D warnings`. The long-term solution is to fix the warning in upstream Charon (if it's not already fixed), and update our Charon pin to point to a commit that includes the fix. Updating the Charon pin requires us to upgrade our MIR to ULLBC module though, which will require a non-trivial amount of work since it's 2.5 months old. Resolves #3944 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- kani-compiler/src/kani_compiler.rs | 2 ++ rust-toolchain.toml | 2 +- scripts/kani-regression.sh | 7 ++++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/kani-compiler/src/kani_compiler.rs b/kani-compiler/src/kani_compiler.rs index e4bfba3113b0..8382c5421222 100644 --- a/kani-compiler/src/kani_compiler.rs +++ b/kani-compiler/src/kani_compiler.rs @@ -41,6 +41,7 @@ pub fn run(args: Vec) { } /// Configure the LLBC backend (Aeneas's IR). +#[allow(unused)] fn llbc_backend(_queries: Arc>) -> Box { #[cfg(feature = "llbc")] return Box::new(LlbcCodegenBackend::new(_queries)); @@ -49,6 +50,7 @@ fn llbc_backend(_queries: Arc>) -> Box { } /// Configure the cprover backend that generates goto-programs. +#[allow(unused)] fn cprover_backend(_queries: Arc>) -> Box { #[cfg(feature = "cprover")] return Box::new(GotocCodegenBackend::new(_queries)); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 8bd21a773249..8bfb24bdc697 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-03-17" +channel = "nightly-2025-03-18" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] diff --git a/scripts/kani-regression.sh b/scripts/kani-regression.sh index 1837c4cc8a82..6c3334a3e794 100755 --- a/scripts/kani-regression.sh +++ b/scripts/kani-regression.sh @@ -105,7 +105,12 @@ cargo clean --manifest-path "$FEATURES_MANIFEST_PATH" # Setting RUSTFLAGS like this always resets cargo's build cache resulting in # all tests to be re-run. I.e., cannot keep re-runing the regression from where # we stopped. -RUSTFLAGS="-D warnings" cargo build --target-dir /tmp/kani_build_warnings +# Only run with the `cprover` feature to avoid compiling the `charon` library +# which is not our code and may have warnings. The downside is that we wouldn't +# detect any warnings in the charon code path. TODO: Remove +# `--no-default-features --features cprover` when the warnings in charon are +# fixed and we advance the charon pin to that version +RUSTFLAGS="-D warnings" cargo build --target-dir /tmp/kani_build_warnings --no-default-features --features cprover echo echo "All Kani regression tests completed successfully." From 9f5275350e93becbcb53e122cb2f08b5ee41c97b Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Thu, 27 Mar 2025 00:12:06 -0700 Subject: [PATCH 067/161] Remove unstable-features from code formatting script (#3962) The code formatting script [scripts/kani-fmt.sh](https://github.com/model-checking/kani/blob/main/scripts/kani-fmt.sh) currently uses `--unstable-features` to enable the usage of the `--ignore` pattern in [rustfmt.toml](https://github.com/model-checking/kani/blob/main/rustfmt.toml). Since we're already using a find command to find all the files to format, it is easy to instead add the patterns to exclude to the find command rather than relying on an unstable feature. Resolves #ISSUE-NUMBER By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- rustfmt.toml | 10 ---------- scripts/kani-fmt.sh | 14 ++++++++++---- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/rustfmt.toml b/rustfmt.toml index 44cbfe4a3dc9..f8ff0d0fd105 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -6,13 +6,3 @@ edition = "2021" style_edition = "2024" use_small_heuristics = "Max" merge_derives = false - -ignore = [ - "**/build/", - "**/target/", - - # Do not format submodules - # For some reason, this is not working without the directory wildcard. - "**/firecracker", - "**/tests/perf/s2n-quic/", -] diff --git a/scripts/kani-fmt.sh b/scripts/kani-fmt.sh index 5eca1582cc40..6a136f244ff8 100755 --- a/scripts/kani-fmt.sh +++ b/scripts/kani-fmt.sh @@ -19,19 +19,25 @@ error=0 cargo fmt "$@" || error=1 # Check test source files. -# Note that this will respect the ignore section of rustfmt.toml. If you need to -# skip any file / directory, add it there. TESTS=("tests" "docs/src/tutorial") +# Add ignore patterns for code we don't want to format. +IGNORE=("*/perf/s2n-quic/*") + +# Arguments for the find command for excluding the IGNORE paths +IGNORE_ARGS=() +for ignore in "${IGNORE[@]}"; do + IGNORE_ARGS+=(-not -path "$ignore") +done for suite in "${TESTS[@]}"; do # Find uses breakline to split between files. This ensures that we can # handle files with space in their path. set -f; IFS=$'\n' - files=($(find "${suite}" -name "*.rs")) + files=($(find "${suite}" -name "*.rs" ${IGNORE_ARGS[@]})) set +f; unset IFS # Note: We set the configuration file here because some submodules have # their own configuration file. - rustfmt --unstable-features "$@" --config-path rustfmt.toml "${files[@]}" || error=1 + rustfmt --config-path rustfmt.toml "${files[@]}" || error=1 done exit $error From 641756b5d11c22edb785f86f5bf172d91ed67486 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Thu, 27 Mar 2025 18:59:49 +0100 Subject: [PATCH 068/161] Remove CI job to update features/verify-rust-std (#3963) We no longer use this branch as the verify-rust-std repo instead directly uses versions in `main`. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .github/workflows/verify-std-update.yml | 35 ------------------------- 1 file changed, 35 deletions(-) delete mode 100644 .github/workflows/verify-std-update.yml diff --git a/.github/workflows/verify-std-update.yml b/.github/workflows/verify-std-update.yml deleted file mode 100644 index 518f80ecd1b8..000000000000 --- a/.github/workflows/verify-std-update.yml +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright Kani Contributors -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# This workflow will try to update the verify std branch. - -name: Update "features/verify-rust-std" -on: - schedule: - - cron: "30 3 * * *" # Run this every day at 03:30 UTC - workflow_dispatch: # Allow manual dispatching. - -env: - RUST_BACKTRACE: 1 - -jobs: - # First ensure the HEAD is compatible with the `verify-rust-std` repository. - verify-std: - name: Verify Std - permissions: { } - uses: ./.github/workflows/verify-std-check.yml - - # Push changes to the features branch. - update-branch: - needs: verify-std - permissions: - contents: write - runs-on: ubuntu-latest - steps: - - name: Checkout Kani - uses: actions/checkout@v4 - - - name: Update feature branch - run: | - git push origin HEAD:features/verify-rust-std - From c8b15c6aeddb68a33f2438cc5936ac90796e2293 Mon Sep 17 00:00:00 2001 From: Rajath Kotyal Date: Thu, 27 Mar 2025 11:00:06 -0700 Subject: [PATCH 069/161] Make is_inbounds public (#3958) Resolves #3946 This PR makes `kani::mem::is_inbounds` publicly accessible, allowing direct validation of pointer sizes By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- library/kani_core/src/mem.rs | 7 ++++++- .../expected/MemPredicates/ptr_size_validity.expected | 2 +- tests/expected/MemPredicates/ptr_size_validity.rs | 10 ++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/library/kani_core/src/mem.rs b/library/kani_core/src/mem.rs index d431625a350b..a6c37ca22baa 100644 --- a/library/kani_core/src/mem.rs +++ b/library/kani_core/src/mem.rs @@ -177,7 +177,12 @@ macro_rules! kani_mem { /// /// This function aligns with Rust's memory safety requirements, which restrict valid allocations /// to sizes no larger than `isize::MAX`. - fn is_inbounds(ptr: *const T) -> bool { + #[crate::kani::unstable_feature( + feature = "mem-predicates", + issue = 3946, + reason = "experimental memory predicate API" + )] + pub fn is_inbounds(ptr: *const T) -> bool { // If size overflows, then pointer cannot be inbounds. let Some(sz) = checked_size_of_raw(ptr) else { return false }; if sz == 0 { diff --git a/tests/expected/MemPredicates/ptr_size_validity.expected b/tests/expected/MemPredicates/ptr_size_validity.expected index aa71ac4c4795..9eb718272967 100644 --- a/tests/expected/MemPredicates/ptr_size_validity.expected +++ b/tests/expected/MemPredicates/ptr_size_validity.expected @@ -1 +1 @@ -Complete - 1 successfully verified harnesses, 0 failures, 1 total. \ No newline at end of file +Complete - 2 successfully verified harnesses, 0 failures, 2 total. \ No newline at end of file diff --git a/tests/expected/MemPredicates/ptr_size_validity.rs b/tests/expected/MemPredicates/ptr_size_validity.rs index dabca4186b4f..11745b00fad3 100644 --- a/tests/expected/MemPredicates/ptr_size_validity.rs +++ b/tests/expected/MemPredicates/ptr_size_validity.rs @@ -8,6 +8,16 @@ extern crate kani; mod size { use super::*; + #[kani::proof] + fn verify_is_inbounds() { + let val = 42u8; + let ptr = &val as *const u8; + assert!(kani::mem::is_inbounds(ptr)); + + let null_ptr: *const u8 = core::ptr::null(); + assert!(!kani::mem::is_inbounds(null_ptr)); + } + #[kani::proof] fn verify_checked_size_of_raw_exceeds_isize_max() { let len_exceeding_isize_max = (isize::MAX as usize) + 1; From d12f10fd9cdfe32cd20a3d1047d2d25477e5c365 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Fri, 28 Mar 2025 02:19:25 -0700 Subject: [PATCH 070/161] Enable Kani to work with a stable toolchain (#3964) As discussed in #3960, applying changes needed for the stable branch in main to minimize the differences between the two branches. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Michael Tautschnig Co-authored-by: Carolyn Zech --- .cargo/config.toml | 2 ++ kani-compiler/src/main.rs | 3 +++ kani-driver/src/call_cargo.rs | 2 ++ kani-driver/src/call_single_file.rs | 2 ++ tests/cargo-ui/verbose-cmds/expected | 2 +- 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 9bb4e0808345..f0df939ef506 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -20,6 +20,8 @@ KANI_SYSROOT ={value = "target/kani", relative = true} KANI_BUILD_LIBS ={value = "target/build-libs", relative = true} # Build Kani library without `build-std`. KANI_LEGACY_LIBS ={value = "target/legacy-libs", relative = true} +# This is only required for stable but is a no-op for nightly channels +RUSTC_BOOTSTRAP = "1" [target.'cfg(all())'] rustflags = [ # Global lints/warnings. Need to use underscore instead of -. diff --git a/kani-compiler/src/main.rs b/kani-compiler/src/main.rs index c8a56d3ef062..6bf8e549af76 100644 --- a/kani-compiler/src/main.rs +++ b/kani-compiler/src/main.rs @@ -16,6 +16,9 @@ #![feature(f128)] #![feature(f16)] #![feature(non_exhaustive_omitted_patterns_lint)] +#![feature(cfg_version)] +// Once the `stable` branch is at 1.86 or later, remove this line, since float_next_up_down is stabilized +#![cfg_attr(not(version("1.86")), feature(float_next_up_down))] #![feature(try_blocks)] extern crate rustc_abi; extern crate rustc_ast; diff --git a/kani-driver/src/call_cargo.rs b/kani-driver/src/call_cargo.rs index 4c39b03183b6..9097713a0016 100644 --- a/kani-driver/src/call_cargo.rs +++ b/kani-driver/src/call_cargo.rs @@ -206,6 +206,8 @@ crate-type = ["lib"] // Use CARGO_ENCODED_RUSTFLAGS instead of RUSTFLAGS is preferred. See // https://doc.rust-lang.org/cargo/reference/environment-variables.html .env("CARGO_ENCODED_RUSTFLAGS", rustc_args.join(OsStr::new("\x1f"))) + // This is only required for stable but is a no-op for nightly channels + .env("RUSTC_BOOTSTRAP", "1") .env("CARGO_TERM_PROGRESS_WHEN", "never"); match self.run_build_target(cmd, verification_target.target()) { diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index 9a09ce6163f8..f21f83345783 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -84,6 +84,8 @@ impl KaniSession { let mut cmd = Command::new(&self.kani_compiler); let kani_compiler_args = to_rustc_arg(kani_args); cmd.arg(kani_compiler_args).args(rustc_args); + // This is only required for stable but is a no-op for nightly channels + cmd.env("RUSTC_BOOTSTRAP", "1"); if self.args.common_args.quiet { self.run_suppress(cmd)?; diff --git a/tests/cargo-ui/verbose-cmds/expected b/tests/cargo-ui/verbose-cmds/expected index f883ef972cb3..b47b60d51ff4 100644 --- a/tests/cargo-ui/verbose-cmds/expected +++ b/tests/cargo-ui/verbose-cmds/expected @@ -1,5 +1,5 @@ CARGO_ENCODED_RUSTFLAGS= -cargo +nightly +cargo + Running: `goto-cc Running: `goto-instrument Checking harness dummy_harness... From f284b341613541ade7b13b98467883554a832324 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 17:24:23 +0000 Subject: [PATCH 071/161] Automatic cargo update to 2025-03-31 (#3966) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fad5eaa7b651..548dcaf27c5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -276,9 +276,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.32" +version = "4.5.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff" dependencies = [ "clap_builder", "clap_derive", @@ -286,9 +286,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.32" +version = "4.5.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489" dependencies = [ "anstream", "anstyle", @@ -474,9 +474,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", @@ -484,9 +484,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", @@ -498,9 +498,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", @@ -980,9 +980,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "macros" @@ -1181,9 +1181,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.1" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "os_info" From 71b0e860afbffb468b35beaa91c050bfce501827 Mon Sep 17 00:00:00 2001 From: thanhnguyen-aws Date: Tue, 1 Apr 2025 00:59:16 -0700 Subject: [PATCH 072/161] Add support for struct field accessing in loop contracts (#3970) This PR adds support for struct field accessing in loop contracts. Previously, using struct field accessing in loop contracts may result in a panic: ``` internal error: entered unreachable code: The loop invariant support only reference of user variables. The provided invariants contain unsupported dereference. Please report github.com/model-checking/kani/issues/new?template=bug_report.md note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ``` Resolves #3700 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../kani_middle/transform/loop_contracts.rs | 16 +++++++----- .../loop-contract/struct_projection.expected | 14 ++++++++++ .../loop-contract/struct_projection.rs | 26 +++++++++++++++++++ 3 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 tests/expected/loop-contract/struct_projection.expected create mode 100644 tests/expected/loop-contract/struct_projection.rs diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index 4c564ac10505..323cff742ee7 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -16,8 +16,8 @@ use rustc_middle::ty::TyCtxt; use rustc_span::Symbol; use stable_mir::mir::mono::Instance; use stable_mir::mir::{ - AggregateKind, BasicBlock, BasicBlockIdx, Body, ConstOperand, Operand, Place, Rvalue, - Statement, StatementKind, Terminator, TerminatorKind, VarDebugInfoContents, + AggregateKind, BasicBlock, BasicBlockIdx, Body, ConstOperand, Operand, Rvalue, Statement, + StatementKind, Terminator, TerminatorKind, VarDebugInfoContents, }; use stable_mir::ty::{FnDef, MirConst, RigidTy, UintTy}; use std::collections::{HashMap, HashSet, VecDeque}; @@ -295,11 +295,11 @@ impl LoopContractPass { // Collect supported vars assigned in the block. // And check if all arguments of the closure is supported. - let mut supported_vars: Vec = Vec::new(); + let mut supported_vars: Vec = Vec::new(); // All user variables are support supported_vars.extend(new_body.var_debug_info().iter().filter_map(|info| { match &info.value { - VarDebugInfoContents::Place(debug_place) => Some(debug_place.clone()), + VarDebugInfoContents::Place(debug_place) => Some(debug_place.local), _ => None, } })); @@ -310,8 +310,12 @@ impl LoopContractPass { for stmt in &new_body.blocks()[bb_idx].statements { if let StatementKind::Assign(place, rvalue) = &stmt.kind { match rvalue { + Rvalue::Ref(_,_,rplace) => { + if supported_vars.contains(&rplace.local) { + supported_vars.push(place.local); + } } Rvalue::Aggregate(AggregateKind::Closure(..), closure_args) => { - if closure_args.iter().any(|arg| !matches!(arg, Operand::Copy(arg_place) | Operand::Move(arg_place) if supported_vars.contains(arg_place))) { + if closure_args.iter().any(|arg| !matches!(arg, Operand::Copy(arg_place) | Operand::Move(arg_place) if supported_vars.contains(&arg_place.local))) { unreachable!( "The loop invariant support only reference of user variables. The provided invariants contain unsupported dereference. \ Please report github.com/model-checking/kani/issues/new?template=bug_report.md" @@ -320,7 +324,7 @@ impl LoopContractPass { } _ => { if self.is_supported_argument_of_closure(rvalue, new_body) { - supported_vars.push(place.clone()); + supported_vars.push(place.local); } } } diff --git a/tests/expected/loop-contract/struct_projection.expected b/tests/expected/loop-contract/struct_projection.expected new file mode 100644 index 000000000000..e398784ef656 --- /dev/null +++ b/tests/expected/loop-contract/struct_projection.expected @@ -0,0 +1,14 @@ +struct_projection.loop_invariant_step.1\ + - Status: SUCCESS\ + - Description: "Check invariant after step for loop struct_projection.0" + +struct_projection.loop_invariant_step.2\ + - Status: SUCCESS\ + - Description: "Check invariant after step for loop struct_projection.0" + +struct_projection.assertion.3\ + - Status: SUCCESS\ + - Description: "assertion failed: s.b == 2" + + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/loop-contract/struct_projection.rs b/tests/expected/loop-contract/struct_projection.rs new file mode 100644 index 000000000000..70f8a6394528 --- /dev/null +++ b/tests/expected/loop-contract/struct_projection.rs @@ -0,0 +1,26 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: -Z loop-contracts + +//! add support for struct field projection for loop-contract + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + +struct mystruct { + a: i32, + b: i32, +} + +#[kani::proof] +fn struct_projection() { + let mut s = mystruct { a: 0, b: 2 }; + let mut i = 0; + #[kani::loop_invariant((i<=10) && (s.a == i) && (s.b == 2))] + while i < 10 { + s.a += 1; + i += 1; + } + assert!(s.b == 2) +} From 6b7616c0e36f5bb22913f674027cd3667e3a7fb2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 14:57:18 -0700 Subject: [PATCH 073/161] Bump tests/perf/s2n-quic from `324cf31` to `d0aff82` (#3968) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `324cf31` to `d0aff82`.
Commits
  • d0aff82 fix(s2n-quic-dc): convert stream segments to probes when available (#2575)
  • 6a81117 fix(s2n-quic-dc): avoid new handshake queue order on every refill (#2576)
  • 6fddb53 Rework handshakes to use a queue (#2565)
  • 1473a86 feat(s2n-quic-dc): add RPC API for streams (#2572)
  • 479e12d chore: bump MSRV to 1.75 (#2573)
  • 1f29d59 test(s2n-quic-dc): disable tracing by default for tests with large output (#2...
  • d31522c chore: release 1.56.0 (#2568)
  • 99f32e4 test(s2n-quic-dc): disable UDP stream fuzz sims for CI (#2570)
  • b1fcbb7 events: emit on_key_exchange_group event (#2566)
  • 60c7294 test(s2n-quic-dc): use bach for request/response tests (#2564)
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 324cf31b2a62..d0aff821d596 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 324cf31b2a62662e0d50b06023426b7b8c9cd780 +Subproject commit d0aff821d596f279c2fe3af1cfb946174196c257 From 4e81792f309dcc5bea380b2c42cc1938af142e71 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Tue, 1 Apr 2025 18:00:06 -0700 Subject: [PATCH 074/161] Clarify `is_inbounds` docs (#3974) See https://github.com/model-checking/kani/pull/3956#discussion_r2019734480 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- library/kani_core/src/mem.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/library/kani_core/src/mem.rs b/library/kani_core/src/mem.rs index a6c37ca22baa..91148fac5cc5 100644 --- a/library/kani_core/src/mem.rs +++ b/library/kani_core/src/mem.rs @@ -168,15 +168,13 @@ macro_rules! kani_mem { /// Checks that `ptr` points to an allocation that can hold data of size calculated from `T`. /// - /// This will panic if `ptr` points to an invalid `non_null` - /// Returns `false` if: - /// - The computed size overflows. - /// - The computed size exceeds `isize::MAX`. - /// - The pointer is null (except for zero-sized types). - /// - The pointer references unallocated memory. - /// - /// This function aligns with Rust's memory safety requirements, which restrict valid allocations - /// to sizes no larger than `isize::MAX`. + /// This function always returns `true` for ZSTs, since every pointer to a ZST is valid. + /// For non-ZSTs, this function will return `false` if `ptr` is null + /// or the size of the val pointed to exceeds `isize::MAX`. + /// Otherwise, it will return `true` if and only if `ptr` points to allocated memory + /// that can hold data of size calculated from `T`. + /// Note that Kani does not support reasoning about pointers to unallocated memory, + /// so if `ptr` does not point to allocated memory, verification will fail. #[crate::kani::unstable_feature( feature = "mem-predicates", issue = 3946, @@ -192,7 +190,7 @@ macro_rules! kani_mem { } else { // Note that this branch can't be tested in concrete execution as `is_read_ok` needs to be // stubbed. - // We first assert that the data_ptr + // We first assert that the data_ptr points to a valid allocation. let data_ptr = ptr as *const (); if !unsafe { is_allocated(data_ptr, 0) } { crate::kani::unsupported( From bfa2a98f80a300396f9593cce2b8ce43e77c354f Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Wed, 2 Apr 2025 10:36:44 -0700 Subject: [PATCH 075/161] Upgrade toolchain to 2025-04-01 (#3973) Culprit PRs: - https://github.com/rust-lang/rust/pull/138384 - https://github.com/rust-lang/rust/pull/127173 - https://github.com/rust-lang/rust/pull/138659, Resolves #3961, #3976 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../codegen/foreign_function.rs | 30 ++++++++++++------- .../codegen_cprover_gotoc/codegen/function.rs | 6 ++-- .../codegen/source_region.rs | 29 +++++------------- kani-compiler/src/kani_middle/resolve.rs | 6 ++-- rust-toolchain.toml | 2 +- 5 files changed, 35 insertions(+), 38 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/foreign_function.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/foreign_function.rs index 169f2022a6e7..45670332a22f 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/foreign_function.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/foreign_function.rs @@ -49,25 +49,35 @@ impl GotocCtx<'_> { /// handled later. pub fn codegen_foreign_fn(&mut self, instance: Instance) -> &Symbol { debug!(?instance, "codegen_foreign_function"); - let fn_name = instance.mangled_name().intern(); + let trimmed_fn_name = instance.trimmed_name().intern(); + let mangled_fn_name = instance.mangled_name().intern(); let loc = self.codegen_span_stable(instance.def.span()); - if self.symbol_table.contains(fn_name) { - // Symbol has been added (either a built-in CBMC function or a Rust allocation function). - self.symbol_table.lookup(fn_name).unwrap() - } else if RUST_ALLOC_FNS.contains(&fn_name) - || (self.is_cffi_enabled() && instance.fn_abi().unwrap().conv == CallConvention::C) - { + if self.symbol_table.contains(mangled_fn_name) { + // Symbol has been added (a built-in CBMC function) + self.symbol_table.lookup(mangled_fn_name).unwrap() + } else if self.symbol_table.contains(trimmed_fn_name) { + // Symbol has been added (a Rust allocation function) + self.symbol_table.lookup(trimmed_fn_name).unwrap() + } else if RUST_ALLOC_FNS.contains(&trimmed_fn_name) { // Add a Rust alloc lib function as is declared by core. + // We use the trimmed name to ensure that it matches the function names in kani_lib.c + self.ensure(trimmed_fn_name, |gcx, _| { + let typ = gcx.codegen_ffi_type(instance); + Symbol::function(trimmed_fn_name, typ, None, instance.name(), loc) + .with_is_extern(true) + }) + } else if self.is_cffi_enabled() && instance.fn_abi().unwrap().conv == CallConvention::C { // When C-FFI feature is enabled, we just trust the rust declaration. // TODO: Add proper casting and clashing definitions check. // https://github.com/model-checking/kani/issues/1350 // https://github.com/model-checking/kani/issues/2426 - self.ensure(fn_name, |gcx, _| { + self.ensure(mangled_fn_name, |gcx, _| { let typ = gcx.codegen_ffi_type(instance); - Symbol::function(fn_name, typ, None, instance.name(), loc).with_is_extern(true) + Symbol::function(mangled_fn_name, typ, None, instance.name(), loc) + .with_is_extern(true) }) } else { - let shim_name = format!("{fn_name}_ffi_shim"); + let shim_name = format!("{mangled_fn_name}_ffi_shim"); trace!(?shim_name, "codegen_foreign_function"); self.ensure(&shim_name, |gcx, _| { // Generate a shim with an unsupported C-FFI error message. diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs index 7def214fc5a8..dd6909483694 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs @@ -266,11 +266,11 @@ pub mod rustc_smir { for mapping in &cov_info.mappings { let Code { bcb } = mapping.kind else { unreachable!() }; let source_map = tcx.sess.source_map(); - let file = source_map.lookup_source_file(cov_info.body_span.lo()); + let file = source_map.lookup_source_file(mapping.span.lo()); if bcb == coverage { return Some(( - make_source_region(source_map, cov_info, &file, mapping.span).unwrap(), - rustc_internal::stable(cov_info.body_span).get_filename(), + make_source_region(source_map, &file, mapping.span).unwrap(), + rustc_internal::stable(mapping.span).get_filename(), )); } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/source_region.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/source_region.rs index aa327372321f..19cb2a7aeb01 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/source_region.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/source_region.rs @@ -5,7 +5,6 @@ //! the `Span` to `CoverageRegion` conversion defined in //! https://github.com/rust-lang/rust/tree/master/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/spans.rs -use rustc_middle::mir::coverage::FunctionCoverageInfo; use rustc_span::Span; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, SourceFile}; @@ -27,33 +26,22 @@ impl Debug for SourceRegion { } } -fn ensure_non_empty_span( - source_map: &SourceMap, - fn_cov_info: &FunctionCoverageInfo, - span: Span, -) -> Option { +fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option { if !span.is_empty() { return Some(span); } - let lo = span.lo(); - let hi = span.hi(); - // The span is empty, so try to expand it to cover an adjacent '{' or '}', - // but only within the bounds of the body span. - let try_next = hi < fn_cov_info.body_span.hi(); - let try_prev = fn_cov_info.body_span.lo() < lo; - if !(try_next || try_prev) { - return None; - } + + // The span is empty, so try to enlarge it to cover an adjacent '{' or '}'. source_map .span_to_source(span, |src, start, end| try { // Adjusting span endpoints by `BytePos(1)` is normally a bug, // but in this case we have specifically checked that the character // we're skipping over is one of two specific ASCII characters, so // adjusting by exactly 1 byte is correct. - if try_next && src.as_bytes()[end] == b'{' { - Some(span.with_hi(hi + BytePos(1))) - } else if try_prev && src.as_bytes()[start - 1] == b'}' { - Some(span.with_lo(lo - BytePos(1))) + if src.as_bytes().get(end).copied() == Some(b'{') { + Some(span.with_hi(span.hi() + BytePos(1))) + } else if start > 0 && src.as_bytes()[start - 1] == b'}' { + Some(span.with_lo(span.lo() - BytePos(1))) } else { None } @@ -104,11 +92,10 @@ fn check_source_region(source_region: SourceRegion) -> Option { /// better than an ICE or `llvm-cov` failure that the user might have no way to avoid. pub(crate) fn make_source_region( source_map: &SourceMap, - fn_cov_info: &FunctionCoverageInfo, file: &SourceFile, span: Span, ) -> Option { - let span = ensure_non_empty_span(source_map, fn_cov_info, span)?; + let span = ensure_non_empty_span(source_map, span)?; let lo = span.lo(); let hi = span.hi(); // Column numbers need to be in bytes, so we can't use the more convenient diff --git a/kani-compiler/src/kani_middle/resolve.rs b/kani-compiler/src/kani_middle/resolve.rs index accb4807f00e..ea51e80fbf81 100644 --- a/kani-compiler/src/kani_middle/resolve.rs +++ b/kani-compiler/src/kani_middle/resolve.rs @@ -432,10 +432,10 @@ fn resolve_relative(tcx: TyCtxt, current_module: LocalModDefId, name: &str) -> R let mut glob_imports = vec![]; let result = tcx.hir_module_free_items(current_module).find_map(|item_id| { let item = tcx.hir_item(item_id); - if item.ident.as_str() == name { + if item.kind.ident().is_some_and(|ident| ident.as_str() == name) { match item.kind { - ItemKind::Use(use_path, UseKind::Single) => use_path.res[0].opt_def_id(), - ItemKind::ExternCrate(orig_name) => resolve_external( + ItemKind::Use(use_path, UseKind::Single(_)) => use_path.res[0].opt_def_id(), + ItemKind::ExternCrate(orig_name, _) => resolve_external( tcx, orig_name.as_ref().map(|sym| sym.as_str()).unwrap_or(name), ), diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 8bfb24bdc697..61dc66fe9fc0 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-03-18" +channel = "nightly-2025-04-01" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From d5c144b354b5bdd757e1f329e764e6d0af29bf1f Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Wed, 2 Apr 2025 15:37:02 -0700 Subject: [PATCH 076/161] Remove remaining `--enable-unstable` mentions (#3978) #3859 deprecated `--enable-unstable`. This PR replaces the remaining references to `--enable-unstable` with `-Z unstable-options`. Towards #3068 Resolves #3908 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .github/workflows/verify-std-check.yml | 4 ++-- docs/src/reference/experimental/stubbing.md | 8 +++++--- docs/src/usage.md | 2 +- tests/script-based-pre/check-output/check-output.sh | 4 ++-- tests/ui/cbmc_checks/float-overflow/check_message.rs | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/verify-std-check.yml b/.github/workflows/verify-std-check.yml index 33e424065f56..79d79cca3492 100644 --- a/.github/workflows/verify-std-check.yml +++ b/.github/workflows/verify-std-check.yml @@ -59,7 +59,7 @@ jobs: continue-on-error: true run: | kani verify-std -Z unstable-options ./library --target-dir ${{ runner.temp }} -Z function-contracts \ - -Z mem-predicates -Z loop-contracts --enable-unstable --cbmc-args --object-bits 12 + -Z mem-predicates -Z loop-contracts --cbmc-args --object-bits 12 # If the head failed, check if it's a new failure. - name: Checkout BASE @@ -89,7 +89,7 @@ jobs: continue-on-error: true run: | kani verify-std -Z unstable-options ./library --target-dir ${{ runner.temp }} -Z function-contracts \ - -Z mem-predicates -Z loop-contracts --enable-unstable --cbmc-args --object-bits 12 + -Z mem-predicates -Z loop-contracts --cbmc-args --object-bits 12 - name: Compare PR results if: steps.check-head.outcome != 'success' && steps.check-head.outcome != steps.check-base.outcome diff --git a/docs/src/reference/experimental/stubbing.md b/docs/src/reference/experimental/stubbing.md index 0ffbba5f0b0b..0324ffc527aa 100644 --- a/docs/src/reference/experimental/stubbing.md +++ b/docs/src/reference/experimental/stubbing.md @@ -16,8 +16,7 @@ Although definitions for *mocking* (normally used in testing) and *stubbing* may ## Components -The stubbing feature can be enabled by using the `--enable-stubbing` option when calling Kani. -Since it's an unstable feature, it requires passing the `--enable-unstable` option in addition to `--enable-stubbing`. +The stubbing feature can be enabled by using the `-Z stubbing` option when calling Kani (the `-Z` indicates that it's an unstable feature). At present, the only component of the stubbing feature is [the `#[kani::stub(, )]` attribute](#the-kanistub-attribute), which allows you to specify the pair of functions/methods that must be stubbed in a harness. @@ -60,6 +59,8 @@ At present, Kani fails to verify this example due to [issue #1781](https://githu However, we can work around this limitation thanks to the stubbing feature: ```rust +use rand::random; + #[cfg(kani)] fn mock_random() -> T { kani::any() @@ -83,7 +84,8 @@ Note that this is a fair assumption to do: `rand::random` is expected to return Now, let's run it through Kani: ```bash -cargo kani --enable-unstable --enable-stubbing --harness encrypt_then_decrypt_is_identity +cargo add rand +cargo kani -Z stubbing --harness encrypt_then_decrypt_is_identity ``` The verification result is composed of a single check: the assertion corresponding to `assert_eq!(data, decrypted_data)`. diff --git a/docs/src/usage.md b/docs/src/usage.md index 60592aa56c9a..f0da9d532119 100644 --- a/docs/src/usage.md +++ b/docs/src/usage.md @@ -23,7 +23,7 @@ This works like `cargo test` except that it will analyze all proof harnesses ins Common to both `kani` and `cargo kani` are many command-line flags: - * `--concrete-playback=[print|inplace]`: _Experimental_, `--enable-unstable` feature that generates a Rust unit test case + * `--concrete-playback=[print|inplace]`: _Experimental_ feature that generates a Rust unit test case that plays back a failing proof harness using a concrete counterexample. If used with `print`, Kani will only print the unit test to stdout. If used with `inplace`, Kani will automatically add the unit test to the user's source code, next to the proof harness. For more detailed instructions, see the [concrete playback](./experimental/concrete-playback.md) section. diff --git a/tests/script-based-pre/check-output/check-output.sh b/tests/script-based-pre/check-output/check-output.sh index 5567f1a981a3..0bc2fae6ce24 100755 --- a/tests/script-based-pre/check-output/check-output.sh +++ b/tests/script-based-pre/check-output/check-output.sh @@ -31,7 +31,7 @@ cd $(dirname $0) echo "Running single-file check..." rm -rf *.c -kani --gen-c --enable-unstable singlefile.rs >& kani.log || \ +kani --gen-c -Z unstable-options singlefile.rs >& kani.log || \ { ret=$?; echo "== Failed to run Kani"; cat kani.log; rm kani.log; exit 1; } rm -f kani.log if ! [ -e singlefile_*main.c ] @@ -70,7 +70,7 @@ echo (cd multifile echo "Running multi-file check..." rm -rf build -cargo kani --target-dir build --gen-c --enable-unstable >& kani.log || \ +cargo kani --target-dir build --gen-c -Z unstable-options >& kani.log || \ { ret=$?; echo "== Failed to run Kani"; cat kani.log; rm kani.log; exit 1; } rm -f kani.log cd build/kani/${TARGET}/debug/deps/ diff --git a/tests/ui/cbmc_checks/float-overflow/check_message.rs b/tests/ui/cbmc_checks/float-overflow/check_message.rs index 229bc373a883..fdfb59aca760 100644 --- a/tests/ui/cbmc_checks/float-overflow/check_message.rs +++ b/tests/ui/cbmc_checks/float-overflow/check_message.rs @@ -1,7 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // -// kani-flags: --enable-unstable --cbmc-args --float-overflow-check +// kani-flags: -Z unstable-options --cbmc-args --float-overflow-check // Check we don't print temporary variables as part of CBMC messages. extern crate kani; From fa34b1c7df4cad1aba98ab419a8632a6d815af70 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Wed, 2 Apr 2025 17:44:16 -0700 Subject: [PATCH 077/161] Clean up unused dependencies (#3981) Remove crates that are not used from the dependency list. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- Cargo.lock | 89 ++---------------------------------- kani-compiler/Cargo.toml | 3 -- kani-driver/Cargo.toml | 3 -- tools/compiletest/Cargo.toml | 1 - 4 files changed, 4 insertions(+), 92 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 548dcaf27c5e..26ce0a3debc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,7 +27,7 @@ dependencies = [ "getrandom 0.2.15", "once_cell", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -330,15 +330,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "colour" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b536eebcabe54980476d120a182f7da2268fe02d22575cca99cee5fdda178280" -dependencies = [ - "winapi", -] - [[package]] name = "comfy-table" version = "7.1.4" @@ -357,7 +348,6 @@ dependencies = [ "getopts", "glob", "libc", - "regex", "serde", "serde_json", "serde_yaml", @@ -833,16 +823,13 @@ dependencies = [ "charon", "clap", "cprover_bindings", - "home", "itertools", "kani_metadata", "lazy_static", "num", "quote", - "regex", "serde", "serde_json", - "shell-words", "strum", "strum_macros", "syn", @@ -872,14 +859,11 @@ dependencies = [ "anyhow", "cargo_metadata", "clap", - "colour", "comfy-table", "console", - "glob", "kani_metadata", "once_cell", "pathdiff", - "rand", "rayon", "regex", "rustc-demangle", @@ -1267,15 +1251,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy 0.8.24", -] - [[package]] name = "predicates" version = "3.1.3" @@ -1358,36 +1333,6 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.15", -] - [[package]] name = "rayon" version = "1.10.0" @@ -1642,12 +1587,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shell-words" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" - [[package]] name = "shlex" version = "1.3.0" @@ -2027,9 +1966,9 @@ checksum = "c4013970217383f67b18aef68f6fb2e8d409bc5755227092d32efb0422ba24b8" [[package]] name = "tree-sitter-rust" -version = "0.23.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4d64d449ca63e683c562c7743946a646671ca23947b9c925c0cfbe65051a4af" +checksum = "ca8ccb3e3a3495c8a943f6c3fd24c3804c471fd7f4f16087623c7fa4c0068e8a" dependencies = [ "cc", "tree-sitter-language", @@ -2272,16 +2211,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy" -version = "0.8.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" -dependencies = [ - "zerocopy-derive 0.8.24", + "zerocopy-derive", ] [[package]] @@ -2294,14 +2224,3 @@ dependencies = [ "quote", "syn", ] - -[[package]] -name = "zerocopy-derive" -version = "0.8.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index f11932a7387e..119199b54911 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -12,19 +12,16 @@ publish = false cbmc = { path = "../cprover_bindings", package = "cprover_bindings", optional = true } charon = { path = "../charon/charon", optional = true, default-features = false } clap = { version = "4.4.11", features = ["derive", "cargo"] } -home = "0.5" itertools = "0.13" kani_metadata = { path = "../kani_metadata" } lazy_static = "1.4.0" num = { version = "0.4.0", optional = true } quote = "1.0.36" -regex = "1.7.0" serde = { version = "1", optional = true } serde_json = "1" strum = "0.26" strum_macros = "0.26" syn = { version = "2.0.72", features = ["parsing", "extra-traits"] } -shell-words = "1.0.0" tracing = {version = "0.1", features = ["max_level_trace", "release_max_level_debug"]} tracing-subscriber = {version = "0.3.8", features = ["env-filter", "json", "fmt"]} tracing-tree = "0.4.0" diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index e6efa9460afb..3079514c854a 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -21,8 +21,6 @@ to_markdown_table = "0.1.0" serde = { version = "1", features = ["derive"] } serde_json = { version = "1", features = ["preserve_order"] } clap = { version = "4.4.11", features = ["derive"] } -colour = "2.1.0" -glob = "0.3" toml = "0.8" regex = "1.6" rustc-demangle = "0.1.21" @@ -34,7 +32,6 @@ strum_macros = {version = "0.26"} tempfile = "3" tracing = {version = "0.1", features = ["max_level_trace", "release_max_level_debug"]} tracing-subscriber = {version = "0.3.8", features = ["env-filter", "json", "fmt"]} -rand = "0.8" which = "7" time = {version = "0.3.36", features = ["formatting"]} tokio = { version = "1.40.0", features = ["io-util", "process", "rt", "time"] } diff --git a/tools/compiletest/Cargo.toml b/tools/compiletest/Cargo.toml index 967bc1715525..fe0a6b10df4a 100644 --- a/tools/compiletest/Cargo.toml +++ b/tools/compiletest/Cargo.toml @@ -20,7 +20,6 @@ publish = false getopts = "0.2" tracing = "0.1" tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } -regex = "1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_yaml = "0.9" From a52b9e39a9c9c179df16cb788106fd5667146701 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 2 Apr 2025 20:51:25 -0700 Subject: [PATCH 078/161] Automatic toolchain upgrade to nightly-2025-04-02 (#3983) Update Rust toolchain from nightly-2025-04-01 to nightly-2025-04-02 without any other source changes. Co-authored-by: carolynzech <71352687+carolynzech@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 61dc66fe9fc0..2c9d4b842f75 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-04-01" +channel = "nightly-2025-04-02" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 6e9cb87b378789034a02ce7da6be31d2c99928d0 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Thu, 3 Apr 2025 12:04:13 -0700 Subject: [PATCH 079/161] Update dependencies per `cargo-outdated` (#3982) In preparation for the release of Kani v0.61.0, I ran `cargo-outdated` and updated the out-of-date dependencies. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- Cargo.lock | 65 +++++++++++++++++++++++++------------ cprover_bindings/Cargo.toml | 2 +- kani-compiler/Cargo.toml | 6 ++-- kani-driver/Cargo.toml | 4 +-- kani_metadata/Cargo.toml | 4 +-- tools/kani-cov/Cargo.toml | 4 +-- tools/scanner/Cargo.toml | 8 ++--- 7 files changed, 59 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26ce0a3debc4..5f047c1403ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -253,13 +253,13 @@ dependencies = [ "hashlink", "index_vec", "indoc", - "itertools", + "itertools 0.13.0", "lazy_static", "log", "macros", "nom", "nom-supreme", - "petgraph", + "petgraph 0.6.5", "rustc_version", "serde", "serde-map-to-array", @@ -523,7 +523,7 @@ checksum = "885f5274163b5b1720591c0c24b34350a0b05e4774351f9fb3d13c192d8c995b" dependencies = [ "convert_case", "darling", - "itertools", + "itertools 0.13.0", "proc-macro2", "quote", "syn", @@ -610,6 +610,12 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "fnv" version = "1.0.7" @@ -668,13 +674,12 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "graph-cycles" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6ad932c6dd3cfaf16b66754a42f87bbeefd591530c4b6a8334270a7df3e853" +checksum = "3092eb8c0a52f9146161afd672f10fbb970daec4fa79d17b26f79f10139cba9e" dependencies = [ "ahash", - "petgraph", - "thiserror 1.0.69", + "petgraph 0.7.1", ] [[package]] @@ -772,6 +777,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -823,7 +837,7 @@ dependencies = [ "charon", "clap", "cprover_bindings", - "itertools", + "itertools 0.14.0", "kani_metadata", "lazy_static", "num", @@ -1220,7 +1234,17 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ - "fixedbitset", + "fixedbitset 0.4.2", + "indexmap", +] + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset 0.5.7", "indexmap", ] @@ -1474,7 +1498,7 @@ version = "0.0.0" dependencies = [ "csv", "graph-cycles", - "petgraph", + "petgraph 0.7.1", "serde", "strum", "strum_macros", @@ -1636,9 +1660,9 @@ checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" [[package]] name = "string-interner" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3275464d7a9f2d4cac57c89c2ef96a8524dba2864c8d6f82e3980baf136f9b" +checksum = "23de088478b31c349c9ba67816fa55d9355232d63c3afea8bf513e31f0f1d2c0" dependencies = [ "hashbrown 0.15.2", "serde", @@ -1652,15 +1676,15 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.3" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" [[package]] name = "strum_macros" -version = "0.26.4" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" dependencies = [ "heck", "proc-macro2", @@ -1947,13 +1971,14 @@ dependencies = [ [[package]] name = "tree-sitter" -version = "0.24.7" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5387dffa7ffc7d2dae12b50c6f7aab8ff79d6210147c6613561fc3d474c6f75" +checksum = "b9ac5ea5e7f2f1700842ec071401010b9c59bf735295f6e9fa079c3dc035b167" dependencies = [ "cc", "regex", "regex-syntax 0.8.5", + "serde_json", "streaming-iterator", "tree-sitter-language", ] @@ -1966,9 +1991,9 @@ checksum = "c4013970217383f67b18aef68f6fb2e8d409bc5755227092d32efb0422ba24b8" [[package]] name = "tree-sitter-rust" -version = "0.23.3" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8ccb3e3a3495c8a943f6c3fd24c3804c471fd7f4f16087623c7fa4c0068e8a" +checksum = "4b9b18034c684a2420722be8b2a91c9c44f2546b631c039edf575ccba8c61be1" dependencies = [ "cc", "tree-sitter-language", diff --git a/cprover_bindings/Cargo.toml b/cprover_bindings/Cargo.toml index 8c8cf77fd411..9024dc3fb388 100644 --- a/cprover_bindings/Cargo.toml +++ b/cprover_bindings/Cargo.toml @@ -17,7 +17,7 @@ lazy_static = "1.4.0" num = "0.4.0" num-traits = "0.2" serde = {version = "1", features = ["derive"]} -string-interner = "0.18" +string-interner = "0.19" tracing = "0.1" linear-map = {version = "1.2", features = ["serde_impl"]} diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index 119199b54911..f1640a0027ba 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -12,15 +12,15 @@ publish = false cbmc = { path = "../cprover_bindings", package = "cprover_bindings", optional = true } charon = { path = "../charon/charon", optional = true, default-features = false } clap = { version = "4.4.11", features = ["derive", "cargo"] } -itertools = "0.13" +itertools = "0.14" kani_metadata = { path = "../kani_metadata" } lazy_static = "1.4.0" num = { version = "0.4.0", optional = true } quote = "1.0.36" serde = { version = "1", optional = true } serde_json = "1" -strum = "0.26" -strum_macros = "0.26" +strum = "0.27.1" +strum_macros = "0.27.1" syn = { version = "2.0.72", features = ["parsing", "extra-traits"] } tracing = {version = "0.1", features = ["max_level_trace", "release_max_level_debug"]} tracing-subscriber = {version = "0.3.8", features = ["env-filter", "json", "fmt"]} diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index 3079514c854a..e5ac4aec0c68 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -27,8 +27,8 @@ rustc-demangle = "0.1.21" pathdiff = "0.2.1" rayon = "1.5.3" comfy-table = "7.0.1" -strum = {version = "0.26"} -strum_macros = {version = "0.26"} +strum = {version = "0.27.1"} +strum_macros = {version = "0.27.1"} tempfile = "3" tracing = {version = "0.1", features = ["max_level_trace", "release_max_level_debug"]} tracing-subscriber = {version = "0.3.8", features = ["env-filter", "json", "fmt"]} diff --git a/kani_metadata/Cargo.toml b/kani_metadata/Cargo.toml index 038b9e235663..638b81f609d2 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -13,8 +13,8 @@ publish = false [dependencies] serde = {version = "1", features = ["derive"]} cbmc = { path = "../cprover_bindings", package = "cprover_bindings" } -strum = "0.26" -strum_macros = "0.26" +strum = "0.27.1" +strum_macros = "0.27.1" clap = { version = "4.4.11", features = ["derive"] } [lints] diff --git a/tools/kani-cov/Cargo.toml b/tools/kani-cov/Cargo.toml index a2764791dcf1..7fb484eb07be 100644 --- a/tools/kani-cov/Cargo.toml +++ b/tools/kani-cov/Cargo.toml @@ -17,5 +17,5 @@ console = "0.15.8" serde = "1.0.197" serde_derive = "1.0.197" serde_json = "1.0.115" -tree-sitter = "0.24" -tree-sitter-rust = "0.23" +tree-sitter = "0.25.3" +tree-sitter-rust = "0.24" diff --git a/tools/scanner/Cargo.toml b/tools/scanner/Cargo.toml index f27e9e06c72c..1ec22cba984b 100644 --- a/tools/scanner/Cargo.toml +++ b/tools/scanner/Cargo.toml @@ -13,10 +13,10 @@ publish = false [dependencies] csv = "1.3" serde = {version = "1", features = ["derive"]} -strum = "0.26" -strum_macros = "0.26" -petgraph = "0.6.5" -graph-cycles = "0.1.0" +strum = "0.27.1" +strum_macros = "0.27.1" +petgraph = "0.7.1" +graph-cycles = "0.2.0" [package.metadata.rust-analyzer] # This crate uses rustc crates. From 2cfe4dc0201f5b6f34787f86e91d25c628d0f438 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Thu, 3 Apr 2025 12:43:40 -0700 Subject: [PATCH 080/161] Fix `autoharness` termination test & print metadata in alphabetical order (#3971) https://github.com/model-checking/kani/pull/3968 [failed in the merge queue](https://github.com/model-checking/kani/actions/runs/14180398658/job/39724995535) because the `cargo_autoharness_termination` test assumed that the recursion test wouldn't time out, but it did. This doesn't happen often, since this test has worked up until now, but in the interest of having robust tests, this PR splits the termination test into two: - `autoharness_timeout`, which tests that the default timeout of 60s kicks in - `autoharness_unwind`, which tests that the default unwind bound of 20 kicks in The unwind test specifies a timeout of 5m to ensure that the unwind bound takes effect before the timeout. While I was fixing this, I also fixed some inconsistencies with the printed metadata: - We use "Chosen Function" in one table and "Selected Function" in another--changed it to "Selected Function" in both to be consistent - The functions aren't printed alphabetically in 2/3 tables--fixed so that they're always sorted By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../src/kani_middle/codegen_units.rs | 6 +- kani-driver/src/autoharness/mod.rs | 7 +- kani_metadata/src/lib.rs | 6 +- .../contracts.expected | 14 +- .../dependencies.expected | 10 +- .../exclude.expected | 10 +- .../cargo_autoharness_filter/filter.expected | 154 +++++++++--------- .../harnesses_fail.expected | 16 +- .../include.expected | 10 +- .../cargo_autoharness_list/list.expected | 2 +- .../cargo_autoharness_termination/src/lib.rs | 38 ----- .../termination.expected | 53 ------ .../Cargo.toml | 10 ++ .../config.yml | 4 + .../src/lib.rs | 14 ++ .../termination_timeout.expected | 24 +++ .../termination_timeout.sh} | 0 .../Cargo.toml | 2 +- .../config.yml | 4 +- .../src/lib.rs | 23 +++ .../termination_unwind.expected | 49 ++++++ .../termination_unwind.sh | 7 + 22 files changed, 253 insertions(+), 210 deletions(-) delete mode 100644 tests/script-based-pre/cargo_autoharness_termination/src/lib.rs delete mode 100644 tests/script-based-pre/cargo_autoharness_termination/termination.expected create mode 100644 tests/script-based-pre/cargo_autoharness_termination_timeout/Cargo.toml create mode 100644 tests/script-based-pre/cargo_autoharness_termination_timeout/config.yml create mode 100644 tests/script-based-pre/cargo_autoharness_termination_timeout/src/lib.rs create mode 100644 tests/script-based-pre/cargo_autoharness_termination_timeout/termination_timeout.expected rename tests/script-based-pre/{cargo_autoharness_termination/termination.sh => cargo_autoharness_termination_timeout/termination_timeout.sh} (100%) rename tests/script-based-pre/{cargo_autoharness_termination => cargo_autoharness_termination_unwind}/Cargo.toml (81%) rename tests/script-based-pre/{cargo_autoharness_termination => cargo_autoharness_termination_unwind}/config.yml (52%) create mode 100644 tests/script-based-pre/cargo_autoharness_termination_unwind/src/lib.rs create mode 100644 tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.expected create mode 100755 tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.sh diff --git a/kani-compiler/src/kani_middle/codegen_units.rs b/kani-compiler/src/kani_middle/codegen_units.rs index e60455ed8bc2..c3b6e45c15b9 100644 --- a/kani-compiler/src/kani_middle/codegen_units.rs +++ b/kani-compiler/src/kani_middle/codegen_units.rs @@ -92,9 +92,11 @@ impl CodegenUnits { args, *kani_fns.get(&KaniModel::Any.into()).unwrap(), ); - let chosen_fn_names = chosen.iter().map(|func| func.name()).collect::>(); AUTOHARNESS_MD - .set(AutoHarnessMetadata { chosen: chosen_fn_names, skipped }) + .set(AutoHarnessMetadata { + chosen: chosen.iter().map(|func| func.name()).collect::>(), + skipped, + }) .expect("Initializing the autoharness metdata failed"); let automatic_harnesses = get_all_automatic_harnesses( diff --git a/kani-driver/src/autoharness/mod.rs b/kani-driver/src/autoharness/mod.rs index 83162621dd1b..76803847b66e 100644 --- a/kani-driver/src/autoharness/mod.rs +++ b/kani-driver/src/autoharness/mod.rs @@ -83,7 +83,7 @@ fn postprocess_project( /// Print automatic harness metadata to the terminal. fn print_autoharness_metadata(metadata: Vec) { let mut chosen_table = PrettyTable::new(); - chosen_table.set_header(vec!["Chosen Function"]); + chosen_table.set_header(vec!["Selected Function"]); let mut skipped_table = PrettyTable::new(); skipped_table.set_header(vec!["Skipped Function", "Reason for Skipping"]); @@ -122,7 +122,7 @@ fn print_autoharness_metadata(metadata: Vec) { fn print_chosen_table(table: &mut PrettyTable) { if table.is_empty() { println!( - "\nChosen Functions: None. Kani did not generate automatic harnesses for any functions in the available crate(s)." + "\nSelected Functions: None. Kani did not generate automatic harnesses for any functions in the available crate(s)." ); return; } @@ -178,7 +178,8 @@ impl KaniSession { } /// Prints the results from running the `autoharness` subcommand. - pub fn print_autoharness_summary(&self, automatic: Vec<&HarnessResult<'_>>) -> Result<()> { + pub fn print_autoharness_summary(&self, mut automatic: Vec<&HarnessResult<'_>>) -> Result<()> { + automatic.sort_by(|a, b| a.harness.pretty_name.cmp(&b.harness.pretty_name)); let (successes, failures): (Vec<_>, Vec<_>) = automatic.into_iter().partition(|r| r.result.status == VerificationStatus::Success); diff --git a/kani_metadata/src/lib.rs b/kani_metadata/src/lib.rs index 775679329753..2117c04aac17 100644 --- a/kani_metadata/src/lib.rs +++ b/kani_metadata/src/lib.rs @@ -5,7 +5,7 @@ extern crate clap; use serde::{Deserialize, Serialize}; use std::{ - collections::{BTreeMap, HashSet}, + collections::{BTreeMap, BTreeSet, HashSet}, path::PathBuf, }; use strum_macros::{Display, EnumString}; @@ -43,12 +43,12 @@ pub struct KaniMetadata { /// For the autoharness subcommand, all of the user-defined functions we found, /// which are "chosen" if we generated an automatic harness for them, and "skipped" otherwise. +/// We use ordered data structures so that the metadata is in alphabetical order. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AutoHarnessMetadata { /// Functions we generated automatic harnesses for. - pub chosen: Vec, + pub chosen: BTreeSet, /// Map function names to the reason why we did not generate an automatic harness for that function. - /// We use an ordered map so that when we print the data, it is ordered alphabetically by function name. pub skipped: BTreeMap, } diff --git a/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected b/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected index 200dd56bdaba..7f3b92ffef86 100644 --- a/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected +++ b/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected @@ -1,16 +1,16 @@ Kani generated automatic harnesses for 5 function(s): +--------------------------------+ -| Chosen Function | +| Selected Function | +================================+ -| should_pass::div | +| should_fail::max | |--------------------------------| -| should_pass::has_recursion_gcd | +| should_pass::div | |--------------------------------| | should_pass::has_loop_contract | |--------------------------------| -| should_pass::unchecked_mul | +| should_pass::has_recursion_gcd | |--------------------------------| -| should_fail::max | +| should_pass::unchecked_mul | +--------------------------------+ Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). @@ -44,13 +44,13 @@ Autoharness Summary: +--------------------------------+-----------------------------+---------------------+ | Selected Function | Kind of Automatic Harness | Verification Result | +====================================================================================+ -| should_pass::unchecked_mul | #[kani::proof_for_contract] | Success | +| should_pass::div | #[kani::proof_for_contract] | Success | |--------------------------------+-----------------------------+---------------------| | should_pass::has_loop_contract | #[kani::proof] | Success | |--------------------------------+-----------------------------+---------------------| | should_pass::has_recursion_gcd | #[kani::proof_for_contract] | Success | |--------------------------------+-----------------------------+---------------------| -| should_pass::div | #[kani::proof_for_contract] | Success | +| should_pass::unchecked_mul | #[kani::proof_for_contract] | Success | |--------------------------------+-----------------------------+---------------------| | should_fail::max | #[kani::proof_for_contract] | Failure | +--------------------------------+-----------------------------+---------------------+ diff --git a/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected b/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected index aee1a3675f06..553573b70d2b 100644 --- a/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected +++ b/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected @@ -1,9 +1,9 @@ Kani generated automatic harnesses for 1 function(s): -+-----------------+ -| Chosen Function | -+=================+ -| yes_harness | -+-----------------+ ++-------------------+ +| Selected Function | ++===================+ +| yes_harness | ++-------------------+ Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). diff --git a/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected b/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected index 2f5e75ef5a3c..1b0085fbcf28 100644 --- a/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected +++ b/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected @@ -1,9 +1,9 @@ Kani generated automatic harnesses for 1 function(s): -+-----------------+ -| Chosen Function | -+=================+ -| include::simple | -+-----------------+ ++-------------------+ +| Selected Function | ++===================+ +| include::simple | ++-------------------+ Kani did not generate automatic harnesses for 2 function(s). If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832 diff --git a/tests/script-based-pre/cargo_autoharness_filter/filter.expected b/tests/script-based-pre/cargo_autoharness_filter/filter.expected index 8be532a63419..a7bc89c2116c 100644 --- a/tests/script-based-pre/cargo_autoharness_filter/filter.expected +++ b/tests/script-based-pre/cargo_autoharness_filter/filter.expected @@ -1,20 +1,26 @@ Kani generated automatic harnesses for 42 function(s): +----------------------------------------------+ -| Chosen Function | +| Selected Function | +==============================================+ -| yes_harness::f_u8 | +| yes_harness::empty_body | |----------------------------------------------| -| yes_harness::f_u16 | +| yes_harness::f_array | |----------------------------------------------| -| yes_harness::f_u32 | +| yes_harness::f_bool | |----------------------------------------------| -| yes_harness::f_u64 | +| yes_harness::f_char | |----------------------------------------------| -| yes_harness::f_u128 | +| yes_harness::f_derives_arbitrary | |----------------------------------------------| -| yes_harness::f_usize | +| yes_harness::f_f128 | |----------------------------------------------| -| yes_harness::f_i8 | +| yes_harness::f_f16 | +|----------------------------------------------| +| yes_harness::f_f32 | +|----------------------------------------------| +| yes_harness::f_f64 | +|----------------------------------------------| +| yes_harness::f_i128 | |----------------------------------------------| | yes_harness::f_i16 | |----------------------------------------------| @@ -22,69 +28,63 @@ Kani generated automatic harnesses for 42 function(s): |----------------------------------------------| | yes_harness::f_i64 | |----------------------------------------------| -| yes_harness::f_i128 | +| yes_harness::f_i8 | |----------------------------------------------| | yes_harness::f_isize | |----------------------------------------------| -| yes_harness::f_bool | -|----------------------------------------------| -| yes_harness::f_char | +| yes_harness::f_manually_implements_arbitrary | |----------------------------------------------| -| yes_harness::f_f32 | +| yes_harness::f_maybe_uninit | |----------------------------------------------| -| yes_harness::f_f64 | +| yes_harness::f_multiple_args | |----------------------------------------------| -| yes_harness::f_f16 | +| yes_harness::f_nonzero_i128 | |----------------------------------------------| -| yes_harness::f_f128 | +| yes_harness::f_nonzero_i16 | |----------------------------------------------| -| yes_harness::f_nonzero_u8 | +| yes_harness::f_nonzero_i32 | |----------------------------------------------| -| yes_harness::f_nonzero_u16 | +| yes_harness::f_nonzero_i64 | |----------------------------------------------| -| yes_harness::f_nonzero_u32 | +| yes_harness::f_nonzero_i8 | |----------------------------------------------| -| yes_harness::f_nonzero_u64 | +| yes_harness::f_nonzero_isize | |----------------------------------------------| | yes_harness::f_nonzero_u128 | |----------------------------------------------| -| yes_harness::f_nonzero_usize | -|----------------------------------------------| -| yes_harness::f_nonzero_i8 | +| yes_harness::f_nonzero_u16 | |----------------------------------------------| -| yes_harness::f_nonzero_i16 | +| yes_harness::f_nonzero_u32 | |----------------------------------------------| -| yes_harness::f_nonzero_i32 | +| yes_harness::f_nonzero_u64 | |----------------------------------------------| -| yes_harness::f_nonzero_i64 | +| yes_harness::f_nonzero_u8 | |----------------------------------------------| -| yes_harness::f_nonzero_i128 | +| yes_harness::f_nonzero_usize | |----------------------------------------------| -| yes_harness::f_nonzero_isize | +| yes_harness::f_option | |----------------------------------------------| -| yes_harness::f_array | +| yes_harness::f_phantom_data | |----------------------------------------------| -| yes_harness::f_option | +| yes_harness::f_phantom_pinned | |----------------------------------------------| | yes_harness::f_result | |----------------------------------------------| -| yes_harness::f_maybe_uninit | -|----------------------------------------------| | yes_harness::f_tuple | |----------------------------------------------| -| yes_harness::f_unsupported_return_type | +| yes_harness::f_u128 | |----------------------------------------------| -| yes_harness::f_multiple_args | +| yes_harness::f_u16 | |----------------------------------------------| -| yes_harness::f_derives_arbitrary | +| yes_harness::f_u32 | |----------------------------------------------| -| yes_harness::f_manually_implements_arbitrary | +| yes_harness::f_u64 | |----------------------------------------------| -| yes_harness::f_phantom_data | +| yes_harness::f_u8 | |----------------------------------------------| -| yes_harness::f_phantom_pinned | +| yes_harness::f_unsupported_return_type | |----------------------------------------------| -| yes_harness::empty_body | +| yes_harness::f_usize | +----------------------------------------------+ Kani did not generate automatic harnesses for 8 function(s). @@ -161,86 +161,86 @@ Autoharness Summary: +================================================================================================+ | yes_harness::empty_body | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_phantom_pinned | #[kani::proof] | Success | +| yes_harness::f_array | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_phantom_data | #[kani::proof] | Success | +| yes_harness::f_bool | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_manually_implements_arbitrary | #[kani::proof] | Success | +| yes_harness::f_char | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| | yes_harness::f_derives_arbitrary | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_multiple_args | #[kani::proof] | Success | +| yes_harness::f_f128 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_unsupported_return_type | #[kani::proof] | Success | +| yes_harness::f_f16 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_tuple | #[kani::proof] | Success | +| yes_harness::f_f32 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_maybe_uninit | #[kani::proof] | Success | +| yes_harness::f_f64 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_result | #[kani::proof] | Success | +| yes_harness::f_i128 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_option | #[kani::proof] | Success | +| yes_harness::f_i16 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_array | #[kani::proof] | Success | +| yes_harness::f_i32 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_isize | #[kani::proof] | Success | +| yes_harness::f_i64 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_i8 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_isize | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_manually_implements_arbitrary | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_maybe_uninit | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_multiple_args | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| | yes_harness::f_nonzero_i128 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_i64 | #[kani::proof] | Success | +| yes_harness::f_nonzero_i16 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| | yes_harness::f_nonzero_i32 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_i16 | #[kani::proof] | Success | +| yes_harness::f_nonzero_i64 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| | yes_harness::f_nonzero_i8 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_usize | #[kani::proof] | Success | +| yes_harness::f_nonzero_isize | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| | yes_harness::f_nonzero_u128 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_u64 | #[kani::proof] | Success | +| yes_harness::f_nonzero_u16 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| | yes_harness::f_nonzero_u32 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_u16 | #[kani::proof] | Success | +| yes_harness::f_nonzero_u64 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| | yes_harness::f_nonzero_u8 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_f128 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_f16 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_f64 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_f32 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_char | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_bool | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_isize | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_i128 | #[kani::proof] | Success | +| yes_harness::f_nonzero_usize | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_i64 | #[kani::proof] | Success | +| yes_harness::f_option | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_i32 | #[kani::proof] | Success | +| yes_harness::f_phantom_data | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_i16 | #[kani::proof] | Success | +| yes_harness::f_phantom_pinned | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_i8 | #[kani::proof] | Success | +| yes_harness::f_result | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_usize | #[kani::proof] | Success | +| yes_harness::f_tuple | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| | yes_harness::f_u128 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_u64 | #[kani::proof] | Success | +| yes_harness::f_u16 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| | yes_harness::f_u32 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_u16 | #[kani::proof] | Success | +| yes_harness::f_u64 | #[kani::proof] | Success | |----------------------------------------------+---------------------------+---------------------| | yes_harness::f_u8 | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_unsupported_return_type | #[kani::proof] | Success | +|----------------------------------------------+---------------------------+---------------------| +| yes_harness::f_usize | #[kani::proof] | Success | +----------------------------------------------+---------------------------+---------------------+ Complete - 42 successfully verified functions, 0 failures, 42 total. diff --git a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected index d1727ed86d03..d9969a0f38c9 100644 --- a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected +++ b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected @@ -1,13 +1,13 @@ Kani generated automatic harnesses for 5 function(s): +-------------------------+ -| Chosen Function | +| Selected Function | +=========================+ +| integer_overflow | +|-------------------------| | oob_safe_array_access | |-------------------------| | oob_unsafe_array_access | |-------------------------| -| integer_overflow | -|-------------------------| | panic | |-------------------------| | unchecked_mul | @@ -76,15 +76,15 @@ Autoharness Summary: +-------------------------+---------------------------+---------------------+ | Selected Function | Kind of Automatic Harness | Verification Result | +===========================================================================+ -| unchecked_mul | #[kani::proof] | Failure | -|-------------------------+---------------------------+---------------------| -| panic | #[kani::proof] | Failure | -|-------------------------+---------------------------+---------------------| | integer_overflow | #[kani::proof] | Failure | |-------------------------+---------------------------+---------------------| +| oob_safe_array_access | #[kani::proof] | Failure | +|-------------------------+---------------------------+---------------------| | oob_unsafe_array_access | #[kani::proof] | Failure | |-------------------------+---------------------------+---------------------| -| oob_safe_array_access | #[kani::proof] | Failure | +| panic | #[kani::proof] | Failure | +|-------------------------+---------------------------+---------------------| +| unchecked_mul | #[kani::proof] | Failure | +-------------------------+---------------------------+---------------------+ Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). diff --git a/tests/script-based-pre/cargo_autoharness_include/include.expected b/tests/script-based-pre/cargo_autoharness_include/include.expected index 2f5e75ef5a3c..1b0085fbcf28 100644 --- a/tests/script-based-pre/cargo_autoharness_include/include.expected +++ b/tests/script-based-pre/cargo_autoharness_include/include.expected @@ -1,9 +1,9 @@ Kani generated automatic harnesses for 1 function(s): -+-----------------+ -| Chosen Function | -+=================+ -| include::simple | -+-----------------+ ++-------------------+ +| Selected Function | ++===================+ +| include::simple | ++-------------------+ Kani did not generate automatic harnesses for 2 function(s). If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832 diff --git a/tests/script-based-pre/cargo_autoharness_list/list.expected b/tests/script-based-pre/cargo_autoharness_list/list.expected index bd20978a480e..4c9feb73d184 100644 --- a/tests/script-based-pre/cargo_autoharness_list/list.expected +++ b/tests/script-based-pre/cargo_autoharness_list/list.expected @@ -1,6 +1,6 @@ Kani generated automatic harnesses for 3 function(s): +---------------------------+ -| Chosen Function | +| Selected Function | +===========================+ | f_u8 | |---------------------------| diff --git a/tests/script-based-pre/cargo_autoharness_termination/src/lib.rs b/tests/script-based-pre/cargo_autoharness_termination/src/lib.rs deleted file mode 100644 index ab30e8e79eb7..000000000000 --- a/tests/script-based-pre/cargo_autoharness_termination/src/lib.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT - -// Test that the default bounds for automatic harnesses takes effect -// to terminate harnesses that would otherwise run forever. - -// Test that the --default-unwind makes harnesses terminate. -// (Technically, the harness timeout could take effect before the unwind bound, -// but in practice the unwind bound is low enough that it always takes effect first.) -mod unwind_bound { - fn infinite_loop() { - loop {} - } - - fn gcd_recursion(x: u64, y: u64) -> u64 { - let mut max = x; - let mut min = y; - if min > max { - let val = max; - max = min; - min = val; - } - let res = max % min; - if res == 0 { min } else { gcd_recursion(min, res + 1) } - } -} - -// Test that when there is no loop/recursion unwinding, the default harness timeout terminates the harness eventually. -mod timeout { - fn check_harness_timeout() { - // construct a problem that requires a long time to solve - let (a1, b1, c1): (u64, u64, u64) = kani::any(); - let (a2, b2, c2): (u64, u64, u64) = kani::any(); - let p1 = a1.saturating_mul(b1).saturating_mul(c1); - let p2 = a2.saturating_mul(b2).saturating_mul(c2); - assert!(a1 != a2 || b1 != b2 || c1 != c2 || p1 == p2) - } -} diff --git a/tests/script-based-pre/cargo_autoharness_termination/termination.expected b/tests/script-based-pre/cargo_autoharness_termination/termination.expected deleted file mode 100644 index 62aaf328f3d5..000000000000 --- a/tests/script-based-pre/cargo_autoharness_termination/termination.expected +++ /dev/null @@ -1,53 +0,0 @@ -Kani generated automatic harnesses for 3 function(s): -+--------------------------------+ -| Chosen Function | -+================================+ -| unwind_bound::infinite_loop | -|--------------------------------| -| unwind_bound::gcd_recursion | -|--------------------------------| -| timeout::check_harness_timeout | -+--------------------------------+ - -Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). - -Autoharness: Checking function timeout::check_harness_timeout against all possible inputs... -CBMC failed -VERIFICATION:- FAILED -CBMC timed out. You may want to rerun your proof with a larger timeout or use stubbing to reduce the size of the code the verifier reasons about. - -Autoharness: Checking function unwind_bound::gcd_recursion against all possible inputs... -Not unwinding recursion unwind_bound::gcd_recursion iteration 21 - -unwind_bound::gcd_recursion.assertion\ - - Status: FAILURE\ - - Description: "attempt to calculate the remainder with a divisor of zero" - -unwind_bound::gcd_recursion.recursion\ - - Status: FAILURE\ - - Description: "recursion unwinding assertion" - -VERIFICATION:- FAILED -[Kani] info: Verification output shows one or more unwinding failures. - -Autoharness: Checking function unwind_bound::infinite_loop against all possible inputs... -unwind_bound::infinite_loop.unwind\ - - Status: FAILURE\ - - Description: "unwinding assertion loop 0" - -VERIFICATION:- FAILED -[Kani] info: Verification output shows one or more unwinding failures. - -Autoharness Summary: -+--------------------------------+---------------------------+---------------------+ -| Selected Function | Kind of Automatic Harness | Verification Result | -+==================================================================================+ -| timeout::check_harness_timeout | #[kani::proof] | Failure | -|--------------------------------+---------------------------+---------------------| -| unwind_bound::gcd_recursion | #[kani::proof] | Failure | -|--------------------------------+---------------------------+---------------------| -| unwind_bound::infinite_loop | #[kani::proof] | Failure | -+--------------------------------+---------------------------+---------------------+ -Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. -If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). -Complete - 0 successfully verified functions, 3 failures, 3 total. diff --git a/tests/script-based-pre/cargo_autoharness_termination_timeout/Cargo.toml b/tests/script-based-pre/cargo_autoharness_termination_timeout/Cargo.toml new file mode 100644 index 000000000000..6d2cb6b893bf --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_termination_timeout/Cargo.toml @@ -0,0 +1,10 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +[package] +name = "cargo_autoharness_termination_timeout" +version = "0.1.0" +edition = "2024" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tests/script-based-pre/cargo_autoharness_termination_timeout/config.yml b/tests/script-based-pre/cargo_autoharness_termination_timeout/config.yml new file mode 100644 index 000000000000..b6fab7133fd6 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_termination_timeout/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: termination_timeout.sh +expected: termination_timeout.expected diff --git a/tests/script-based-pre/cargo_autoharness_termination_timeout/src/lib.rs b/tests/script-based-pre/cargo_autoharness_termination_timeout/src/lib.rs new file mode 100644 index 000000000000..a86a6ceff3fd --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_termination_timeout/src/lib.rs @@ -0,0 +1,14 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Test that the default timeout for automatic harnesses takes effect +// to terminate harnesses that would otherwise run for a long time. + +fn check_harness_timeout() { + // construct a problem that requires a long time to solve + let (a1, b1, c1): (u64, u64, u64) = kani::any(); + let (a2, b2, c2): (u64, u64, u64) = kani::any(); + let p1 = a1.saturating_mul(b1).saturating_mul(c1); + let p2 = a2.saturating_mul(b2).saturating_mul(c2); + assert!(a1 != a2 || b1 != b2 || c1 != c2 || p1 == p2) +} diff --git a/tests/script-based-pre/cargo_autoharness_termination_timeout/termination_timeout.expected b/tests/script-based-pre/cargo_autoharness_termination_timeout/termination_timeout.expected new file mode 100644 index 000000000000..1729bc2b823c --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_termination_timeout/termination_timeout.expected @@ -0,0 +1,24 @@ +Kani generated automatic harnesses for 1 function(s): ++-----------------------+ +| Selected Function | ++=======================+ +| check_harness_timeout | ++-----------------------+ + +Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). +Autoharness: Checking function check_harness_timeout against all possible inputs... +CBMC failed +VERIFICATION:- FAILED +CBMC timed out. You may want to rerun your proof with a larger timeout or use stubbing to reduce the size of the code the verifier reasons about. + +Manual Harness Summary: +No proof harnesses (functions with #[kani::proof]) were found to verify. + +Autoharness Summary: ++-----------------------+---------------------------+---------------------+ +| Selected Function | Kind of Automatic Harness | Verification Result | ++=========================================================================+ +| check_harness_timeout | #[kani::proof] | Failure | ++-----------------------+---------------------------+---------------------+ +Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. +If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). diff --git a/tests/script-based-pre/cargo_autoharness_termination/termination.sh b/tests/script-based-pre/cargo_autoharness_termination_timeout/termination_timeout.sh similarity index 100% rename from tests/script-based-pre/cargo_autoharness_termination/termination.sh rename to tests/script-based-pre/cargo_autoharness_termination_timeout/termination_timeout.sh diff --git a/tests/script-based-pre/cargo_autoharness_termination/Cargo.toml b/tests/script-based-pre/cargo_autoharness_termination_unwind/Cargo.toml similarity index 81% rename from tests/script-based-pre/cargo_autoharness_termination/Cargo.toml rename to tests/script-based-pre/cargo_autoharness_termination_unwind/Cargo.toml index 0a677a6f88cd..65204b3fdb72 100644 --- a/tests/script-based-pre/cargo_autoharness_termination/Cargo.toml +++ b/tests/script-based-pre/cargo_autoharness_termination_unwind/Cargo.toml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [package] -name = "cargo_autoharness_termination" +name = "cargo_autoharness_termination_unwind" version = "0.1.0" edition = "2024" diff --git a/tests/script-based-pre/cargo_autoharness_termination/config.yml b/tests/script-based-pre/cargo_autoharness_termination_unwind/config.yml similarity index 52% rename from tests/script-based-pre/cargo_autoharness_termination/config.yml rename to tests/script-based-pre/cargo_autoharness_termination_unwind/config.yml index 14e4df6b5fc2..148335b104cc 100644 --- a/tests/script-based-pre/cargo_autoharness_termination/config.yml +++ b/tests/script-based-pre/cargo_autoharness_termination_unwind/config.yml @@ -1,4 +1,4 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -script: termination.sh -expected: termination.expected +script: termination_unwind.sh +expected: termination_unwind.expected diff --git a/tests/script-based-pre/cargo_autoharness_termination_unwind/src/lib.rs b/tests/script-based-pre/cargo_autoharness_termination_unwind/src/lib.rs new file mode 100644 index 000000000000..7df1ab9cf68c --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_termination_unwind/src/lib.rs @@ -0,0 +1,23 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Test that the default loop/recursion bounds for automatic harnesses takes effect +// to terminate harnesses that would otherwise run forever. +// (Technically, the harness timeout could take effect before the unwind bound, +// but we raise the timeout to 5mins to make that unlikely.) + +fn infinite_loop() { + loop {} +} + +fn gcd_recursion(x: u64, y: u64) -> u64 { + let mut max = x; + let mut min = y; + if min > max { + let val = max; + max = min; + min = val; + } + let res = max % min; + if res == 0 { min } else { gcd_recursion(min, res + 1) } +} diff --git a/tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.expected b/tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.expected new file mode 100644 index 000000000000..1e198bb02988 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.expected @@ -0,0 +1,49 @@ +Kani generated automatic harnesses for 2 function(s): ++-------------------+ +| Selected Function | ++===================+ +| gcd_recursion | +|-------------------| +| infinite_loop | ++-------------------+ + +Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). +Autoharness: Checking function gcd_recursion against all possible inputs... +Not unwinding recursion gcd_recursion iteration 21 + +gcd_recursion.assertion\ + - Status: FAILURE\ + - Description: "attempt to calculate the remainder with a divisor of zero" + +gcd_recursion.recursion\ + - Status: FAILURE\ + - Description: "recursion unwinding assertion" + +VERIFICATION:- FAILED +[Kani] info: Verification output shows one or more unwinding failures. +[Kani] tip: Consider increasing the unwinding value or disabling `--unwinding-assertions`. + +Autoharness: Checking function infinite_loop against all possible inputs... +Not unwinding loop + +infinite_loop.unwind.0\ + - Status: FAILURE\ + - Description: "unwinding assertion loop 0" + +VERIFICATION:- FAILED +[Kani] info: Verification output shows one or more unwinding failures. +[Kani] tip: Consider increasing the unwinding value or disabling `--unwinding-assertions`. + +Manual Harness Summary: +No proof harnesses (functions with #[kani::proof]) were found to verify. + +Autoharness Summary: ++-------------------+---------------------------+---------------------+ +| Selected Function | Kind of Automatic Harness | Verification Result | ++=====================================================================+ +| gcd_recursion | #[kani::proof] | Failure | +|-------------------+---------------------------+---------------------| +| infinite_loop | #[kani::proof] | Failure | ++-------------------+---------------------------+---------------------+ +Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. +If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). diff --git a/tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.sh b/tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.sh new file mode 100755 index 000000000000..78df727def41 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +# Set the timeout to 5m to ensure that the gcd_recursion test gets killed because of the unwind bound +# and not because CBMC times out. +cargo kani autoharness -Z autoharness -Z function-contracts --harness-timeout 5m -Z unstable-options From 7a126c2b2e16b843d94bcd850a17375c3299b826 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Thu, 3 Apr 2025 14:09:45 -0700 Subject: [PATCH 081/161] Fix cargo invocations to only use `pkg_args` where appropriate (#3984) ## Summary When we invoke `cargo rustc` in `kani-driver`, we have some options in a `pkg_args` variable, and some in `kani_compiler_flags`. This PR removes a couple of uses of `pkg_args` that should have really been `kani_compiler_flags`, and provides documentation about the difference. ## Explanation Hopefully the documentation in the code is sufficient to understand the difference (please suggest changes if it's not!), but here's a longer explanation: `cargo kani` invokes [cargo rustc](https://doc.rust-lang.org/cargo/commands/cargo-rustc.html), described as follows: > cargo rustc [options] [-- args] > The specified target for the current package (or package specified by -p if provided) will be compiled along with all of its dependencies. The specified args will all be passed to the final compiler invocation, not any of the dependencies. Our `pkg_args` variable is what we provide for `-- args`, i.e., the arguments that we want to provide to kani-compiler when it compiles the package under verification, but *not its dependencies.* The docs then say: > To pass flags to all compiler processes spawned by Cargo, use the RUSTFLAGS [environment variable](https://doc.rust-lang.org/cargo/reference/environment-variables.html) We use the `RUSTFLAGS` environment variable to provide the `kani_compiler_flags` that should be passed when we invoke `kani-compiler` on the package to verify *and its dependencies.* So we should use `kani_compiler_flags` when the dependencies of the target package should receive the flag, and `pkg_args` when it shouldn't. I concluded that the only argument that it makes sense to provide in `pkg_args` is `--reachability`, because when `--reachability` isn't provided it defaults to `None`, which is the behavior we want. (Otherwise, we'd run Kani harnesses that we find in dependencies, or if autoharness is running, generate automatic harnesses for functions in dependencies, neither of which we want). Dependencies can get all of the other compiler arguments, since they don't do anything with them when `--reachability=None` anyway. ## Commit by Commit - `--no-assert-contracts` is already provided in `kani_compiler_flags`, and never should have been in `pkg_args` in the first place. - Having `--backend=llbc` as a `pkg_arg` means that it doesn't get provided to dependencies, so that when we run Kani's compiler on dependencies, we'd actually enter the cprover compiler interface. Move it to compiler args so that it gets passed to the target crate and its dependencies. - Autoharness should also use `kani_compiler_flags`, but that involves a larger refactor than I want to do this close to a release, so added a TODO for now. - Add documentation explaining the difference between `pkg_args` and `kani_compiler_flags`. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- kani-compiler/src/kani_compiler.rs | 1 + kani-compiler/src/main.rs | 9 +++++++-- kani-driver/src/autoharness/mod.rs | 2 ++ kani-driver/src/call_cargo.rs | 19 ++++++++++++------- kani-driver/src/call_single_file.rs | 15 ++++----------- kani-driver/src/session.rs | 2 +- 6 files changed, 27 insertions(+), 21 deletions(-) diff --git a/kani-compiler/src/kani_compiler.rs b/kani-compiler/src/kani_compiler.rs index 8382c5421222..25de67e45db2 100644 --- a/kani-compiler/src/kani_compiler.rs +++ b/kani-compiler/src/kani_compiler.rs @@ -111,6 +111,7 @@ impl Callbacks for KaniCompiler { let queries = self.queries.clone(); move |_cfg| backend(queries) })); + // `kani-driver` passes the `kani-compiler` specific arguments through llvm-args, so extract them here. args.extend(config.opts.cg.llvm_args.iter().cloned()); let args = Arguments::parse_from(args); init_session(&args, matches!(config.opts.error_format, ErrorOutputType::Json { .. })); diff --git a/kani-compiler/src/main.rs b/kani-compiler/src/main.rs index 6bf8e549af76..3452484c5054 100644 --- a/kani-compiler/src/main.rs +++ b/kani-compiler/src/main.rs @@ -72,10 +72,15 @@ fn main() { /// Return whether we should run our flavour of the compiler, and which arguments to pass to rustc. /// -/// We add a `--kani-compiler` argument to run the Kani version of the compiler, which needs to be +/// `kani-driver` adds a `--kani-compiler` argument to run the Kani version of the compiler, which needs to be /// filtered out before passing the arguments to rustc. -/// /// All other Kani arguments are today located inside `--llvm-args`. +/// +/// This function returns `true` for rustc invocations that originate from our rustc / cargo rustc invocations in `kani-driver`. +/// It returns `false` for rustc invocations that cargo adds in the process of executing the `kani-driver` rustc command. +/// For example, if we are compiling a crate that has a build.rs file, cargo will compile and run that build script +/// (c.f. https://doc.rust-lang.org/cargo/reference/build-scripts.html#life-cycle-of-a-build-script). +/// The build script should be compiled with normal rustc, not the Kani compiler. pub fn is_kani_compiler(args: Vec) -> (bool, Vec) { assert!(!args.is_empty(), "Arguments should always include executable name"); const KANI_COMPILER: &str = "--kani-compiler"; diff --git a/kani-driver/src/autoharness/mod.rs b/kani-driver/src/autoharness/mod.rs index 76803847b66e..498a8d3e94fe 100644 --- a/kani-driver/src/autoharness/mod.rs +++ b/kani-driver/src/autoharness/mod.rs @@ -154,6 +154,8 @@ impl KaniSession { } /// Add the compiler arguments specific to the `autoharness` subcommand. + /// TODO: this should really be appending onto the `kani_compiler_flags()` output instead of `pkg_args`. + /// It doesn't affect functionality since autoharness doesn't examine dependencies, but would still be better practice. pub fn add_auto_harness_args(&mut self, included: &[String], excluded: &[String]) { for func in included { self.pkg_args diff --git a/kani-driver/src/call_cargo.rs b/kani-driver/src/call_cargo.rs index 9097713a0016..5f9f18d03563 100644 --- a/kani-driver/src/call_cargo.rs +++ b/kani-driver/src/call_cargo.rs @@ -182,14 +182,19 @@ crate-type = ["lib"] cargo_args.push("-v".into()); } - // Arguments that will only be passed to the target package. + // Arguments that will only be passed to the target package (the package under verification) + // and not its dependencies, c.f. https://doc.rust-lang.org/cargo/commands/cargo-rustc.html. + // The difference between pkg_args and rustc_args is that rustc_args are also provided when + // we invoke rustc on the target package's dependencies. + // We do not provide the `--reachability` argument to dependencies so that it has the default value `None` + // (c.f. kani-compiler::args::ReachabilityType) and we skip codegen for the dependency. + // This is the desired behavior because we only want to construct `CodegenUnits` for the target package; + // i.e., if some dependency has harnesses, we don't want to run them. + + // If you are adding a new `kani-compiler` argument, you likely want to put it `kani_compiler_flags()` instead, + // unless there a reason it shouldn't be passed to dependencies. + // (Note that at the time of writing, passing the other compiler args to dependencies is a no-op, since `--reachability=None` skips codegen anyway.) self.pkg_args.push(self.reachability_arg()); - if let Some(backend_arg) = self.backend_arg() { - self.pkg_args.push(backend_arg); - } - if self.args.no_assert_contracts { - self.pkg_args.push("--no-assert-contracts".into()); - } let mut found_target = false; let packages = self.packages_to_verify(&self.args, &metadata)?; diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index f21f83345783..a24028dd84ba 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -53,9 +53,6 @@ impl KaniSession { ) -> Result<()> { let mut kani_args = self.kani_compiler_flags(); kani_args.push(format!("--reachability={}", self.reachability_mode())); - if self.args.common_args.unstable_features.contains(UnstableFeature::Lean) { - kani_args.push("--backend=llbc".into()); - } let lib_path = lib_folder().unwrap(); let mut rustc_args = self.kani_rustc_flags(LibConfig::new(lib_path)); @@ -100,14 +97,6 @@ impl KaniSession { to_rustc_arg(vec![format!("--reachability={}", self.reachability_mode())]) } - pub fn backend_arg(&self) -> Option { - if self.args.common_args.unstable_features.contains(UnstableFeature::Lean) { - Some(to_rustc_arg(vec!["--backend=llbc".into()])) - } else { - None - } - } - /// These arguments are arguments passed to kani-compiler that are `kani` compiler specific. pub fn kani_compiler_flags(&self) -> Vec { let mut flags = vec![check_version()]; @@ -150,6 +139,10 @@ impl KaniSession { flags.push("--ub-check=uninit".into()); } + if self.args.common_args.unstable_features.contains(UnstableFeature::Lean) { + flags.push("--backend=llbc".into()); + } + if self.args.print_llbc { flags.push("--print-llbc".into()); } diff --git a/kani-driver/src/session.rs b/kani-driver/src/session.rs index 6645db06e45e..20ebef0e2aaa 100644 --- a/kani-driver/src/session.rs +++ b/kani-driver/src/session.rs @@ -34,7 +34,7 @@ pub struct KaniSession { /// Automatically verify functions in the crate, in addition to running manual harnesses. pub auto_harness: bool, - /// The arguments that will be passed to the target package, i.e. kani_compiler. + /// The arguments that will be passed only to the target package, not its dependencies. pub pkg_args: Vec, /// Include all publicly-visible symbols in the generated goto binary, not just those reachable from From 1ff0c8f562687c380d0f8d1022b8584b009659a9 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Fri, 4 Apr 2025 05:51:40 -0700 Subject: [PATCH 082/161] Upgrade toolchain to 2025-04-03 (#3988) Culprit PR: https://github.com/rust-lang/rust/pull/139102 Resolves #3986 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- rust-toolchain.toml | 2 +- tests/coverage/abort/expected | 2 +- tests/coverage/known_issues/variant/expected | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 2c9d4b842f75..4286e45b410c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-04-02" +channel = "nightly-2025-04-03" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/coverage/abort/expected b/tests/coverage/abort/expected index e9c9727a6f03..0334841a880e 100644 --- a/tests/coverage/abort/expected +++ b/tests/coverage/abort/expected @@ -14,7 +14,7 @@ 14| 1| }\ 15| 1| if i == 2 {\ 16| | // This should never happen.\ - 17| 0| ```process::exit(1)''';\ + 17| 0| ```process::exit'''(1);\ 18| 1| } \ 19| | }\ 20| 0| ```assert!'''(false, ```"This is unreachable"''');\ diff --git a/tests/coverage/known_issues/variant/expected b/tests/coverage/known_issues/variant/expected index 6445c038f060..a42769c39f40 100644 --- a/tests/coverage/known_issues/variant/expected +++ b/tests/coverage/known_issues/variant/expected @@ -16,12 +16,12 @@ 16| 1| fn print_direction(dir: Direction) {\ 17| | // For some reason, `dir`'s span is reported as `UNCOVERED` too\ 18| 0| match ```dir''' {\ - 19| 0| Direction::Up => ```println!("Going up!")''',\ - 20| 0| Direction::Down => ```println!("Going down!")''',\ + 19| 0| Direction::Up => ```println!("Going up!"'''),\ + 20| 0| Direction::Down => ```println!("Going down!"'''),\ 21| 1| Direction::Left => println!("Going left!"),\ - 22| 0| Direction::Right if 1 == ```1 => println!("Going right!")''',\ + 22| 0| Direction::Right if 1 == ```1 => println!("Going right!"'''),\ 23| | // This part is unreachable since we cover all variants in the match.\ - 24| 0| _ => ```println!("Not going anywhere!")''',\ + 24| 0| _ => ```println!("Not going anywhere!"'''),\ 25| | }\ 26| | }\ 27| | \ From 3a82c3b5ba623507d3ca0b05ed55294748293b48 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Fri, 4 Apr 2025 09:43:00 -0700 Subject: [PATCH 083/161] Bump Kani version to 0.61.0 (#3989) Bump Kani version to 0.61.0. Github-generated release notes: ## What's Changed * Fix CHANGELOG of 0.60.0 by @qinheping in https://github.com/model-checking/kani/pull/3925 * Bump tests/perf/s2n-quic from `d88faa4` to `8670e83` by @dependabot in https://github.com/model-checking/kani/pull/3928 * Update toolchain to 2025-03-04 by @qinheping in https://github.com/model-checking/kani/pull/3927 * Install the right toolchain for HEAD and BASE checks in `verify-std-check.yml` by @remi-delmas-3000 in https://github.com/model-checking/kani/pull/3920 * Automatic cargo update to 2025-03-10 by @github-actions in https://github.com/model-checking/kani/pull/3926 * Automatic toolchain upgrade to nightly-2025-03-05 by @github-actions in https://github.com/model-checking/kani/pull/3929 * Upgrade toolchain to nightly-2025-03-07 by @tautschnig in https://github.com/model-checking/kani/pull/3931 * Upgrade toolchain to nightly-2025-03-12 by @tautschnig in https://github.com/model-checking/kani/pull/3933 * Automatic toolchain upgrade to nightly-2025-03-13 by @github-actions in https://github.com/model-checking/kani/pull/3934 * Update CBMC dependency to 6.5.0 by @tautschnig in https://github.com/model-checking/kani/pull/3936 * Automatic toolchain upgrade to nightly-2025-03-14 by @github-actions in https://github.com/model-checking/kani/pull/3937 * Automatic toolchain upgrade to nightly-2025-03-15 by @github-actions in https://github.com/model-checking/kani/pull/3938 * Automatic toolchain upgrade to nightly-2025-03-16 by @github-actions in https://github.com/model-checking/kani/pull/3939 * Automatic toolchain upgrade to nightly-2025-03-17 by @github-actions in https://github.com/model-checking/kani/pull/3940 * Automatic cargo update to 2025-03-17 by @github-actions in https://github.com/model-checking/kani/pull/3941 * Autoharness: Don't panic on `_` argument and add `_autoharness` suffix to GOTO files by @carolynzech in https://github.com/model-checking/kani/pull/3942 * Implement `f16` and `f128` cases in `codegen_float_type` by @carolynzech in https://github.com/model-checking/kani/pull/3943 * Support function implementations of known built-ins by @tautschnig in https://github.com/model-checking/kani/pull/3945 * Autoharness: metadata improvements and enable standard library application by @carolynzech in https://github.com/model-checking/kani/pull/3948 * Autoharness: `--list` option by @carolynzech in https://github.com/model-checking/kani/pull/3952 * Add support for anonymous nested statics by @carolynzech in https://github.com/model-checking/kani/pull/3953 * Automatic cargo update to 2025-03-24 by @github-actions in https://github.com/model-checking/kani/pull/3954 * Bump tests/perf/s2n-quic from `8670e83` to `324cf31` by @dependabot in https://github.com/model-checking/kani/pull/3955 * Document behavior of checked_size_of_raw and is_inbounds by @rajath-mk in https://github.com/model-checking/kani/pull/3956 * Upgrade toolchain to 2025-03-18 by @zhassan-aws in https://github.com/model-checking/kani/pull/3959 * Remove unstable-features from code formatting script by @zhassan-aws in https://github.com/model-checking/kani/pull/3962 * Remove CI job to update features/verify-rust-std by @tautschnig in https://github.com/model-checking/kani/pull/3963 * Make is_inbounds public by @rajath-mk in https://github.com/model-checking/kani/pull/3958 * Enable Kani to work with a stable toolchain by @zhassan-aws in https://github.com/model-checking/kani/pull/3964 * Automatic cargo update to 2025-03-31 by @github-actions in https://github.com/model-checking/kani/pull/3966 * Add support for struct field accessing in loop contracts by @thanhnguyen-aws in https://github.com/model-checking/kani/pull/3970 * Bump tests/perf/s2n-quic from `324cf31` to `d0aff82` by @dependabot in https://github.com/model-checking/kani/pull/3968 * Clarify `is_inbounds` docs by @carolynzech in https://github.com/model-checking/kani/pull/3974 * Upgrade toolchain to 2025-04-01 by @carolynzech in https://github.com/model-checking/kani/pull/3973 * Remove remaining `--enable-unstable` mentions by @carolynzech in https://github.com/model-checking/kani/pull/3978 * Clean up unused dependencies by @zhassan-aws in https://github.com/model-checking/kani/pull/3981 * Automatic toolchain upgrade to nightly-2025-04-02 by @github-actions in https://github.com/model-checking/kani/pull/3983 * Update dependencies per `cargo-outdated` by @carolynzech in https://github.com/model-checking/kani/pull/3982 * Fix `autoharness` termination test & print metadata in alphabetical order by @carolynzech in https://github.com/model-checking/kani/pull/3971 * Fix cargo invocations to only use `pkg_args` where appropriate by @carolynzech in https://github.com/model-checking/kani/pull/3984 **Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.60.0...kani-0.61.0 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- CHANGELOG.md | 15 +++++++++++++++ Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- cprover_bindings/Cargo.toml | 2 +- kani-compiler/Cargo.toml | 2 +- kani-driver/Cargo.toml | 2 +- kani_metadata/Cargo.toml | 2 +- library/kani/Cargo.toml | 2 +- library/kani_core/Cargo.toml | 2 +- library/kani_macros/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- tools/build-kani/Cargo.toml | 2 +- 12 files changed, 35 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d205f8af8e31..4e4bb46e4633 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ This file contains notable changes (e.g. breaking changes, major changes, etc.) This file was introduced starting Kani 0.23.0, so it only contains changes from version 0.23.0 onwards. +## [0.61.0] + +### What's Changed +* Make `is_inbounds` public by @rajath-mk in https://github.com/model-checking/kani/pull/3958 +* Finish adding support for `f16` and `f128` by @carolynzech in https://github.com/model-checking/kani/pull/3943 +* Support user overrides of Rust built-ins by @tautschnig in https://github.com/model-checking/kani/pull/3945 +* Add support for anonymous nested statics by @carolynzech in https://github.com/model-checking/kani/pull/3953 +* Add support for struct field access in loop contracts by @thanhnguyen-aws in https://github.com/model-checking/kani/pull/3970 +* Autoharness: Don't panic on `_` argument by @carolynzech in https://github.com/model-checking/kani/pull/3942 +* Autoharness: improve metdata printed to terminal and enable standard library application by @carolynzech in https://github.com/model-checking/kani/pull/3948, https://github.com/model-checking/kani/pull/3952, https://github.com/model-checking/kani/pull/3971 +* Upgrade toolchain to nightly-2025-04-03 by @qinheping, @tautschnig, @zhassan-aws, @carolynzech in https://github.com/model-checking/kani/pull/3988 +* Update CBMC dependency to 6.5.0 by @tautschnig in https://github.com/model-checking/kani/pull/3936 + +**Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.60.0...kani-0.61.0 + ## [0.60.0] ### Breaking Changes diff --git a/Cargo.lock b/Cargo.lock index 5f047c1403ac..347020b9a99d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -176,7 +176,7 @@ dependencies = [ [[package]] name = "build-kani" -version = "0.60.0" +version = "0.61.0" dependencies = [ "anyhow", "cargo_metadata", @@ -381,7 +381,7 @@ dependencies = [ [[package]] name = "cprover_bindings" -version = "0.60.0" +version = "0.61.0" dependencies = [ "lazy_static", "linear-map", @@ -824,7 +824,7 @@ checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5" [[package]] name = "kani" -version = "0.60.0" +version = "0.61.0" dependencies = [ "kani_core", "kani_macros", @@ -832,7 +832,7 @@ dependencies = [ [[package]] name = "kani-compiler" -version = "0.60.0" +version = "0.61.0" dependencies = [ "charon", "clap", @@ -868,7 +868,7 @@ dependencies = [ [[package]] name = "kani-driver" -version = "0.60.0" +version = "0.61.0" dependencies = [ "anyhow", "cargo_metadata", @@ -897,7 +897,7 @@ dependencies = [ [[package]] name = "kani-verifier" -version = "0.60.0" +version = "0.61.0" dependencies = [ "anyhow", "home", @@ -906,14 +906,14 @@ dependencies = [ [[package]] name = "kani_core" -version = "0.60.0" +version = "0.61.0" dependencies = [ "kani_macros", ] [[package]] name = "kani_macros" -version = "0.60.0" +version = "0.61.0" dependencies = [ "proc-macro-error2", "proc-macro2", @@ -923,7 +923,7 @@ dependencies = [ [[package]] name = "kani_metadata" -version = "0.60.0" +version = "0.61.0" dependencies = [ "clap", "cprover_bindings", @@ -1647,7 +1647,7 @@ dependencies = [ [[package]] name = "std" -version = "0.60.0" +version = "0.61.0" dependencies = [ "kani", ] diff --git a/Cargo.toml b/Cargo.toml index beabf68f6e53..e98c08d7d2a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-verifier" -version = "0.60.0" +version = "0.61.0" edition = "2021" description = "A bit-precise model checker for Rust." readme = "README.md" diff --git a/cprover_bindings/Cargo.toml b/cprover_bindings/Cargo.toml index 9024dc3fb388..3ff968d46e8a 100644 --- a/cprover_bindings/Cargo.toml +++ b/cprover_bindings/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "cprover_bindings" -version = "0.60.0" +version = "0.61.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index f1640a0027ba..4b31fbd0b499 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-compiler" -version = "0.60.0" +version = "0.61.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index e5ac4aec0c68..b0d2c3055125 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-driver" -version = "0.60.0" +version = "0.61.0" edition = "2021" description = "Build a project with Kani and run all proof harnesses" license = "MIT OR Apache-2.0" diff --git a/kani_metadata/Cargo.toml b/kani_metadata/Cargo.toml index 638b81f609d2..148276d2c561 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_metadata" -version = "0.60.0" +version = "0.61.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index 4b32b92485e8..93ba8d876639 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.60.0" +version = "0.61.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani_core/Cargo.toml b/library/kani_core/Cargo.toml index 17338d03f12f..cea8ae669af7 100644 --- a/library/kani_core/Cargo.toml +++ b/library/kani_core/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_core" -version = "0.60.0" +version = "0.61.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani_macros/Cargo.toml b/library/kani_macros/Cargo.toml index 8a68cd8cc124..2204721dd116 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_macros" -version = "0.60.0" +version = "0.61.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index b4ca6fee7822..392812deaffa 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -5,7 +5,7 @@ # Note: this package is intentionally named std to make sure the names of # standard library symbols are preserved name = "std" -version = "0.60.0" +version = "0.61.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/tools/build-kani/Cargo.toml b/tools/build-kani/Cargo.toml index 23632adc48ec..87e29cef410f 100644 --- a/tools/build-kani/Cargo.toml +++ b/tools/build-kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "build-kani" -version = "0.60.0" +version = "0.61.0" edition = "2021" description = "Builds Kani, Sysroot and release bundle." license = "MIT OR Apache-2.0" From 04ed7a3aa9230e798fee10794657cdfa16241b0e Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Fri, 4 Apr 2025 11:07:40 -0700 Subject: [PATCH 084/161] Disable llbc feature by default (#3980) Disable the Kani compiler's `llbc` feature by default, and add a CI workflow that compiles with that feature enabled and runs the llbc regressions. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .github/workflows/kani.yml | 17 ++++++++++ kani-compiler/Cargo.toml | 2 +- scripts/kani-llbc-regression.sh | 33 +++++++++++++++++++ tests/{expected => }/llbc/basic0/expected | 0 tests/{expected => }/llbc/basic0/test.rs | 0 tests/{expected => }/llbc/basic1/expected | 0 tests/{expected => }/llbc/basic1/test.rs | 0 tests/{expected => }/llbc/enum/expected | 0 tests/{expected => }/llbc/enum/test.rs | 0 tests/{expected => }/llbc/generic/expected | 0 tests/{expected => }/llbc/generic/test.rs | 0 tests/{expected => }/llbc/option/expected | 0 tests/{expected => }/llbc/option/test.rs | 0 tests/{expected => }/llbc/projection/expected | 0 tests/{expected => }/llbc/projection/test.rs | 0 tests/{expected => }/llbc/struct/expected | 0 tests/{expected => }/llbc/struct/test.rs | 0 tests/{expected => }/llbc/traitimpl/expected | 0 tests/{expected => }/llbc/traitimpl/test.rs | 0 tests/{expected => }/llbc/tuple/expected | 0 tests/{expected => }/llbc/tuple/test.rs | 0 21 files changed, 51 insertions(+), 1 deletion(-) create mode 100755 scripts/kani-llbc-regression.sh rename tests/{expected => }/llbc/basic0/expected (100%) rename tests/{expected => }/llbc/basic0/test.rs (100%) rename tests/{expected => }/llbc/basic1/expected (100%) rename tests/{expected => }/llbc/basic1/test.rs (100%) rename tests/{expected => }/llbc/enum/expected (100%) rename tests/{expected => }/llbc/enum/test.rs (100%) rename tests/{expected => }/llbc/generic/expected (100%) rename tests/{expected => }/llbc/generic/test.rs (100%) rename tests/{expected => }/llbc/option/expected (100%) rename tests/{expected => }/llbc/option/test.rs (100%) rename tests/{expected => }/llbc/projection/expected (100%) rename tests/{expected => }/llbc/projection/test.rs (100%) rename tests/{expected => }/llbc/struct/expected (100%) rename tests/{expected => }/llbc/struct/test.rs (100%) rename tests/{expected => }/llbc/traitimpl/expected (100%) rename tests/{expected => }/llbc/traitimpl/test.rs (100%) rename tests/{expected => }/llbc/tuple/expected (100%) rename tests/{expected => }/llbc/tuple/test.rs (100%) diff --git a/.github/workflows/kani.yml b/.github/workflows/kani.yml index 1c8cd119c393..5892ba13d26a 100644 --- a/.github/workflows/kani.yml +++ b/.github/workflows/kani.yml @@ -70,6 +70,23 @@ jobs: env: RUST_TEST_THREADS: 1 + llbc-regression: + runs-on: ubuntu-24.04 + steps: + - name: Checkout Kani + uses: actions/checkout@v4 + + - name: Setup Kani Dependencies + uses: ./.github/actions/setup + with: + os: ubuntu-24.04 + + - name: Build Kani with Charon + run: cargo build-dev -- --features cprover --features llbc + + - name: Run tests + run: ./scripts/kani-llbc-regression.sh + documentation: runs-on: ubuntu-24.04 permissions: diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index 4b31fbd0b499..e79b167dafb2 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -28,7 +28,7 @@ tracing-tree = "0.4.0" # Future proofing: enable backend dependencies using feature. [features] -default = ['cprover', 'llbc'] +default = ['cprover'] llbc = ['charon'] cprover = ['cbmc', 'num', 'serde'] diff --git a/scripts/kani-llbc-regression.sh b/scripts/kani-llbc-regression.sh new file mode 100755 index 000000000000..9d07814e4ab3 --- /dev/null +++ b/scripts/kani-llbc-regression.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +if [[ -z $KANI_REGRESSION_KEEP_GOING ]]; then + set -o errexit +fi +set -o pipefail +set -o nounset + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +export PATH=$SCRIPT_DIR:$PATH + +# Formatting check +${SCRIPT_DIR}/kani-fmt.sh --check + +# Build kani +cargo build-dev -- --features cprover --features llbc + +# Build compiletest and print configuration. We pick suite / mode combo so there's no test. +echo "--- Compiletest configuration" +cargo run -p compiletest --quiet -- --suite kani --mode cargo-kani --dry-run --verbose +echo "-----------------------------" + +suite="llbc" +mode="expected" +echo "Check compiletest suite=$suite mode=$mode" +cargo run -p compiletest --quiet -- --suite $suite --mode $mode \ + --quiet --no-fail-fast + +echo +echo "All Kani llbc regression tests completed successfully." +echo diff --git a/tests/expected/llbc/basic0/expected b/tests/llbc/basic0/expected similarity index 100% rename from tests/expected/llbc/basic0/expected rename to tests/llbc/basic0/expected diff --git a/tests/expected/llbc/basic0/test.rs b/tests/llbc/basic0/test.rs similarity index 100% rename from tests/expected/llbc/basic0/test.rs rename to tests/llbc/basic0/test.rs diff --git a/tests/expected/llbc/basic1/expected b/tests/llbc/basic1/expected similarity index 100% rename from tests/expected/llbc/basic1/expected rename to tests/llbc/basic1/expected diff --git a/tests/expected/llbc/basic1/test.rs b/tests/llbc/basic1/test.rs similarity index 100% rename from tests/expected/llbc/basic1/test.rs rename to tests/llbc/basic1/test.rs diff --git a/tests/expected/llbc/enum/expected b/tests/llbc/enum/expected similarity index 100% rename from tests/expected/llbc/enum/expected rename to tests/llbc/enum/expected diff --git a/tests/expected/llbc/enum/test.rs b/tests/llbc/enum/test.rs similarity index 100% rename from tests/expected/llbc/enum/test.rs rename to tests/llbc/enum/test.rs diff --git a/tests/expected/llbc/generic/expected b/tests/llbc/generic/expected similarity index 100% rename from tests/expected/llbc/generic/expected rename to tests/llbc/generic/expected diff --git a/tests/expected/llbc/generic/test.rs b/tests/llbc/generic/test.rs similarity index 100% rename from tests/expected/llbc/generic/test.rs rename to tests/llbc/generic/test.rs diff --git a/tests/expected/llbc/option/expected b/tests/llbc/option/expected similarity index 100% rename from tests/expected/llbc/option/expected rename to tests/llbc/option/expected diff --git a/tests/expected/llbc/option/test.rs b/tests/llbc/option/test.rs similarity index 100% rename from tests/expected/llbc/option/test.rs rename to tests/llbc/option/test.rs diff --git a/tests/expected/llbc/projection/expected b/tests/llbc/projection/expected similarity index 100% rename from tests/expected/llbc/projection/expected rename to tests/llbc/projection/expected diff --git a/tests/expected/llbc/projection/test.rs b/tests/llbc/projection/test.rs similarity index 100% rename from tests/expected/llbc/projection/test.rs rename to tests/llbc/projection/test.rs diff --git a/tests/expected/llbc/struct/expected b/tests/llbc/struct/expected similarity index 100% rename from tests/expected/llbc/struct/expected rename to tests/llbc/struct/expected diff --git a/tests/expected/llbc/struct/test.rs b/tests/llbc/struct/test.rs similarity index 100% rename from tests/expected/llbc/struct/test.rs rename to tests/llbc/struct/test.rs diff --git a/tests/expected/llbc/traitimpl/expected b/tests/llbc/traitimpl/expected similarity index 100% rename from tests/expected/llbc/traitimpl/expected rename to tests/llbc/traitimpl/expected diff --git a/tests/expected/llbc/traitimpl/test.rs b/tests/llbc/traitimpl/test.rs similarity index 100% rename from tests/expected/llbc/traitimpl/test.rs rename to tests/llbc/traitimpl/test.rs diff --git a/tests/expected/llbc/tuple/expected b/tests/llbc/tuple/expected similarity index 100% rename from tests/expected/llbc/tuple/expected rename to tests/llbc/tuple/expected diff --git a/tests/expected/llbc/tuple/test.rs b/tests/llbc/tuple/test.rs similarity index 100% rename from tests/expected/llbc/tuple/test.rs rename to tests/llbc/tuple/test.rs From 8565e1e6c23f0d695c5eaf6b50232daf4ba08fd3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 11:34:09 -0400 Subject: [PATCH 085/161] Automatic cargo update to 2025-04-07 (#3992) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 347020b9a99d..44ccca756440 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.17" +version = "1.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" dependencies = [ "shlex", ] @@ -276,9 +276,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.34" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff" +checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" dependencies = [ "clap_builder", "clap_derive", @@ -286,9 +286,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.34" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489" +checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" dependencies = [ "anstream", "anstyle", @@ -499,9 +499,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] @@ -571,9 +571,9 @@ checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" [[package]] name = "env_logger" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", @@ -590,9 +590,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -748,9 +748,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -1020,9 +1020,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" dependencies = [ "adler2", ] @@ -1379,9 +1379,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" dependencies = [ "bitflags", ] @@ -1460,9 +1460,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ "bitflags", "errno", @@ -1628,9 +1628,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "stacker" @@ -1719,7 +1719,7 @@ dependencies = [ "fastrand", "getrandom 0.3.2", "once_cell", - "rustix 1.0.3", + "rustix 1.0.5", "windows-sys 0.59.0", ] @@ -1823,9 +1823,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.1" +version = "1.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" dependencies = [ "backtrace", "bytes", From 034b0569561ebe841bf73155360a8f830e1512c5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 11:35:52 -0400 Subject: [PATCH 086/161] Automatic toolchain upgrade to nightly-2025-04-04 (#3991) Update Rust toolchain from nightly-2025-04-03 to nightly-2025-04-04 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 4286e45b410c..fc500eb3b1c4 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-04-03" +channel = "nightly-2025-04-04" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 1301558193725bf55fbe2cea28859ba4552a96a6 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Tue, 8 Apr 2025 10:14:16 -0400 Subject: [PATCH 087/161] Fix release schedule in docs (#3994) We release on a monthly cadence now. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- docs/src/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/getting-started.md b/docs/src/getting-started.md index 1acfd039ce97..dbf626dc793d 100644 --- a/docs/src/getting-started.md +++ b/docs/src/getting-started.md @@ -20,7 +20,7 @@ We also publish updates on Kani use cases and features on our [blog](https://mod There is support for a fair amount of Rust language features, but not all (e.g., concurrency). Please see [Limitations](./limitations.md) for a detailed list of supported features. -Kani releases every two weeks. +Kani releases every month. As part of every release, Kani will synchronize with a recent nightly release of Rust, and so is generally up-to-date with the latest Rust language features. If you encounter issues when using Kani, we encourage you to [report them to us](https://github.com/model-checking/kani/issues/new/choose). From e6bc7e21c72c18d1daeb261acf527fa016c72eea Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Apr 2025 11:13:55 -0400 Subject: [PATCH 088/161] Automatic toolchain upgrade to nightly-2025-04-05 (#3996) Update Rust toolchain from nightly-2025-04-04 to nightly-2025-04-05 without any other source changes. Co-authored-by: carolynzech <71352687+carolynzech@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index fc500eb3b1c4..12e1956c0337 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-04-04" +channel = "nightly-2025-04-05" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 5351603ca8664eedd81baaaa273724f1125a3d5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Apr 2025 11:15:12 -0400 Subject: [PATCH 089/161] Bump tests/perf/s2n-quic from `d0aff82` to `9f7e0a9` (#3995) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `d0aff82` to `9f7e0a9`.
Commits
  • 9f7e0a9 fix: typo in rebind_before_handshake_confirmed comment (#2594)
  • 1081782 ci: Fix cargo-udeps install (#2592)
  • a459e4b chore: Fix new clippy warnings (#2590)
  • 0016178 fix(s2n-quic-core): Avoid copy for unique Bytes in put_bytes (#2589)
  • 89c62d6 fix(s2n-quic-core): optimize Bytes read on full read (#2588)
  • 79ef737 fix(s2n-quic-dc): correctly map addresses for pooled UDP streams (#2587)
  • de41b12 ci: Fix hanging miri job (#2584)
  • 9fb10b2 ci: Disable Github actions cache (#2582)
  • cd97a79 fix(s2n-quic-dc): export local addr regardless of reuse_port config (#2578)
  • 551559d fix(s2n-quic-dc): fix retransmission cloning (#2579)
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Carolyn Zech --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index d0aff821d596..9f7e0a9e5b1a 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit d0aff821d596f279c2fe3af1cfb946174196c257 +Subproject commit 9f7e0a9e5b1a6b918b7436a2905621e56d05a388 From c80e966b6b805cc2680259e896f710b8a602c352 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 00:40:20 -0700 Subject: [PATCH 090/161] Automatic toolchain upgrade to nightly-2025-04-06 (#4004) Update Rust toolchain from nightly-2025-04-05 to nightly-2025-04-06 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 12e1956c0337..3eab2efec586 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-04-05" +channel = "nightly-2025-04-06" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 0e4cab92da84cfccbb0dd3066f5e2d9ef45cfbdd Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Thu, 10 Apr 2025 03:38:52 -0700 Subject: [PATCH 091/161] Fix the package docker job in the release workflow (#4003) https://github.com/model-checking/kani/pull/3918 included this change to the release workflow: ```diff diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df2710ee3e0..244c8063f0c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml - name: 'Build release bundle' run: | @@ -339,7 +336,7 @@ jobs: uses: docker/build-push-action@v6 with: context: . - file: scripts/ci/Dockerfile.bundle-release-20-04 + file: scripts/ci/Dockerfile.bundle-release-24-04 push: true github-token: ${{ secrets.GITHUB_TOKEN }} tags: | ``` but the referenced file (`scripts/ci/Dockerfile.bundle-release-20-04`) was not renamed. This caused the package docker job to fail in the last release: https://github.com/model-checking/kani/actions/runs/14296758126/job/40065081063 with: ``` ERROR: failed to solve: failed to read dockerfile: open Dockerfile.bundle-release-24-04: no such file or directory ``` This PR renames the file and updates it to use Ubuntu 24.04. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- ...e.bundle-release-20-04 => Dockerfile.bundle-release-24-04} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename scripts/ci/{Dockerfile.bundle-release-20-04 => Dockerfile.bundle-release-24-04} (93%) diff --git a/scripts/ci/Dockerfile.bundle-release-20-04 b/scripts/ci/Dockerfile.bundle-release-24-04 similarity index 93% rename from scripts/ci/Dockerfile.bundle-release-20-04 rename to scripts/ci/Dockerfile.bundle-release-24-04 index 963aa952d6df..7231da5afb76 100644 --- a/scripts/ci/Dockerfile.bundle-release-20-04 +++ b/scripts/ci/Dockerfile.bundle-release-24-04 @@ -1,9 +1,9 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -# Docker image for Kani GitHub Package release ubuntu-20-04. +# Docker image for Kani GitHub Package release ubuntu-24-04. -FROM ubuntu:20.04 +FROM ubuntu:24.04 ENV DEBIAN_FRONTEND=noninteractive \ DEBCONF_NONINTERACTIVE_SEEN=true \ PATH="/root/.cargo/bin:${PATH}" From b70548f9ff0a6c36b950ca0cd6f451898b233977 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Thu, 10 Apr 2025 15:29:35 +0200 Subject: [PATCH 092/161] Avoid spurious action failures in forks (#4005) Forks are not generally configured to take issues, making the create-issue step (spuriously) fail with "Issues are disabled for this repo". To avoid repeat notifications about those spurious failures, do not attempt the run the create-issue step outside the main repository. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .github/workflows/cbmc-update.yml | 2 +- .github/workflows/toolchain-upgrade.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cbmc-update.yml b/.github/workflows/cbmc-update.yml index 8d0bc057da2f..927dde0037be 100644 --- a/.github/workflows/cbmc-update.yml +++ b/.github/workflows/cbmc-update.yml @@ -75,7 +75,7 @@ jobs: Upgrade CBMC to its latest release. - name: Create Issue - if: ${{ env.next_step == 'create_issue' }} + if: ${{ env.next_step == 'create_issue' && github.repository_owner == 'model-checking' }} uses: dacbd/create-issue-action@main with: token: ${{ github.token }} diff --git a/.github/workflows/toolchain-upgrade.yml b/.github/workflows/toolchain-upgrade.yml index 8a7f448ca84e..c242cae0d4cf 100644 --- a/.github/workflows/toolchain-upgrade.yml +++ b/.github/workflows/toolchain-upgrade.yml @@ -68,7 +68,7 @@ jobs: }) - name: Create Issue - if: ${{ env.next_step == 'create_issue' }} + if: ${{ env.next_step == 'create_issue' && github.repository_owner == 'model-checking' }} uses: dacbd/create-issue-action@main with: token: ${{ github.token }} From 1537c294dec7f6eb2dfa27ac4868301c36b4fbe9 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Thu, 10 Apr 2025 10:48:56 -0700 Subject: [PATCH 093/161] Add an option to skip codegen (#4002) This PR adds a new unstable option, `--no-codegen`, that can be used for running Kani, just to check if the code compiles, i.e. similar to running `cargo check`. This is useful as a lightweight check for compilation errors including code under `#[cfg(kani)]`. This is implemented through passing rustc's `-Zno-codegen` option, which is what `cargo check` passes to `rustc`. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Carolyn Zech --- Cargo.toml | 2 ++ kani-driver/src/args/mod.rs | 11 +++++++++++ kani-driver/src/call_single_file.rs | 5 +++++ kani-driver/src/project.rs | 9 ++++++++- tests/script-based-pre/no_codegen/Cargo.toml | 9 +++++++++ tests/script-based-pre/no_codegen/config.yml | 5 +++++ tests/script-based-pre/no_codegen/expected | 1 + tests/script-based-pre/no_codegen/run.sh | 14 ++++++++++++++ tests/script-based-pre/no_codegen/src/main.rs | 14 ++++++++++++++ tests/script-based-pre/no_codegen_error/Cargo.toml | 9 +++++++++ tests/script-based-pre/no_codegen_error/config.yml | 5 +++++ tests/script-based-pre/no_codegen_error/expected | 1 + tests/script-based-pre/no_codegen_error/run.sh | 8 ++++++++ .../script-based-pre/no_codegen_error/src/main.rs | 13 +++++++++++++ 14 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 tests/script-based-pre/no_codegen/Cargo.toml create mode 100644 tests/script-based-pre/no_codegen/config.yml create mode 100644 tests/script-based-pre/no_codegen/expected create mode 100755 tests/script-based-pre/no_codegen/run.sh create mode 100644 tests/script-based-pre/no_codegen/src/main.rs create mode 100644 tests/script-based-pre/no_codegen_error/Cargo.toml create mode 100644 tests/script-based-pre/no_codegen_error/config.yml create mode 100644 tests/script-based-pre/no_codegen_error/expected create mode 100755 tests/script-based-pre/no_codegen_error/run.sh create mode 100644 tests/script-based-pre/no_codegen_error/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index e98c08d7d2a3..93b0b727f030 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,8 @@ exclude = [ "tests/script-based-pre/build-cache-dirty/target/new_dep", "tests/script-based-pre/verify_std_cmd/tmp_dir/target/kani_verify_std", "tests/script-based-pre/kani_lib_dep", + "tests/script-based-pre/no_codegen", + "tests/script-based-pre/no_codegen_error", ] [workspace.lints.clippy] diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index fb18504c6182..76fbc626d113 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -242,6 +242,11 @@ pub struct VerificationArgs { #[arg(long, hide_short_help = true)] pub only_codegen: bool, + /// Run Kani without codegen. Useful for quick feedback on whether the code would compile successfully (similar to `cargo check`). + /// This feature is unstable and requires `-Z unstable-options` to be used + #[arg(long, hide_short_help = true)] + pub no_codegen: bool, + /// Specify the value used for loop unwinding in CBMC #[arg(long)] pub default_unwind: Option, @@ -660,6 +665,12 @@ impl ValidateArgs for VerificationArgs { UnstableFeature::UnstableOptions, )?; + self.common_args.check_unstable( + self.no_codegen, + "--no-codegen", + UnstableFeature::UnstableOptions, + )?; + self.common_args.check_unstable( self.jobs.is_some(), "--jobs", diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index a24028dd84ba..4c049ca3196b 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -182,6 +182,11 @@ impl KaniSession { .map(OsString::from), ); + if self.args.no_codegen { + flags.push("-Z".into()); + flags.push("no-codegen".into()); + } + if let Some(seed_opt) = self.args.randomize_layout { flags.push("-Z".into()); flags.push("randomize-layout".into()); diff --git a/kani-driver/src/project.rs b/kani-driver/src/project.rs index 2a1aad2c1eff..72c407cb7048 100644 --- a/kani-driver/src/project.rs +++ b/kani-driver/src/project.rs @@ -6,7 +6,7 @@ use crate::metadata::from_json; use crate::session::KaniSession; -use crate::util::crate_name; +use crate::util::{crate_name, info_operation}; use anyhow::{Context, Result}; use kani_metadata::{ ArtifactType, ArtifactType::*, HarnessMetadata, KaniMetadata, artifact::convert_type, @@ -177,6 +177,13 @@ impl Artifact { /// be collected from the project. pub fn cargo_project(session: &mut KaniSession, keep_going: bool) -> Result { let outputs = session.cargo_build(keep_going)?; + if session.args.no_codegen { + info_operation( + "info:", + "Compilation succeeded up until codegen. Skipping codegen because of `--no-codegen` option. Rerun without `--no-codegen` to perform codegen.", + ); + return Ok(Project::default()); + } let outdir = outputs.outdir.canonicalize()?; // For the MIR Linker we know there is only one metadata per crate. Use that in our favor. let metadata = diff --git a/tests/script-based-pre/no_codegen/Cargo.toml b/tests/script-based-pre/no_codegen/Cargo.toml new file mode 100644 index 000000000000..330c69766800 --- /dev/null +++ b/tests/script-based-pre/no_codegen/Cargo.toml @@ -0,0 +1,9 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +[package] +name = "no_codegen" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/tests/script-based-pre/no_codegen/config.yml b/tests/script-based-pre/no_codegen/config.yml new file mode 100644 index 000000000000..cd1a72e5f000 --- /dev/null +++ b/tests/script-based-pre/no_codegen/config.yml @@ -0,0 +1,5 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: run.sh +expected: expected +exit_code: 0 diff --git a/tests/script-based-pre/no_codegen/expected b/tests/script-based-pre/no_codegen/expected new file mode 100644 index 000000000000..609978a7272c --- /dev/null +++ b/tests/script-based-pre/no_codegen/expected @@ -0,0 +1 @@ +info: Compilation succeeded up until codegen. Skipping codegen because of `--no-codegen` option. Rerun without `--no-codegen` to perform codegen. diff --git a/tests/script-based-pre/no_codegen/run.sh b/tests/script-based-pre/no_codegen/run.sh new file mode 100755 index 000000000000..01a6a5e2787b --- /dev/null +++ b/tests/script-based-pre/no_codegen/run.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +rm -rf target + +# Test the behavior of the `--no-codegen` option +cargo kani --no-codegen -Zunstable-options + +# Ensure no goto binaries (*.out) are generated +[[ -z $(find target -name "*.out") ]] || { + echo "ERROR: Found goto binaries (*.out) in target directory" + exit 1 +} diff --git a/tests/script-based-pre/no_codegen/src/main.rs b/tests/script-based-pre/no_codegen/src/main.rs new file mode 100644 index 000000000000..7757baddc225 --- /dev/null +++ b/tests/script-based-pre/no_codegen/src/main.rs @@ -0,0 +1,14 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This test checks the behavior of Kani's `--no-codegen` option + +#[kani::proof] +fn main() { + let x: u8 = kani::any(); + if x < 100 { + assert!(x < 101); + } else { + assert!(x > 99); + } +} diff --git a/tests/script-based-pre/no_codegen_error/Cargo.toml b/tests/script-based-pre/no_codegen_error/Cargo.toml new file mode 100644 index 000000000000..b44424869d11 --- /dev/null +++ b/tests/script-based-pre/no_codegen_error/Cargo.toml @@ -0,0 +1,9 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +[package] +name = "no_codegen_error" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/tests/script-based-pre/no_codegen_error/config.yml b/tests/script-based-pre/no_codegen_error/config.yml new file mode 100644 index 000000000000..482747bf1e99 --- /dev/null +++ b/tests/script-based-pre/no_codegen_error/config.yml @@ -0,0 +1,5 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: run.sh +expected: expected +exit_code: 1 diff --git a/tests/script-based-pre/no_codegen_error/expected b/tests/script-based-pre/no_codegen_error/expected new file mode 100644 index 000000000000..09089fc5b16b --- /dev/null +++ b/tests/script-based-pre/no_codegen_error/expected @@ -0,0 +1 @@ +could not compile `no_codegen_error` diff --git a/tests/script-based-pre/no_codegen_error/run.sh b/tests/script-based-pre/no_codegen_error/run.sh new file mode 100755 index 000000000000..c00403549aa7 --- /dev/null +++ b/tests/script-based-pre/no_codegen_error/run.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +rm -rf target + +# Test the behavior of the `--no-codegen` option +cargo kani --no-codegen -Zunstable-options diff --git a/tests/script-based-pre/no_codegen_error/src/main.rs b/tests/script-based-pre/no_codegen_error/src/main.rs new file mode 100644 index 000000000000..2afa9b9e747b --- /dev/null +++ b/tests/script-based-pre/no_codegen_error/src/main.rs @@ -0,0 +1,13 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This test checks the behavior of Kani's `--no-codegen` option when the crate +//! has compilation errors + +#[kani::proof] +fn main() { + let x: i32 = 5; + // Error: different types + let y: u32 = x; + assert_eq!(y, 5); +} From e2d9c3b7ef5532258dcde757833c099d19d571dc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Apr 2025 00:08:39 +0200 Subject: [PATCH 094/161] Automatic toolchain upgrade to nightly-2025-04-07 (#4006) Update Rust toolchain from nightly-2025-04-06 to nightly-2025-04-07 without any other source changes. Co-authored-by: carolynzech <71352687+carolynzech@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 3eab2efec586..c1527c24328a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-04-06" +channel = "nightly-2025-04-07" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From c87c7d09d4e6f56df87233b5ea225e55c9759c1f Mon Sep 17 00:00:00 2001 From: thanhnguyen-aws Date: Fri, 11 Apr 2025 11:05:50 -0700 Subject: [PATCH 095/161] Add support for loop-contract historic values (#3951) Add support for `old` and `prev` to refers to historic values in loop contracts where - `old(expr)` refers to the value of the expr before the loop. - `prev(expr)` refers to the value of the expr at the previous iteration. Resolves #3697 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../src/sysroot/loop_contracts/mod.rs | 300 +++++++++++++++++- .../loop-contract/loop_with_old.expected | 5 + tests/expected/loop-contract/loop_with_old.rs | 24 ++ .../loop_with_old_and_prev.expected | 6 + .../loop-contract/loop_with_old_and_prev.rs | 22 ++ .../loop-contract/loop_with_prev.expected | 5 + .../expected/loop-contract/loop_with_prev.rs | 24 ++ .../loop_with_prev_break_first_iter.expected | 3 + .../loop_with_prev_break_first_iter.rs | 22 ++ 9 files changed, 406 insertions(+), 5 deletions(-) create mode 100644 tests/expected/loop-contract/loop_with_old.expected create mode 100644 tests/expected/loop-contract/loop_with_old.rs create mode 100644 tests/expected/loop-contract/loop_with_old_and_prev.expected create mode 100644 tests/expected/loop-contract/loop_with_old_and_prev.rs create mode 100644 tests/expected/loop-contract/loop_with_prev.expected create mode 100644 tests/expected/loop-contract/loop_with_prev.rs create mode 100644 tests/expected/loop-contract/loop_with_prev_break_first_iter.expected create mode 100644 tests/expected/loop-contract/loop_with_prev_break_first_iter.rs diff --git a/library/kani_macros/src/sysroot/loop_contracts/mod.rs b/library/kani_macros/src/sysroot/loop_contracts/mod.rs index 01166af3d988..844ca7be8165 100644 --- a/library/kani_macros/src/sysroot/loop_contracts/mod.rs +++ b/library/kani_macros/src/sysroot/loop_contracts/mod.rs @@ -9,8 +9,72 @@ use proc_macro_error2::abort_call_site; use quote::{format_ident, quote}; use syn::spanned::Spanned; use syn::token::AndAnd; -use syn::{BinOp, Expr, ExprBinary, Stmt}; +use syn::{BinOp, Block, Expr, ExprBinary, Ident, Stmt, visit_mut::VisitMut}; +/* +Transform the loop to support on_entry(expr) : the value of expr before entering the loop +1. For each on_entry(expr) in the loop variant, replace it with a newly generated "memory" variable old_k +2. Add the declaration of i before the loop: let old_k = expr +For example: +#[kani::loop_invariant(on_entry(x+y) = x + y -1)] +while(....) + +is transformed into +let old_1 = x + y +#[kani::loop_invariant(old_1 = x + y -1)] +while(....) + +Then the loop_invartiant is transformed + +*/ + +/* +Transform the loop to support prev(expr) : the value of expr at the end of the previous iteration +Semantic: If the loop has at least 1 iteration: prev(expr) is the value of expr at the end of the previous iteration. Otherwise, just remove the loop (without check for the invariant too). + +Transformation: basically, if the loop has at least 1 iteration (loop_quard is satisfied at the beginning), we unfold the loop once, declare the variables for prev values and update them at the beginning of the loop body. +Otherwise, we remove the loop. +If there is a prev(expr) in the loop_invariant: +1. Firstly, add an if block whose condition is the loop_quard, inside its body add/do the followings: +2. For each prev(expr) in the loop variant, replace it with a newly generated "memory" variable prev_k +3. Add the declaration of prev_k before the loop: let mut prev_k = expr +4. Define a mut closure whose body is exactly the loop body, but replace all continue/break statements with return true/false statements, + then add a final return true statement at the end of it +5. Add an if statement with condition to be the that closure's call (the same as run the loop once): + True block: add the loop with expanded macros (see next section) and inside the loop body: + add the assignment statements (exactly the same as the declarations without the "let mut") on the top to update the "memory" variables + Else block: Add the assertion for the loop_invariant (not includes the loop_quard): check if the loop_invariant holds after the first iteration. + +For example: +#[kani::loop_invariant(prev(x+y) = x + y -1 && ...)] +while(loop_guard) +{ + loop_body +} + +is transformed into + +assert!(loop_guard); +let mut prev_1 = x + y; +let mut loop_body_closure = || { + loop_body_replaced //replace breaks/continues in loop_body with returns +}; +if loop_body_closure(){ + #[kani::loop_invariant(prev_1 = x + y -1)] + while(loop_guard) + { + prev_1 = x + y; + loop_body + } +} +else{ + assert!(prev_1 = x + y -1 && ...); +} + + +*/ + +/// After that: /// Expand loop contracts macros. /// /// A while loop of the form @@ -30,6 +94,152 @@ use syn::{BinOp, Expr, ExprBinary, Stmt}; /// body /// } /// ``` + +struct TransformationResult { + transformed_expr: Expr, + declarations_block: Block, + assignments_block: Block, +} + +struct CallReplacer { + old_name: String, + replacements: Vec<(Expr, proc_macro2::Ident)>, + counter: usize, + var_prefix: String, +} + +// This impl replaces any function call of a function name : old_name with a newly generated variable. +impl CallReplacer { + fn new(old_name: &str, var_prefix: String) -> Self { + Self { + old_name: old_name.to_string(), + replacements: Vec::new(), + counter: 0, + var_prefix: var_prefix, + } + } + + fn generate_var_name(&mut self) -> proc_macro2::Ident { + let var_name = format_ident!("{}_{}", self.var_prefix, self.counter); + self.counter += 1; + var_name + } + + //Check if the function name is old_name + fn should_replace(&self, expr_path: &syn::ExprPath) -> bool { + let full_path = expr_path + .path + .segments + .iter() + .map(|seg| seg.ident.to_string()) + .collect::>() + .join("::"); + + full_path == self.old_name + } +} + +impl VisitMut for CallReplacer { + fn visit_expr_mut(&mut self, expr: &mut Expr) { + // Visit nested expressions first + syn::visit_mut::visit_expr_mut(self, expr); + + if let Expr::Call(call) = expr { + if let Expr::Path(expr_path) = &*call.func { + if self.should_replace(expr_path) { + let new_var = self.generate_var_name(); + self.replacements.push((expr.clone(), new_var.clone())); + *expr = syn::parse_quote!(#new_var); + } + } + } + } +} + +// The main function to replace the function call with the variables +fn transform_function_calls( + expr: Expr, + function_name: &str, + var_prefix: String, +) -> TransformationResult { + let mut replacer = CallReplacer::new(function_name, var_prefix); + let mut transformed_expr = expr; + replacer.visit_expr_mut(&mut transformed_expr); + + let mut newreplace: Vec<(Expr, Ident)> = Vec::new(); + for (call, var) in replacer.replacements { + match call { + Expr::Call(call_expr) => { + let insideexpr = call_expr.args[0].clone(); + newreplace.push((insideexpr, var.clone())); + } + _ => {} + } + } + + // Generate declarations block of the newly generated variables (will added before the loop) + let declarations: Vec = newreplace + .iter() + .map(|(call, var)| syn::parse_quote!(let mut #var = #call.clone();)) + .collect(); + let declarations_block: Block = syn::parse_quote!({ + #(#declarations)* + }); + + // Generate declarations block of the newly generated variables (will be added on the loop of the loop body) + let assignments: Vec = newreplace + .into_iter() + .map(|(call, var)| syn::parse_quote!(#var = #call.clone();)) + .collect(); + let assignments_block: Block = syn::parse_quote!({ + #(#assignments)* + }); + + TransformationResult { transformed_expr, declarations_block, assignments_block } +} + +struct BreakContinueReplacer; + +impl VisitMut for BreakContinueReplacer { + fn visit_expr_mut(&mut self, expr: &mut Expr) { + // Visit nested expressions first + syn::visit_mut::visit_expr_mut(self, expr); + + // Replace the expression + *expr = match expr { + Expr::Break(_) => { + syn::parse_quote!(return (false, None)) + } + Expr::Continue(_) => { + syn::parse_quote!(return (true, None)) + } + Expr::Return(rexpr) => match rexpr.expr.clone() { + Some(ret) => syn::parse_quote!(return (false, Some(#ret))), + _ => syn::parse_quote!(return (false, Some(()))), + }, + _ => return, + }; + } +} + +// This function replace the break/continue statements inside a loop body with return statements +fn transform_break_continue(block: &mut Block) { + let mut replacer = BreakContinueReplacer; + replacer.visit_block_mut(block); + let return_stmt: Stmt = syn::parse_quote! { + return (true, None); + }; + // Add semicolon to the last statement if it's an expression without semicolon + if let Some(last_stmt) = block.stmts.last_mut() { + if let Stmt::Expr(expr, ref mut semi) = last_stmt { + if semi.is_none() { + *semi = Some(Default::default()); + } + } + } + block.stmts.push(return_stmt); +} + pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { // parse the stmt of the loop let mut loop_stmt: Stmt = syn::parse(item.clone()).unwrap(); @@ -41,7 +251,53 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { inv_name.push_str(&loop_id); // expr of the loop invariant - let inv_expr: Expr = syn::parse(attr).unwrap(); + let mut inv_expr: Expr = syn::parse(attr).unwrap(); + + // adding on_entry variables + let mut onentry_var_prefix: String = "__kani_onentry_var".to_owned(); + onentry_var_prefix.push_str(&loop_id); + let replace_onentry: TransformationResult = + transform_function_calls(inv_expr.clone(), "on_entry", onentry_var_prefix); + inv_expr = replace_onentry.transformed_expr.clone(); + let onentry_decl_stms = replace_onentry.declarations_block.stmts.clone(); + + // adding prev variables + let mut prev_var_prefix: String = "__kani_prev_var".to_owned(); + prev_var_prefix.push_str(&loop_id); + let transform_inv: TransformationResult = + transform_function_calls(inv_expr.clone(), "prev", prev_var_prefix); + let has_prev = !transform_inv.declarations_block.stmts.is_empty(); + let prev_decl_stms = transform_inv.declarations_block.stmts.clone(); + let mut assign_stms = transform_inv.assignments_block.stmts.clone(); + let (mut loop_body, loop_guard) = match loop_stmt { + Stmt::Expr(ref mut e, _) => match e { + Expr::While(ew) => (ew.body.clone(), ew.cond.clone()), + _ => panic!(), + }, + _ => panic!(), + }; + let loop_body_stms = loop_body.stmts.clone(); + assign_stms.extend(loop_body_stms); + transform_break_continue(&mut loop_body); + let mut loop_body_closure_name: String = "__kani_loop_body_closure".to_owned(); + loop_body_closure_name.push_str(&loop_id); + let loop_body_closure = format_ident!("{}", loop_body_closure_name); + let mut loop_body_closure_ret_1_name: String = "__kani_loop_body_closure_ret_1".to_owned(); + loop_body_closure_ret_1_name.push_str(&loop_id); + let loop_body_closure_ret_1 = format_ident!("{}", loop_body_closure_ret_1_name); + let mut loop_body_closure_ret_2_name: String = "__kani_loop_body_closure_ret_2".to_owned(); + loop_body_closure_ret_2_name.push_str(&loop_id); + let loop_body_closure_ret_2 = format_ident!("{}", loop_body_closure_ret_2_name); + if has_prev { + inv_expr = transform_inv.transformed_expr.clone(); + match loop_stmt { + Stmt::Expr(ref mut e, _) => match e { + Expr::While(ew) => ew.body.stmts = assign_stms.clone(), + _ => panic!(), + }, + _ => panic!(), + }; + } // ident of the register function let mut register_name: String = "kani_register_loop_contract".to_owned(); @@ -74,8 +330,40 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { note = "for now, loop contracts is only supported for while-loops."; ), } - quote!( + + if has_prev { + quote!( { + if (#loop_guard) { + #(#onentry_decl_stms)* + #(#prev_decl_stms)* + let mut #loop_body_closure = || + #loop_body; + let (#loop_body_closure_ret_1, #loop_body_closure_ret_2) = #loop_body_closure (); + if #loop_body_closure_ret_2.is_some() { + return #loop_body_closure_ret_2.unwrap(); + } + if #loop_body_closure_ret_1 { + // Dummy function used to force the compiler to capture the environment. + // We cannot call closures inside constant functions. + // This function gets replaced by `kani::internal::call_closure`. + #[inline(never)] + #[kanitool::fn_marker = "kani_register_loop_contract"] + const fn #register_ident bool>(_f: &F, _transformed: usize) -> bool { + true + } + #loop_stmt + } + else { + assert!(#inv_expr); + }; + } + }) + .into() + } else { + quote!( + { + #(#onentry_decl_stms)* // Dummy function used to force the compiler to capture the environment. // We cannot call closures inside constant functions. // This function gets replaced by `kani::internal::call_closure`. @@ -84,8 +372,10 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { const fn #register_ident bool>(_f: &F, _transformed: usize) -> bool { true } - #loop_stmt}) - .into() + #loop_stmt + }) + .into() + } } fn generate_unique_id_from_span(stmt: &Stmt) -> String { diff --git a/tests/expected/loop-contract/loop_with_old.expected b/tests/expected/loop-contract/loop_with_old.expected new file mode 100644 index 000000000000..33035940db68 --- /dev/null +++ b/tests/expected/loop-contract/loop_with_old.expected @@ -0,0 +1,5 @@ +loop_with_old.loop_invariant_base.1\ + - Status: SUCCESS\ + - Description: "Check invariant before entry for loop loop_with_old.0" + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/loop-contract/loop_with_old.rs b/tests/expected/loop-contract/loop_with_old.rs new file mode 100644 index 000000000000..32371b926831 --- /dev/null +++ b/tests/expected/loop-contract/loop_with_old.rs @@ -0,0 +1,24 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: -Z loop-contracts + +//! Check the use of "old" in loop invariant + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + +#[kani::proof] +pub fn loop_with_old() { + let mut x: u8 = kani::any_where(|v| *v < 10); + let mut y: u8 = kani::any(); + let mut i = 0; + #[kani::loop_invariant( (i<=5) && (x <= on_entry(x) + i) && (on_entry(x) + i == on_entry(i) + x))] + while i < 5 { + if i == 0 { + y = x + } + x += 1; + i += 1; + } +} diff --git a/tests/expected/loop-contract/loop_with_old_and_prev.expected b/tests/expected/loop-contract/loop_with_old_and_prev.expected new file mode 100644 index 000000000000..3d74c06488b8 --- /dev/null +++ b/tests/expected/loop-contract/loop_with_old_and_prev.expected @@ -0,0 +1,6 @@ +loop_with_old_and_prev.loop_invariant_base.1\ + - Status: SUCCESS\ + - Description: "Check invariant before entry for loop loop_with_old_and_prev.0"\ + + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/loop-contract/loop_with_old_and_prev.rs b/tests/expected/loop-contract/loop_with_old_and_prev.rs new file mode 100644 index 000000000000..87273c8d79df --- /dev/null +++ b/tests/expected/loop-contract/loop_with_old_and_prev.rs @@ -0,0 +1,22 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: -Z loop-contracts + +//! Check the use of both "old" and "prev" in loop invariant + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + +#[kani::proof] +pub fn loop_with_old_and_prev() { + let mut i = 100; + #[kani::loop_invariant((i >= 2) && (i <= 100) && (i % 2 == 0) && (on_entry(i) == 100) && (prev(i) == i + 2))] + while i > 2 { + if i == 1 { + break; + } + i = i - 2; + } + assert!(i == 2); +} diff --git a/tests/expected/loop-contract/loop_with_prev.expected b/tests/expected/loop-contract/loop_with_prev.expected new file mode 100644 index 000000000000..1ff35b1c3e37 --- /dev/null +++ b/tests/expected/loop-contract/loop_with_prev.expected @@ -0,0 +1,5 @@ +loop_with_prev.loop_invariant_step.1\ + - Status: SUCCESS\ + - Description: "Check invariant after step for loop loop_with_prev.0" + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/loop-contract/loop_with_prev.rs b/tests/expected/loop-contract/loop_with_prev.rs new file mode 100644 index 000000000000..d64e766a4abf --- /dev/null +++ b/tests/expected/loop-contract/loop_with_prev.rs @@ -0,0 +1,24 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: -Z loop-contracts + +//! Check the use of "prev" in loop invariant + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + +#[kani::proof] +pub fn loop_with_prev() { + let mut i = 100; + let mut j = 100; + #[kani::loop_invariant((i >= 2) && (i <= 100) && (i % 2 == 0) && (j == 2*i-100) && (prev(i) == i + 2) && (prev(j) == j + 4) && (prev(i-j) == i-j-2) )] + while i > 2 { + if i == 1 { + break; + } + i = i - 2; + j = j - 4 + } + assert!(i == 2); +} diff --git a/tests/expected/loop-contract/loop_with_prev_break_first_iter.expected b/tests/expected/loop-contract/loop_with_prev_break_first_iter.expected new file mode 100644 index 000000000000..68dfcd51d085 --- /dev/null +++ b/tests/expected/loop-contract/loop_with_prev_break_first_iter.expected @@ -0,0 +1,3 @@ +Failed Checks: assertion failed: (i >= 2) && (i <= 100) && (__kani_prev_var_ + +VERIFICATION:- FAILED diff --git a/tests/expected/loop-contract/loop_with_prev_break_first_iter.rs b/tests/expected/loop-contract/loop_with_prev_break_first_iter.rs new file mode 100644 index 000000000000..87cab0426373 --- /dev/null +++ b/tests/expected/loop-contract/loop_with_prev_break_first_iter.rs @@ -0,0 +1,22 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: -Z loop-contracts + +//! Check if loop contracts is correctly applied. + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + +#[kani::proof] +pub fn loop_with_prev() { + let mut i = 100; + #[kani::loop_invariant((i >= 2) && (i <= 100) && (prev(i) == i + 2))] + while i > 2 { + if i == 100 { + break; + } + i = i - 2; + } + assert!(i == 2); +} From 59b25fd511363f19286a058079b74633f4e4c90e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 09:41:54 -0700 Subject: [PATCH 096/161] Automatic cargo update to 2025-04-14 (#4013) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 44ccca756440..88b7e64ba579 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,9 +101,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "arrayvec" @@ -165,9 +165,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", "regex-automata 0.4.9", @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.18" +version = "1.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" +checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" dependencies = [ "shlex", ] @@ -276,9 +276,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.35" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" +checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" dependencies = [ "clap_builder", "clap_derive", @@ -286,9 +286,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.35" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" +checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" dependencies = [ "anstream", "anstyle", @@ -794,9 +794,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.5" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260" +checksum = "e5ad87c89110f55e4cd4dc2893a9790820206729eaf221555f742d540b0724a0" dependencies = [ "jiff-static", "log", @@ -807,9 +807,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.5" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c" +checksum = "d076d5b64a7e2fe6f0743f02c43ca4a6725c0f904203bfe276a5b3e793103605" dependencies = [ "proc-macro2", "quote", @@ -962,9 +962,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "lock_api" @@ -1020,9 +1020,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -1467,7 +1467,7 @@ dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.9.3", + "linux-raw-sys 0.9.4", "windows-sys 0.59.0", ] @@ -2083,13 +2083,13 @@ dependencies = [ [[package]] name = "which" -version = "7.0.2" +version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2774c861e1f072b3aadc02f8ba886c26ad6321567ecc294c935434cad06f1283" +checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" dependencies = [ "either", "env_home", - "rustix 0.38.44", + "rustix 1.0.5", "winsafe", ] @@ -2208,9 +2208,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" dependencies = [ "memchr", ] From 946263e1284e6a91c84d4361ccaaa6571e1613bf Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Mon, 14 Apr 2025 16:45:48 -0400 Subject: [PATCH 097/161] Clarify Rust intrinsic assumption error message (#4015) "assumption failed" is mysterious--the user will likely conclude that a `kani::assume` failed, when in fact this is an assume introduced by the Rust compiler to perform optimizations. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs | 2 +- kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs | 5 ++++- tests/expected/any_vec/exact_length.expected | 4 ++-- tests/expected/any_vec/out_bounds.expected | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 88f4763be49e..bf3671bb3a16 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -299,7 +299,7 @@ impl GotocCtx<'_> { Intrinsic::Assume => self.codegen_assert_assume( fargs.remove(0).cast_to(Type::bool()), PropertyClass::Assume, - "assumption failed", + "Rust intrinsic assumption failed", loc, ), Intrinsic::AtomicAnd(_) => codegen_atomic_binop!(bitand), diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 0ffa893056be..788e5f6ec31d 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -151,12 +151,15 @@ impl GotocCtx<'_> { let farg_types = operands.map(|op| self.operand_ty_stable(&op)); self.codegen_copy("copy_nonoverlapping", true, fargs, &farg_types, None, location) } + // https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/mir/enum.NonDivergingIntrinsic.html#variant.Assume + // Informs the optimizer that a condition is always true. + // If the condition is false, the behavior is undefined. StatementKind::Intrinsic(NonDivergingIntrinsic::Assume(ref op)) => { let cond = self.codegen_operand_stable(op).cast_to(Type::bool()); self.codegen_assert_assume( cond, PropertyClass::Assume, - "assumption failed", + "Rust intrinsic assumption failed", location, ) } diff --git a/tests/expected/any_vec/exact_length.expected b/tests/expected/any_vec/exact_length.expected index f64d2830a7b8..7191e2175f0a 100644 --- a/tests/expected/any_vec/exact_length.expected +++ b/tests/expected/any_vec/exact_length.expected @@ -1,11 +1,11 @@ Checking harness check_access_length_17... -Failed Checks: assumption failed\ +Failed Checks: Rust intrinsic assumption failed\ in >::get_unchecked Checking harness check_access_length_zero... -Failed Checks: assumption failed\ +Failed Checks: Rust intrinsic assumption failed\ in >::get_unchecked Verification failed for - check_access_length_17 diff --git a/tests/expected/any_vec/out_bounds.expected b/tests/expected/any_vec/out_bounds.expected index 24121aee4ee8..b457127fec4c 100644 --- a/tests/expected/any_vec/out_bounds.expected +++ b/tests/expected/any_vec/out_bounds.expected @@ -1,6 +1,6 @@ Checking harness check_always_out_bounds... -Failed Checks: assumption failed +Failed Checks: Rust intrinsic assumption failed in >::get_unchecked Verification failed for - check_always_out_bounds From 7a226f4b0e1a6d9e9047a9b5af03dce9816a2e0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 23:19:30 +0200 Subject: [PATCH 098/161] Bump tests/perf/s2n-quic from `9f7e0a9` to `0413d9a` (#4014) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `9f7e0a9` to `0413d9a`.
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 9f7e0a9e5b1a..0413d9a618b9 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 9f7e0a9e5b1a6b918b7436a2905621e56d05a388 +Subproject commit 0413d9a618b9dfe53dd7ed3539a24f940a15e7af From dc9d0abb56411cb3bab05c5a1e419790e536e3dc Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Mon, 14 Apr 2025 18:29:00 -0400 Subject: [PATCH 099/161] Autoharness: enable function-contracts and loop-contracts features by default (#4016) ## Problem Right now, `-Z autoharness` implies `-Z function-contracts`, since it will generate contract harnesses for contracts it finds without you explicitly opting in. It does not, however, imply `-Z loop-contracts`, so if you don't provide that flag you can unwind until you hit the default loop bound, and you don't prove the contract. This behavior is inconsistent and confusing. ## Solution Autoharness is already unstable, so it feels redundant to make people opt in to instability on top of instability. Instead, just make `-Z autoharness` imply `-Z function-contracts` and `-Z loop-contracts`. ## Alternative Alternatively, we could make `-Z autoharness` imply neither of these options, and skip functions with contracts altogether / generate standard harnesses for them. But I fear even with warnings, users wouldn't notice we did this, and erroneously conclude that we've proven their contracts. If they're already opting into the unstable autoharness feature, I think we can safely assume they are okay with instability and turn `-Z function-contracts` and `-Z loop-contracts` on by default. Towards https://github.com/model-checking/kani/issues/3832 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- docs/src/reference/experimental/autoharness.md | 13 +++++++------ kani-driver/src/args/mod.rs | 4 ++-- kani-driver/src/autoharness/mod.rs | 3 +++ kani_metadata/src/unstable.rs | 9 +++++++++ .../cargo_autoharness_contracts/contracts.sh | 2 +- .../script-based-pre/cargo_autoharness_list/list.sh | 2 +- .../termination_timeout.sh | 2 +- .../termination_unwind.sh | 2 +- 8 files changed, 25 insertions(+), 12 deletions(-) diff --git a/docs/src/reference/experimental/autoharness.md b/docs/src/reference/experimental/autoharness.md index 67f46a9abf47..34d69da13738 100644 --- a/docs/src/reference/experimental/autoharness.md +++ b/docs/src/reference/experimental/autoharness.md @@ -29,14 +29,20 @@ Autoharness: Checking function foo against all possible inputs... ``` -However, if Kani detects that `foo` has a [contract](./contracts.md), it will instead generate a `#[kani::proof_for_contract]` harness and verify the contract: +However, if Kani detects that `foo` has a [function contract](./contracts.md), it will instead generate a `#[kani::proof_for_contract]` harness and verify the contract: ``` Autoharness: Checking function foo's contract against all possible inputs... ``` +Similarly, Kani will detect the presence of [loop contracts](./loop-contracts.md) and verify them. + +Thus, `-Z autoharness` implies `-Z function-contracts` and `-Z loop-contracts`, i.e., opting into the experimental +autoharness feature means that you are also opting into the function contracts and loop contracts features. + Kani generates and runs these harnesses internally—the user only sees the verification results. +### Options The `autoharness` subcommand has options `--include-function` and `--exclude-function` to include and exclude particular functions. These flags look for partial matches against the fully qualified name of a function. @@ -112,8 +118,3 @@ Failed Checks: x and y are equal VERIFICATION:- FAILED ``` - -### Loop Contracts -If a function contains a loop with a loop contract, Kani will detect the presence of a loop contract and verify that contract. -If, however, the loop does not have a contract, then there is currently no way to specify an unwinding bound for the function, meaning that Kani may hang as it tries to unwind the loop. -We recommend using the `--exclude-function` option to exclude any functions that have this issue (or `--harness-timeout` to bail after attempting verification for some amount of time). \ No newline at end of file diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index 76fbc626d113..be198df73c25 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -146,7 +146,7 @@ pub enum StandaloneSubcommand { VerifyStd(Box), /// List contracts and harnesses. List(Box), - /// Scan the input file for functions eligible for automatic (i.e., harness-free) verification and verify them. + /// Create and run harnesses automatically for eligible functions. Implies -Z function-contracts and -Z loop-contracts. Autoharness(Box), } @@ -177,7 +177,7 @@ pub enum CargoKaniSubcommand { /// List contracts and harnesses. List(Box), - /// Scan the crate for functions eligible for automatic (i.e., harness-free) verification and verify them. + /// Create and run harnesses automatically for eligible functions. Implies -Z function-contracts and -Z loop-contracts. Autoharness(Box), } diff --git a/kani-driver/src/autoharness/mod.rs b/kani-driver/src/autoharness/mod.rs index 498a8d3e94fe..15ada39332d3 100644 --- a/kani-driver/src/autoharness/mod.rs +++ b/kani-driver/src/autoharness/mod.rs @@ -7,6 +7,7 @@ use crate::args::Timeout; use crate::args::autoharness_args::{ CargoAutoharnessArgs, CommonAutoharnessArgs, StandaloneAutoharnessArgs, }; +use crate::args::common::UnstableFeature; use crate::call_cbmc::VerificationStatus; use crate::call_single_file::to_rustc_arg; use crate::harness_runner::HarnessResult; @@ -151,6 +152,8 @@ impl KaniSession { /// Enable autoharness mode. pub fn enable_autoharness(&mut self) { self.auto_harness = true; + self.args.common_args.unstable_features.enable_feature(UnstableFeature::FunctionContracts); + self.args.common_args.unstable_features.enable_feature(UnstableFeature::LoopContracts); } /// Add the compiler arguments specific to the `autoharness` subcommand. diff --git a/kani_metadata/src/unstable.rs b/kani_metadata/src/unstable.rs index a901f11b357d..f72c53b91e57 100644 --- a/kani_metadata/src/unstable.rs +++ b/kani_metadata/src/unstable.rs @@ -142,4 +142,13 @@ impl EnabledUnstableFeatures { pub fn contains(&self, feature: UnstableFeature) -> bool { self.enabled_unstable_features.contains(&feature) } + + /// Enable an additional unstable feature. + /// Note that this enables an unstable feature that the user did not pass on the command line, so this function should be called with caution. + /// At time of writing, the only use is to enable -Z function-contracts and -Z loop-contracts when the autoharness subcommand is running. + pub fn enable_feature(&mut self, feature: UnstableFeature) { + if !self.contains(feature) { + self.enabled_unstable_features.push(feature); + } + } } diff --git a/tests/script-based-pre/cargo_autoharness_contracts/contracts.sh b/tests/script-based-pre/cargo_autoharness_contracts/contracts.sh index d4c93fcd828d..34e177be9134 100755 --- a/tests/script-based-pre/cargo_autoharness_contracts/contracts.sh +++ b/tests/script-based-pre/cargo_autoharness_contracts/contracts.sh @@ -2,7 +2,7 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -cargo kani autoharness -Z autoharness -Z function-contracts -Z loop-contracts +cargo kani autoharness -Z autoharness # We expect verification to fail, so the above command will produce an exit status of 1 # However, we don't want the test to fail because of that exit status; we only want it to fail if the expected file doesn't match # So, exit with a status code of 0 explicitly. diff --git a/tests/script-based-pre/cargo_autoharness_list/list.sh b/tests/script-based-pre/cargo_autoharness_list/list.sh index 7deb9a0667cc..bf02eeb0276b 100755 --- a/tests/script-based-pre/cargo_autoharness_list/list.sh +++ b/tests/script-based-pre/cargo_autoharness_list/list.sh @@ -2,4 +2,4 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -cargo kani autoharness -Z autoharness --list -Z list -Z function-contracts +cargo kani autoharness -Z autoharness --list -Z list diff --git a/tests/script-based-pre/cargo_autoharness_termination_timeout/termination_timeout.sh b/tests/script-based-pre/cargo_autoharness_termination_timeout/termination_timeout.sh index 65f949b65ad3..db4a99f8be09 100755 --- a/tests/script-based-pre/cargo_autoharness_termination_timeout/termination_timeout.sh +++ b/tests/script-based-pre/cargo_autoharness_termination_timeout/termination_timeout.sh @@ -2,4 +2,4 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -cargo kani autoharness -Z autoharness -Z function-contracts +cargo kani autoharness -Z autoharness diff --git a/tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.sh b/tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.sh index 78df727def41..b197c0b1e077 100755 --- a/tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.sh +++ b/tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.sh @@ -4,4 +4,4 @@ # Set the timeout to 5m to ensure that the gcd_recursion test gets killed because of the unwind bound # and not because CBMC times out. -cargo kani autoharness -Z autoharness -Z function-contracts --harness-timeout 5m -Z unstable-options +cargo kani autoharness -Z autoharness --harness-timeout 5m -Z unstable-options From 2a528a5478c49ec5fa5229d647d6e54e2e3f0f49 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Mon, 14 Apr 2025 19:40:10 -0400 Subject: [PATCH 100/161] Autoharness: Harness Generation Improvements (#4017) The important changes are: - 37db951c921686a7a5f92833c65e74f745c68eb0, which will allow autoharness to eventually work with modifies clauses. Since we don't generate autoharnesses for functions that take pointers as arguments anyway, this doesn't affect current functionality. - a325fd8270d97adb2e15ffec1cc365a34c597540, which ensures that the `--function` passed to CBMC is the mangled name of the automatic harness intrinsic, not the target function Towards https://github.com/model-checking/kani/issues/3832 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../compiler_interface.rs | 31 +++-- .../src/kani_middle/codegen_units.rs | 13 +- kani-compiler/src/kani_middle/metadata.rs | 7 +- .../src/kani_middle/transform/automatic.rs | 33 +++++- .../src/kani_middle/transform/contracts.rs | 5 +- .../contracts.expected | 74 +++++++----- .../cargo_autoharness_contracts/src/lib.rs | 84 +++++++++++++ .../Cargo.toml | 10 ++ .../config.yml | 4 + .../src/lib.rs | 112 ++++++++++++++++++ .../type-invariant.expected | 110 +++++++++++++++++ .../type-invariant.sh | 9 ++ 12 files changed, 445 insertions(+), 47 deletions(-) create mode 100644 tests/script-based-pre/cargo_autoharness_type_invariant/Cargo.toml create mode 100644 tests/script-based-pre/cargo_autoharness_type_invariant/config.yml create mode 100644 tests/script-based-pre/cargo_autoharness_type_invariant/src/lib.rs create mode 100644 tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.expected create mode 100755 tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.sh diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index 8ed90b2c975e..ef32d99042c1 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -42,8 +42,8 @@ use rustc_session::config::{CrateType, OutputFilenames, OutputType}; use rustc_session::output::out_filename; use rustc_smir::rustc_internal; use rustc_target::spec::PanicStrategy; +use stable_mir::CrateDef; use stable_mir::mir::mono::{Instance, MonoItem}; -use stable_mir::{CrateDef, DefId}; use std::any::Any; use std::collections::BTreeMap; use std::fmt::Write; @@ -212,6 +212,27 @@ impl GotocCodegenBackend { (gcx, items, contract_info) } + + /// Given a contract harness, get the DefId of its target. + /// For manual harnesses, extract it from the #[proof_for_contract] attribute. + /// For automatic harnesses, extract the target from the harness's GenericArgs. + fn target_def_id_for_harness( + &self, + tcx: TyCtxt, + harness: &Instance, + is_automatic_harness: bool, + ) -> Option { + if is_automatic_harness { + let kind = harness.args().0[0].expect_ty().kind(); + let (fn_to_verify_def, _) = kind.fn_def().unwrap(); + let def_id = fn_to_verify_def.def_id(); + let attrs = KaniAttributes::for_def_id(tcx, def_id); + if attrs.has_contract() { Some(rustc_internal::internal(tcx, def_id)) } else { None } + } else { + let harness_attrs = KaniAttributes::for_def_id(tcx, harness.def.def_id()); + harness_attrs.interpret_for_contract_attribute().map(|(_, id, _)| id) + } + } } impl CodegenBackend for GotocCodegenBackend { @@ -275,8 +296,9 @@ impl CodegenBackend for GotocCodegenBackend { for harness in &unit.harnesses { let transformer = BodyTransformation::new(&queries, tcx, &unit); let model_path = units.harness_model_path(*harness).unwrap(); + let is_automatic_harness = units.is_automatic_harness(harness); let contract_metadata = - contract_metadata_for_harness(tcx, harness.def.def_id()); + self.target_def_id_for_harness(tcx, harness, is_automatic_harness); let (gcx, items, contract_info) = self.codegen_items( tcx, &[MonoItem::Fn(*harness)], @@ -453,11 +475,6 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { } } -fn contract_metadata_for_harness(tcx: TyCtxt, def_id: DefId) -> Option { - let attrs = KaniAttributes::for_def_id(tcx, def_id); - attrs.interpret_for_contract_attribute().map(|(_, id, _)| id) -} - fn check_target(session: &Session) { // The requirement below is needed to build a valid CBMC machine model // in function `machine_model_from_session` from diff --git a/kani-compiler/src/kani_middle/codegen_units.rs b/kani-compiler/src/kani_middle/codegen_units.rs index c3b6e45c15b9..7a9f494d997e 100644 --- a/kani-compiler/src/kani_middle/codegen_units.rs +++ b/kani-compiler/src/kani_middle/codegen_units.rs @@ -97,7 +97,7 @@ impl CodegenUnits { chosen: chosen.iter().map(|func| func.name()).collect::>(), skipped, }) - .expect("Initializing the autoharness metdata failed"); + .expect("Initializing the autoharness metadata failed"); let automatic_harnesses = get_all_automatic_harnesses( tcx, @@ -133,6 +133,10 @@ impl CodegenUnits { self.units.iter() } + pub fn is_automatic_harness(&self, harness: &Harness) -> bool { + self.harness_info.get(harness).is_some_and(|md| md.is_automatically_generated) + } + /// We store which instance of modifies was generated. pub fn store_modifies(&mut self, harness_modifies: &[(Harness, AssignsContract)]) { for (harness, modifies) in harness_modifies { @@ -340,7 +344,12 @@ fn get_all_automatic_harnesses( &GenericArgs(vec![GenericArgKind::Type(fn_to_verify.ty())]), ) .unwrap(); - let metadata = gen_automatic_proof_metadata(tcx, &base_filename, &fn_to_verify); + let metadata = gen_automatic_proof_metadata( + tcx, + &base_filename, + &fn_to_verify, + harness.mangled_name(), + ); (harness, metadata) }) .collect::>() diff --git a/kani-compiler/src/kani_middle/metadata.rs b/kani-compiler/src/kani_middle/metadata.rs index 50487ce45714..4a7a0aa07b94 100644 --- a/kani-compiler/src/kani_middle/metadata.rs +++ b/kani-compiler/src/kani_middle/metadata.rs @@ -112,12 +112,13 @@ pub fn gen_contracts_metadata( /// Generate metadata for automatically generated harnesses. /// For now, we just use the data from the function we are verifying; since we only generate one automatic harness per function, /// the metdata from that function uniquely identifies the harness. -/// In future iterations of this feature, we will likely have multiple harnesses for a single function (e.g., for generic functions), +/// TODO: In future iterations of this feature, we will likely have multiple harnesses for a single function (e.g., for generic functions), /// in which case HarnessMetadata will need to change further to differentiate between those harnesses. pub fn gen_automatic_proof_metadata( tcx: TyCtxt, base_name: &Path, fn_to_verify: &Instance, + harness_mangled_name: String, ) -> HarnessMetadata { let def = fn_to_verify.def; let pretty_name = fn_to_verify.name(); @@ -137,8 +138,10 @@ pub fn gen_automatic_proof_metadata( }; HarnessMetadata { + // pretty_name is what gets displayed to the user, and that should be the name of the function being verified, hence using fn_to_verify name pretty_name, - mangled_name, + // We pass --function mangled_name to CBMC to select the entry point, which should be the mangled name of the automatic harness intrinsic + mangled_name: harness_mangled_name, crate_name: def.krate().name, original_file: loc.filename, original_start_line: loc.start_line, diff --git a/kani-compiler/src/kani_middle/transform/automatic.rs b/kani-compiler/src/kani_middle/transform/automatic.rs index 705a8ac26a88..76d21043355a 100644 --- a/kani-compiler/src/kani_middle/transform/automatic.rs +++ b/kani-compiler/src/kani_middle/transform/automatic.rs @@ -7,21 +7,24 @@ //! then transform its body to be a harness for that function. use crate::args::ReachabilityType; +use crate::kani_middle::attributes::KaniAttributes; use crate::kani_middle::codegen_units::CodegenUnit; -use crate::kani_middle::kani_functions::{KaniIntrinsic, KaniModel}; +use crate::kani_middle::kani_functions::{KaniHook, KaniIntrinsic, KaniModel}; use crate::kani_middle::transform::body::{InsertPosition, MutableBody, SourceInstruction}; use crate::kani_middle::transform::{TransformPass, TransformationType}; use crate::kani_queries::QueryDb; use rustc_middle::ty::TyCtxt; +use stable_mir::CrateDef; use stable_mir::mir::mono::Instance; -use stable_mir::mir::{Body, Operand, Place, TerminatorKind}; -use stable_mir::ty::{FnDef, GenericArgKind, GenericArgs}; +use stable_mir::mir::{Body, Mutability, Operand, Place, TerminatorKind}; +use stable_mir::ty::{FnDef, GenericArgKind, GenericArgs, RigidTy, Ty}; use tracing::debug; #[derive(Debug)] pub struct AutomaticHarnessPass { /// The FnDef of KaniModel::Any kani_any: FnDef, + init_contracts_hook: Instance, /// All of the automatic harness Instances that we generated in the CodegenUnits constructor automatic_harnesses: Vec, } @@ -37,6 +40,9 @@ impl AutomaticHarnessPass { let kani_fns = query_db.kani_functions(); let harness_intrinsic = *kani_fns.get(&KaniIntrinsic::AutomaticHarness.into()).unwrap(); let kani_any = *kani_fns.get(&KaniModel::Any.into()).unwrap(); + let init_contracts_hook = *kani_fns.get(&KaniHook::InitContracts.into()).unwrap(); + let init_contracts_hook = + Instance::resolve(init_contracts_hook, &GenericArgs(vec![])).unwrap(); let automatic_harnesses = unit .harnesses .iter() @@ -46,7 +52,7 @@ impl AutomaticHarnessPass { def == harness_intrinsic }) .collect::>(); - Self { kani_any, automatic_harnesses } + Self { kani_any, init_contracts_hook, automatic_harnesses } } } @@ -65,7 +71,7 @@ impl TransformPass for AutomaticHarnessPass { matches!(query_db.args().reachability_analysis, ReachabilityType::AllFns) } - fn transform(&mut self, _tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { debug!(function=?instance.name(), "AutomaticHarnessPass::transform"); if !self.automatic_harnesses.contains(&instance) { @@ -83,6 +89,23 @@ impl TransformPass for AutomaticHarnessPass { harness_body.clear_body(TerminatorKind::Return); let mut source = SourceInstruction::Terminator { bb: 0 }; + // Contract harnesses need a free(NULL) statement, c.f. kani_core::init_contracts(). + let attrs = KaniAttributes::for_def_id(tcx, def.def_id()); + if attrs.has_contract() { + let ret_local = harness_body.new_local( + Ty::from_rigid_kind(RigidTy::Tuple(vec![])), + source.span(harness_body.blocks()), + Mutability::Not, + ); + harness_body.insert_call( + &self.init_contracts_hook, + &mut source, + InsertPosition::Before, + vec![], + Place::from(ret_local), + ); + } + let mut arg_locals = vec![]; // For each argument of `fn_to_verify`, create a nondeterministic value of its type diff --git a/kani-compiler/src/kani_middle/transform/contracts.rs b/kani-compiler/src/kani_middle/transform/contracts.rs index df5e4209e900..f7ca09b93a41 100644 --- a/kani-compiler/src/kani_middle/transform/contracts.rs +++ b/kani-compiler/src/kani_middle/transform/contracts.rs @@ -350,12 +350,11 @@ impl FunctionWithContractPass { && !harness_generic_args.is_empty() { let kind = harness.args().0[0].expect_ty().kind(); - let (def, args) = kind.fn_def().unwrap(); - let fn_to_verify = Instance::resolve(def, &args).unwrap(); + let (fn_to_verify_def, _) = kind.fn_def().unwrap(); // For automatic harnesses, the target is the function to verify, // and stubs are empty. ( - Some(rustc_internal::internal(tcx, fn_to_verify.def.def_id())), + Some(rustc_internal::internal(tcx, fn_to_verify_def.def_id())), HashSet::default(), ) } else { diff --git a/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected b/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected index 7f3b92ffef86..e5aa3a29009a 100644 --- a/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected +++ b/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected @@ -1,17 +1,21 @@ -Kani generated automatic harnesses for 5 function(s): -+--------------------------------+ -| Selected Function | -+================================+ -| should_fail::max | -|--------------------------------| -| should_pass::div | -|--------------------------------| -| should_pass::has_loop_contract | -|--------------------------------| -| should_pass::has_recursion_gcd | -|--------------------------------| -| should_pass::unchecked_mul | -+--------------------------------+ +Kani generated automatic harnesses for 7 function(s): ++---------------------------------------------+ +| Selected Function | ++=============================================+ +| should_fail::max | +|---------------------------------------------| +| should_pass::alignment::Alignment | +|---------------------------------------------| +| should_pass::alignment::Alignment::as_usize | +|---------------------------------------------| +| should_pass::div | +|---------------------------------------------| +| should_pass::has_loop_contract | +|---------------------------------------------| +| should_pass::has_recursion_gcd | +|---------------------------------------------| +| should_pass::unchecked_mul | ++---------------------------------------------+ Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). @@ -37,23 +41,37 @@ arithmetic_overflow\ - Status: SUCCESS\ - Description: "attempt to compute `unchecked_mul` which would overflow" +Autoharness: Checking function should_pass::alignment::Alignment::as_usize's contract against all possible inputs... + +should_pass::alignment::Alignment::as_usize\ + - Status: SUCCESS\ + - Description: "Rust intrinsic assumption failed" + +should_pass::alignment::Alignment::as_usize\ + - Status: SUCCESS\ + - Description: "|result| result.is_power_of_two()" + Manual Harness Summary: No proof harnesses (functions with #[kani::proof]) were found to verify. Autoharness Summary: -+--------------------------------+-----------------------------+---------------------+ -| Selected Function | Kind of Automatic Harness | Verification Result | -+====================================================================================+ -| should_pass::div | #[kani::proof_for_contract] | Success | -|--------------------------------+-----------------------------+---------------------| -| should_pass::has_loop_contract | #[kani::proof] | Success | -|--------------------------------+-----------------------------+---------------------| -| should_pass::has_recursion_gcd | #[kani::proof_for_contract] | Success | -|--------------------------------+-----------------------------+---------------------| -| should_pass::unchecked_mul | #[kani::proof_for_contract] | Success | -|--------------------------------+-----------------------------+---------------------| -| should_fail::max | #[kani::proof_for_contract] | Failure | -+--------------------------------+-----------------------------+---------------------+ ++---------------------------------------------+-----------------------------+---------------------+ +| Selected Function | Kind of Automatic Harness | Verification Result | ++=================================================================================================+ +| should_pass::alignment::Alignment | #[kani::proof] | Success | +|---------------------------------------------+-----------------------------+---------------------| +| should_pass::alignment::Alignment::as_usize | #[kani::proof_for_contract] | Success | +|---------------------------------------------+-----------------------------+---------------------| +| should_pass::div | #[kani::proof_for_contract] | Success | +|---------------------------------------------+-----------------------------+---------------------| +| should_pass::has_loop_contract | #[kani::proof] | Success | +|---------------------------------------------+-----------------------------+---------------------| +| should_pass::has_recursion_gcd | #[kani::proof_for_contract] | Success | +|---------------------------------------------+-----------------------------+---------------------| +| should_pass::unchecked_mul | #[kani::proof_for_contract] | Success | +|---------------------------------------------+-----------------------------+---------------------| +| should_fail::max | #[kani::proof_for_contract] | Failure | ++---------------------------------------------+-----------------------------+---------------------+ Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). -Complete - 4 successfully verified functions, 1 failures, 5 total. +Complete - 6 successfully verified functions, 1 failures, 7 total. diff --git a/tests/script-based-pre/cargo_autoharness_contracts/src/lib.rs b/tests/script-based-pre/cargo_autoharness_contracts/src/lib.rs index c66b3dab4fbe..39bd828c682d 100644 --- a/tests/script-based-pre/cargo_autoharness_contracts/src/lib.rs +++ b/tests/script-based-pre/cargo_autoharness_contracts/src/lib.rs @@ -46,6 +46,90 @@ mod should_pass { unsafe fn unchecked_mul(left: u8, rhs: u8) -> u8 { unsafe { left.unchecked_mul(rhs) } } + + // Check that we can create automatic harnesses for more complex situtations, i.e., + // functions with contracts that reference nested data structures that derive Arbitrary. + mod alignment { + // FIXME: Note that since this is a tuple struct, we generate an extra harness for the Alignment constructor, + // c.f. https://github.com/model-checking/kani/issues/3832#issuecomment-2730580836 + #[derive(kani::Arbitrary)] + pub struct Alignment(AlignmentEnum); + + #[derive(kani::Arbitrary)] + enum AlignmentEnum { + _Align1Shl0 = 1 << 0, + _Align1Shl1 = 1 << 1, + _Align1Shl2 = 1 << 2, + _Align1Shl3 = 1 << 3, + _Align1Shl4 = 1 << 4, + _Align1Shl5 = 1 << 5, + _Align1Shl6 = 1 << 6, + _Align1Shl7 = 1 << 7, + _Align1Shl8 = 1 << 8, + _Align1Shl9 = 1 << 9, + _Align1Shl10 = 1 << 10, + _Align1Shl11 = 1 << 11, + _Align1Shl12 = 1 << 12, + _Align1Shl13 = 1 << 13, + _Align1Shl14 = 1 << 14, + _Align1Shl15 = 1 << 15, + _Align1Shl16 = 1 << 16, + _Align1Shl17 = 1 << 17, + _Align1Shl18 = 1 << 18, + _Align1Shl19 = 1 << 19, + _Align1Shl20 = 1 << 20, + _Align1Shl21 = 1 << 21, + _Align1Shl22 = 1 << 22, + _Align1Shl23 = 1 << 23, + _Align1Shl24 = 1 << 24, + _Align1Shl25 = 1 << 25, + _Align1Shl26 = 1 << 26, + _Align1Shl27 = 1 << 27, + _Align1Shl28 = 1 << 28, + _Align1Shl29 = 1 << 29, + _Align1Shl30 = 1 << 30, + _Align1Shl31 = 1 << 31, + _Align1Shl32 = 1 << 32, + _Align1Shl33 = 1 << 33, + _Align1Shl34 = 1 << 34, + _Align1Shl35 = 1 << 35, + _Align1Shl36 = 1 << 36, + _Align1Shl37 = 1 << 37, + _Align1Shl38 = 1 << 38, + _Align1Shl39 = 1 << 39, + _Align1Shl40 = 1 << 40, + _Align1Shl41 = 1 << 41, + _Align1Shl42 = 1 << 42, + _Align1Shl43 = 1 << 43, + _Align1Shl44 = 1 << 44, + _Align1Shl45 = 1 << 45, + _Align1Shl46 = 1 << 46, + _Align1Shl47 = 1 << 47, + _Align1Shl48 = 1 << 48, + _Align1Shl49 = 1 << 49, + _Align1Shl50 = 1 << 50, + _Align1Shl51 = 1 << 51, + _Align1Shl52 = 1 << 52, + _Align1Shl53 = 1 << 53, + _Align1Shl54 = 1 << 54, + _Align1Shl55 = 1 << 55, + _Align1Shl56 = 1 << 56, + _Align1Shl57 = 1 << 57, + _Align1Shl58 = 1 << 58, + _Align1Shl59 = 1 << 59, + _Align1Shl60 = 1 << 60, + _Align1Shl61 = 1 << 61, + _Align1Shl62 = 1 << 62, + _Align1Shl63 = 1 << 63, + } + + impl Alignment { + #[kani::ensures(|result| result.is_power_of_two())] + pub fn as_usize(self) -> usize { + self.0 as usize + } + } + } } mod should_fail { diff --git a/tests/script-based-pre/cargo_autoharness_type_invariant/Cargo.toml b/tests/script-based-pre/cargo_autoharness_type_invariant/Cargo.toml new file mode 100644 index 000000000000..abcecba4b6c2 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_type_invariant/Cargo.toml @@ -0,0 +1,10 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +[package] +name = "cargo_autoharness_type_invariant" +version = "0.1.0" +edition = "2024" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tests/script-based-pre/cargo_autoharness_type_invariant/config.yml b/tests/script-based-pre/cargo_autoharness_type_invariant/config.yml new file mode 100644 index 000000000000..5762f901d15e --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_type_invariant/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: type-invariant.sh +expected: type-invariant.expected diff --git a/tests/script-based-pre/cargo_autoharness_type_invariant/src/lib.rs b/tests/script-based-pre/cargo_autoharness_type_invariant/src/lib.rs new file mode 100644 index 000000000000..4aca2eef9193 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_type_invariant/src/lib.rs @@ -0,0 +1,112 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Test that the autoharness subcommand respects the invariants specified in the type's kani::any() implementation. +// In other words, test that autoharness is actually finding and using the appropriate kani::any() implementation, +// rather than letting CBMC generate nondetermistic values for the type. +// In this example, we check that the methods that take `Duration` as an argument generate Durations that respect the type invariant that nanos < NANOS_PER_SEC. +// See the "TEST NOTE" inline comments for how we specifically check this. + +// Simplified code from https://github.com/model-checking/verify-rust-std/blob/3f4234a19211e677d51df3061db67477b29af171/library/core/src/time.rs#L1 + +use kani::Invariant; + +const NANOS_PER_SEC: u32 = 1_000_000_000; + +#[derive(kani::Arbitrary)] +#[derive(Clone, Copy)] +pub struct Nanoseconds(u32); + +#[derive(Clone, Copy)] +#[derive(kani::Invariant)] +pub struct Duration { + secs: u64, + nanos: Nanoseconds, +} + +impl kani::Invariant for Nanoseconds { + fn is_safe(&self) -> bool { + self.as_inner() < NANOS_PER_SEC + } +} + +impl Nanoseconds { + #[kani::requires(val < NANOS_PER_SEC)] + #[kani::ensures(|nano| nano.is_safe())] + pub const unsafe fn new_unchecked(val: u32) -> Self { + // SAFETY: caller promises that val < NANOS_PER_SEC + Self(val) + } + + pub const fn as_inner(self) -> u32 { + // SAFETY: This is a transparent wrapper, so unwrapping it is sound + unsafe { core::mem::transmute(self) } + } +} + +impl kani::Arbitrary for Duration { + fn any() -> Self { + let d = Duration { secs: kani::any(), nanos: kani::any() }; + kani::assume(d.is_safe()); + d + } +} + +impl Duration { + // TEST NOTE: the automatic harness for this method fails because it can panic. + #[kani::ensures(|duration| duration.is_safe())] + pub const fn new(secs: u64, nanos: u32) -> Duration { + if nanos < NANOS_PER_SEC { + // SAFETY: nanos < NANOS_PER_SEC, therefore nanos is within the valid range + Duration { secs, nanos: unsafe { Nanoseconds::new_unchecked(nanos) } } + } else { + let secs = secs + .checked_add((nanos / NANOS_PER_SEC) as u64) + .expect("overflow in Duration::new"); + let nanos = nanos % NANOS_PER_SEC; + // SAFETY: nanos % NANOS_PER_SEC < NANOS_PER_SEC, therefore nanos is within the valid range + Duration { secs, nanos: unsafe { Nanoseconds::new_unchecked(nanos) } } + } + } + + pub const fn abs_diff(self, other: Duration) -> Duration { + if let Some(res) = self.checked_sub(other) { res } else { other.checked_sub(self).unwrap() } + } + + #[kani::ensures(|duration| duration.is_none() || duration.unwrap().is_safe())] + pub const fn checked_add(self, rhs: Duration) -> Option { + if let Some(mut secs) = self.secs.checked_add(rhs.secs) { + // TEST NOTE: this addition doesn't overflow iff `self` and `rhs` respect the Duration type invariant + let mut nanos = self.nanos.as_inner() + rhs.nanos.as_inner(); + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; + if let Some(new_secs) = secs.checked_add(1) { + secs = new_secs; + } else { + return None; + } + } + Some(Duration::new(secs, nanos)) + } else { + None + } + } + + #[kani::ensures(|duration| duration.is_none() || duration.unwrap().is_safe())] + pub const fn checked_sub(self, rhs: Duration) -> Option { + if let Some(mut secs) = self.secs.checked_sub(rhs.secs) { + let nanos = if self.nanos.as_inner() >= rhs.nanos.as_inner() { + self.nanos.as_inner() - rhs.nanos.as_inner() + } else if let Some(sub_secs) = secs.checked_sub(1) { + secs = sub_secs; + // TEST NOTE: this arithmetic doesn't overflow iff `self` and `rhs` respect the Duration type invariant + self.nanos.as_inner() + NANOS_PER_SEC - rhs.nanos.as_inner() + } else { + return None; + }; + Some(Duration::new(secs, nanos)) + } else { + None + } + } +} diff --git a/tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.expected b/tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.expected new file mode 100644 index 000000000000..bd15576d5369 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.expected @@ -0,0 +1,110 @@ +Kani generated automatic harnesses for 7 function(s): ++----------------------------+ +| Selected Function | ++============================+ +| Duration::abs_diff | +|----------------------------| +| Duration::checked_add | +|----------------------------| +| Duration::checked_sub | +|----------------------------| +| Duration::new | +|----------------------------| +| Nanoseconds | +|----------------------------| +| Nanoseconds::as_inner | +|----------------------------| +| Nanoseconds::new_unchecked | ++----------------------------+ + +Autoharness: Checking function Nanoseconds::new_unchecked's contract against all possible inputs... +Nanoseconds::new_unchecked\ + - Status: SUCCESS\ + - Description: "|nano| nano.is_safe()" + +Autoharness: Checking function Duration::checked_sub's contract against all possible inputs... +Duration::checked_sub\ + - Status: SUCCESS\ + - Description: "attempt to add with overflow" + +Duration::checked_sub\ + - Status: SUCCESS\ + - Description: "attempt to subtract with overflow" + +Nanoseconds::new_unchecked\ + - Status: SUCCESS\ + - Description: "val < NANOS_PER_SEC" + +Duration::checked_sub\ + - Status: SUCCESS\ + - Description: "|duration| duration.is_none() || duration.unwrap().is_safe()" + +Duration::new\ + - Status: SUCCESS\ + - Description: "|duration| duration.is_safe()" + +Autoharness: Checking function Duration::checked_add's contract against all possible inputs... +Nanoseconds::new_unchecked\ + - Status: SUCCESS\ + - Description: "val < NANOS_PER_SEC" + +Duration::new\ + - Status: SUCCESS\ + - Description: "|duration| duration.is_safe()" + +Duration::checked_add\ + - Status: SUCCESS\ + - Description: "|duration| duration.is_none() || duration.unwrap().is_safe()" + +Duration::checked_add\ + - Status: SUCCESS\ + - Description: "attempt to add with overflow" + +Duration::checked_add\ + - Status: SUCCESS\ + - Description: "attempt to subtract with overflow" + +Autoharness: Checking function Duration::abs_diff against all possible inputs... +Duration::checked_sub\ + - Status: SUCCESS\ + - Description: "attempt to subtract with overflow" + +Duration::checked_sub\ + - Status: SUCCESS\ + - Description: "attempt to subtract with overflow" + +Autoharness: Checking function Duration::new's contract against all possible inputs... +Nanoseconds::new_unchecked\ + - Status: SUCCESS\ + - Description: "val < NANOS_PER_SEC" + +Duration::new\ + - Status: SUCCESS\ + - Description: "|duration| duration.is_safe()" + +std::option::expect_failed\ + - Status: FAILURE\ + - Description: "This is a placeholder message; Kani doesn't support message formatted at runtime" + + +Autoharness Summary: ++----------------------------+-----------------------------+---------------------+ +| Selected Function | Kind of Automatic Harness | Verification Result | ++================================================================================+ +| Duration::abs_diff | #[kani::proof] | Success | +|----------------------------+-----------------------------+---------------------| +| Duration::checked_add | #[kani::proof_for_contract] | Success | +|----------------------------+-----------------------------+---------------------| +| Duration::checked_sub | #[kani::proof_for_contract] | Success | +|----------------------------+-----------------------------+---------------------| +| Nanoseconds | #[kani::proof] | Success | +|----------------------------+-----------------------------+---------------------| +| Nanoseconds::as_inner | #[kani::proof] | Success | +|----------------------------+-----------------------------+---------------------| +| Nanoseconds::new_unchecked | #[kani::proof_for_contract] | Success | +|----------------------------+-----------------------------+---------------------| +| Duration::new | #[kani::proof_for_contract] | Failure | ++----------------------------+-----------------------------+---------------------+ +Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. +If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). +Complete - 6 successfully verified functions, 1 failures, 7 total. diff --git a/tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.sh b/tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.sh new file mode 100755 index 000000000000..34e177be9134 --- /dev/null +++ b/tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +cargo kani autoharness -Z autoharness +# We expect verification to fail, so the above command will produce an exit status of 1 +# However, we don't want the test to fail because of that exit status; we only want it to fail if the expected file doesn't match +# So, exit with a status code of 0 explicitly. +exit 0; From 6acad2fdb07d8f63bc892e8c4c76fedb3709d673 Mon Sep 17 00:00:00 2001 From: thanhnguyen-aws Date: Tue, 15 Apr 2025 07:32:05 -0700 Subject: [PATCH 101/161] Add support for Loop-loops (#4011) This PR adds support for Loop-loops By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../src/sysroot/loop_contracts/mod.rs | 12 +++++++++- .../loop-contract/simple_loop_loop.expected | 6 +++++ .../loop-contract/simple_loop_loop.rs | 22 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/expected/loop-contract/simple_loop_loop.expected create mode 100644 tests/expected/loop-contract/simple_loop_loop.rs diff --git a/library/kani_macros/src/sysroot/loop_contracts/mod.rs b/library/kani_macros/src/sysroot/loop_contracts/mod.rs index 844ca7be8165..8b49c660ef1d 100644 --- a/library/kani_macros/src/sysroot/loop_contracts/mod.rs +++ b/library/kani_macros/src/sysroot/loop_contracts/mod.rs @@ -9,7 +9,7 @@ use proc_macro_error2::abort_call_site; use quote::{format_ident, quote}; use syn::spanned::Spanned; use syn::token::AndAnd; -use syn::{BinOp, Block, Expr, ExprBinary, Ident, Stmt, visit_mut::VisitMut}; +use syn::{BinOp, Block, Expr, ExprBinary, Ident, Stmt, parse_quote, visit_mut::VisitMut}; /* Transform the loop to support on_entry(expr) : the value of expr before entering the loop @@ -272,6 +272,7 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { let (mut loop_body, loop_guard) = match loop_stmt { Stmt::Expr(ref mut e, _) => match e { Expr::While(ew) => (ew.body.clone(), ew.cond.clone()), + Expr::Loop(el) => (el.body.clone(), parse_quote!(true)), _ => panic!(), }, _ => panic!(), @@ -293,6 +294,7 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { match loop_stmt { Stmt::Expr(ref mut e, _) => match e { Expr::While(ew) => ew.body.stmts = assign_stms.clone(), + Expr::Loop(el) => el.body.stmts = assign_stms.clone(), _ => panic!(), }, _ => panic!(), @@ -320,6 +322,14 @@ pub fn loop_invariant(attr: TokenStream, item: TokenStream) -> TokenStream { right: ew.cond.clone(), }); } + Expr::Loop(ref mut el) => { + //let retexpr = get_return_statement(&el.body); + let invstmt: Stmt = syn::parse(quote!(if !(#register_ident(&||->bool{#inv_expr}, 0)) {assert!(false); unreachable!()};).into()).unwrap(); + let mut new_stmts: Vec = Vec::new(); + new_stmts.push(invstmt); + new_stmts.extend(el.body.stmts.clone()); + el.body.stmts = new_stmts.clone(); + } _ => { abort_call_site!("`#[kani::loop_invariant]` is now only supported for while-loops."; note = "for now, loop contracts is only supported for while-loops."; diff --git a/tests/expected/loop-contract/simple_loop_loop.expected b/tests/expected/loop-contract/simple_loop_loop.expected new file mode 100644 index 000000000000..80289982610e --- /dev/null +++ b/tests/expected/loop-contract/simple_loop_loop.expected @@ -0,0 +1,6 @@ +main.loop_invariant_base.1\ + - Status: SUCCESS\ + - Description: "Check invariant before entry for loop main.0"\ +in function main + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/loop-contract/simple_loop_loop.rs b/tests/expected/loop-contract/simple_loop_loop.rs new file mode 100644 index 000000000000..7114342f730b --- /dev/null +++ b/tests/expected/loop-contract/simple_loop_loop.rs @@ -0,0 +1,22 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: -Z loop-contracts + +//! Check if loop-loop invariant is correctly applied. + +#![feature(proc_macro_hygiene)] +#![feature(stmt_expr_attributes)] + +#[kani::proof] +fn main() { + let mut i = 100; + #[kani::loop_invariant(i<=100 && i >= 4 && i % 2 ==0)] + loop { + i = i - 2; + if i == 2 { + break; + } + } + assert!(i == 2); +} From 1495e7392ee2dd7539ebe48e565a8b0b5ad89ae4 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Wed, 16 Apr 2025 11:50:02 -0700 Subject: [PATCH 102/161] Clarify installation instructions (#4023) 1. Remove the reference to Python 2. Clarify what the installation steps do Resolves #4008 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- docs/src/install-guide.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/src/install-guide.md b/docs/src/install-guide.md index d64ad3c89ce0..446b5a9b3c9b 100644 --- a/docs/src/install-guide.md +++ b/docs/src/install-guide.md @@ -14,20 +14,24 @@ GitHub CI workflows, see [GitHub CI Action](./install-github-ci.md). The following must already be installed: -* **Python version 3.7 or newer** and the package installer `pip`. * Rust 1.58 or newer installed via `rustup`. ## Installing the latest version -To install the latest version of Kani, run: +Installing the latest version of Kani is a two step process. +First, download and build Kani's installer package using: ```bash cargo install --locked kani-verifier +``` +This will build and place in `~/.cargo/bin` (in a typical environment) the `kani` and `cargo-kani` binaries. + +Next, run the installer to download and install the prebuilt binaries as well as supporting libraries and data: +```bash cargo kani setup ``` -This will build and place in `~/.cargo/bin` (in a typical environment) the `kani` and `cargo-kani` binaries. -The second step (`cargo kani setup`) will download the Kani compiler and other necessary dependencies, and place them under `~/.kani/` by default. +The second step will download the Kani compiler and other necessary dependencies, and place them under `~/.kani/` by default. A custom path can be specified using the `KANI_HOME` environment variable. ## Installing an older version From 150ccd1e4f3526161346e3770667d77d3d439a6c Mon Sep 17 00:00:00 2001 From: thanhnguyen-aws Date: Wed, 16 Apr 2025 12:47:05 -0700 Subject: [PATCH 103/161] Fix the bug of while loop invariant contains no local variables (#4022) This PR fixes the bug of while loop invariant contains no local variables. Previously, for a while loop, if the loop invariant is `#[kani::loop_invariant(true)]`, we will get an error: "The assumptions for loop-contracts transformation are violated by some other transformation. Please report github.com/model-checking/kani/issues/new?template=bug_report.md" By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Michael Tautschnig --- .../kani_middle/transform/loop_contracts.rs | 20 +++++++++++++------ .../loop_with_true_invariant.expected | 5 +++++ .../loop-contract/loop_with_true_invariant.rs | 18 +++++++++++++++++ 3 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 tests/expected/loop-contract/loop_with_true_invariant.expected create mode 100644 tests/expected/loop-contract/loop_with_true_invariant.rs diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index 323cff742ee7..ca01b7071abf 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -19,7 +19,7 @@ use stable_mir::mir::{ AggregateKind, BasicBlock, BasicBlockIdx, Body, ConstOperand, Operand, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarDebugInfoContents, }; -use stable_mir::ty::{FnDef, MirConst, RigidTy, UintTy}; +use stable_mir::ty::{FnDef, GenericArgKind, MirConst, RigidTy, TyKind, UintTy}; use std::collections::{HashMap, HashSet, VecDeque}; use std::fmt::Debug; @@ -261,7 +261,7 @@ impl LoopContractPass { } = &terminator.kind { // Get the function signature of the terminator call. - let Some(RigidTy::FnDef(fn_def, ..)) = terminator_func + let Some(RigidTy::FnDef(fn_def, genarg)) = terminator_func .ty(new_body.locals()) .ok() .map(|fn_ty| fn_ty.kind().rigid().unwrap().clone()) @@ -286,10 +286,18 @@ impl LoopContractPass { Please report github.com/model-checking/kani/issues/new?template=bug_report.md" ); } - - let ori_condition_bb_idx = - new_body.blocks()[terminator_target.unwrap()].terminator.successors()[1]; - self.make_invariant_closure_alive(new_body, ori_condition_bb_idx); + let GenericArgKind::Type(arg_ty) = genarg.0[0] else { return false }; + let TyKind::RigidTy(RigidTy::Closure(_, genarg)) = arg_ty.kind() else { + return false; + }; + let GenericArgKind::Type(arg_ty) = genarg.0[2] else { return false }; + let TyKind::RigidTy(RigidTy::Tuple(args)) = arg_ty.kind() else { return false }; + // Check if the invariant involves any local variable + if !args.is_empty() { + let ori_condition_bb_idx = + new_body.blocks()[terminator_target.unwrap()].terminator.successors()[1]; + self.make_invariant_closure_alive(new_body, ori_condition_bb_idx); + } contain_loop_contracts = true; diff --git a/tests/expected/loop-contract/loop_with_true_invariant.expected b/tests/expected/loop-contract/loop_with_true_invariant.expected new file mode 100644 index 000000000000..556c39e4d7e8 --- /dev/null +++ b/tests/expected/loop-contract/loop_with_true_invariant.expected @@ -0,0 +1,5 @@ +main.loop_invariant_base.1\ + - Status: SUCCESS\ + - Description: "Check invariant before entry for loop main.0" + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/loop-contract/loop_with_true_invariant.rs b/tests/expected/loop-contract/loop_with_true_invariant.rs new file mode 100644 index 000000000000..7b03acb795b6 --- /dev/null +++ b/tests/expected/loop-contract/loop_with_true_invariant.rs @@ -0,0 +1,18 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: -Z loop-contracts + +//! Check the use of "true" in loop invariant + +#![feature(stmt_expr_attributes)] +#![feature(proc_macro_hygiene)] + +#[kani::proof] +fn main(){ + let mut i = 100; + #[kani::loop_invariant(true)] + while i > 1 { + i /= 2; + } +} From 84cd134bb6353a05ec1edd72447d0981623f862a Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Thu, 17 Apr 2025 10:32:47 -0400 Subject: [PATCH 104/161] List Subcommand: include crate name (#4024) Update the list subcommand to include the crate name as part of the markdown table and printed table output. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- kani-driver/src/list/collect_metadata.rs | 73 +++---- kani-driver/src/list/mod.rs | 30 ++- kani-driver/src/list/output.rs | 199 ++++++++++++------ .../cargo_autoharness_list/list.expected | 26 ++- .../cargo_list_md/list.expected | 22 +- .../kani_list_md/list.expected | 22 +- 6 files changed, 236 insertions(+), 136 deletions(-) diff --git a/kani-driver/src/list/collect_metadata.rs b/kani-driver/src/list/collect_metadata.rs index 5a5ed290dcad..c0d452ee6051 100644 --- a/kani-driver/src/list/collect_metadata.rs +++ b/kani-driver/src/list/collect_metadata.rs @@ -10,69 +10,64 @@ use crate::{ VerificationArgs, list_args::{CargoListArgs, StandaloneListArgs}, }, - list::ListMetadata, list::output::output_list_results, + list::{FileName, HarnessName, ListMetadata}, project::{Project, cargo_project, standalone_project, std_project}, session::KaniSession, version::print_kani_version, }; use anyhow::Result; -use kani_metadata::{ContractedFunction, HarnessKind, KaniMetadata}; +use kani_metadata::{ContractedFunction, HarnessKind, HarnessMetadata, KaniMetadata}; /// Process the KaniMetadata output from kani-compiler and output the list subcommand results -pub fn process_metadata(metadata: Vec) -> ListMetadata { - // We use ordered maps and sets so that the output is in lexicographic order (and consistent across invocations). - - // Map each file to a vector of its harnesses. - let mut standard_harnesses: BTreeMap> = BTreeMap::new(); - let mut contract_harnesses: BTreeMap> = BTreeMap::new(); +pub fn process_metadata(metadata: Vec) -> BTreeSet { + let mut list_metadata: BTreeSet = BTreeSet::new(); + + let insert = |harness_meta: HarnessMetadata, + map: &mut BTreeMap>, + count: &mut usize| { + *count += 1; + if let Some(harnesses) = map.get_mut(&harness_meta.original_file) { + harnesses.insert(harness_meta.pretty_name); + } else { + map.insert(harness_meta.original_file, BTreeSet::from([harness_meta.pretty_name])); + }; + }; - let mut contracted_functions: BTreeSet = BTreeSet::new(); + for kani_meta in metadata { + // We use ordered maps and sets so that the output is in lexicographic order (and consistent across invocations). + let mut standard_harnesses: BTreeMap> = BTreeMap::new(); + let mut contract_harnesses: BTreeMap> = BTreeMap::new(); + let mut contracted_functions: BTreeSet = BTreeSet::new(); - let mut standard_harnesses_count = 0; - let mut contract_harnesses_count = 0; + let mut standard_harnesses_count = 0; + let mut contract_harnesses_count = 0; - for kani_meta in metadata { for harness_meta in kani_meta.proof_harnesses { match harness_meta.attributes.kind { HarnessKind::Proof => { - standard_harnesses_count += 1; - if let Some(harnesses) = standard_harnesses.get_mut(&harness_meta.original_file) - { - harnesses.insert(harness_meta.pretty_name); - } else { - standard_harnesses.insert( - harness_meta.original_file, - BTreeSet::from([harness_meta.pretty_name]), - ); - } + insert(harness_meta, &mut standard_harnesses, &mut standard_harnesses_count); } HarnessKind::ProofForContract { .. } => { - contract_harnesses_count += 1; - if let Some(harnesses) = contract_harnesses.get_mut(&harness_meta.original_file) - { - harnesses.insert(harness_meta.pretty_name); - } else { - contract_harnesses.insert( - harness_meta.original_file, - BTreeSet::from([harness_meta.pretty_name]), - ); - } + insert(harness_meta, &mut contract_harnesses, &mut contract_harnesses_count); } HarnessKind::Test => {} } } contracted_functions.extend(kani_meta.contracted_functions.into_iter()); - } - ListMetadata { - standard_harnesses, - standard_harnesses_count, - contract_harnesses, - contract_harnesses_count, - contracted_functions, + list_metadata.insert(ListMetadata { + crate_name: kani_meta.crate_name, + standard_harnesses, + standard_harnesses_count, + contract_harnesses, + contract_harnesses_count, + contracted_functions, + }); } + + list_metadata } pub fn list_cargo(args: CargoListArgs, mut verify_opts: VerificationArgs) -> Result<()> { diff --git a/kani-driver/src/list/mod.rs b/kani-driver/src/list/mod.rs index 0a5aa523ea6a..d300908d476d 100644 --- a/kani-driver/src/list/mod.rs +++ b/kani-driver/src/list/mod.rs @@ -8,15 +8,41 @@ use std::collections::{BTreeMap, BTreeSet}; pub mod collect_metadata; pub mod output; +type FileName = String; +type HarnessName = String; + +/// Metadata for the list subcommand for a given crate. +/// It is important that crate_name is the first field so that `Ord` orders two ListMetadata objects by crate name. +#[derive(PartialEq, Eq, PartialOrd, Ord)] pub struct ListMetadata { + crate_name: String, // Files mapped to their #[kani::proof] harnesses - standard_harnesses: BTreeMap>, + standard_harnesses: BTreeMap>, // Total number of #[kani::proof] harnesses standard_harnesses_count: usize, // Files mapped to their #[kani::proof_for_contract] harnesses - contract_harnesses: BTreeMap>, + contract_harnesses: BTreeMap>, // Total number of #[kani:proof_for_contract] harnesses contract_harnesses_count: usize, // Set of all functions under contract contracted_functions: BTreeSet, } + +/// Given a collection of ListMetadata objects, merge them into a single ListMetadata object. +pub fn merge_list_metadata(collection: T) -> ListMetadata +where + T: Extend, + T: IntoIterator, +{ + collection + .into_iter() + .reduce(|mut acc, item| { + acc.standard_harnesses.extend(item.standard_harnesses); + acc.standard_harnesses_count += item.standard_harnesses_count; + acc.contract_harnesses.extend(item.contract_harnesses); + acc.contract_harnesses_count += item.contract_harnesses_count; + acc.contracted_functions.extend(item.contracted_functions); + acc + }) + .expect("Cannot merge empty collection of ListMetadata objects") +} diff --git a/kani-driver/src/list/output.rs b/kani-driver/src/list/output.rs index 47b32bd6ed5e..e91f71a6c25f 100644 --- a/kani-driver/src/list/output.rs +++ b/kani-driver/src/list/output.rs @@ -3,13 +3,18 @@ //! This module handles outputting the result for the list subcommand use std::{ + collections::BTreeSet, fmt::Display, fs::File, io::{BufWriter, Write}, path::Path, }; -use crate::{args::list_args::Format, list::ListMetadata, version::KANI_VERSION}; +use crate::{ + args::list_args::Format, + list::{ListMetadata, merge_list_metadata}, + version::KANI_VERSION, +}; use anyhow::Result; use comfy_table::Table as PrettyTable; use serde_json::json; @@ -21,7 +26,11 @@ const FILE_VERSION: &str = "0.1"; const OUTPUT_FILENAME: &str = "kani-list"; /// Output the results of the list subcommand. -pub fn output_list_results(list_metadata: ListMetadata, format: Format, quiet: bool) -> Result<()> { +pub fn output_list_results( + list_metadata: BTreeSet, + format: Format, + quiet: bool, +) -> Result<()> { match format { Format::Pretty => pretty(list_metadata), Format::Markdown => markdown(list_metadata, quiet), @@ -29,36 +38,64 @@ pub fn output_list_results(list_metadata: ListMetadata, format: Format, quiet: b } } -/// Print results to the terminal. -fn pretty(list_metadata: ListMetadata) -> Result<()> { - let table = if list_metadata.contracted_functions.is_empty() { - None - } else { - let (header, rows) = construct_contracts_table(&list_metadata); - let mut t = PrettyTable::new(); - t.set_header(header).add_rows(rows); - Some(t) +fn pretty_constructor(header: Vec, rows: Vec>) -> Result { + let mut t = PrettyTable::new(); + t.set_header(header).add_rows(rows); + Ok(t) +} + +fn markdown_constructor(header: Vec, rows: Vec>) -> Result { + Ok(MarkdownTable::new(Some(header), rows)?) +} + +/// Construct the "Contracts" and "Standard Harnesses" tables. +/// `table_constructor` is a function that, given the header and rows for the tables, creates a particular kind of table. +fn construct_output( + list_metadata: BTreeSet, + table_constructor: fn(Vec, Vec>) -> Result, +) -> Result<(String, String)> { + let contract_output = { + const CONTRACTS_SECTION: &str = "Contracts:"; + const NO_CONTRACTS_MSG: &str = "No contracts or contract harnesses found."; + let contract_table = if list_metadata.iter().all(|md| md.contracted_functions.is_empty()) { + None + } else { + let (header, rows) = construct_contracts_table(&list_metadata); + let t = table_constructor(header, rows)?; + Some(t) + }; + format_results(contract_table, CONTRACTS_SECTION.to_string(), NO_CONTRACTS_MSG.to_string()) }; - let output = format_results(table, &list_metadata); - println!("{}", output); + let standard_output = { + const HARNESSES_SECTION: &str = "Standard Harnesses (#[kani::proof]):"; + const NO_HARNESSES_MSG: &str = "No standard harnesses found."; + let standard_table = { + let (header, rows) = construct_standard_table(&list_metadata); + let t = table_constructor(header, rows)?; + Some(t) + }; + format_results(standard_table, HARNESSES_SECTION.to_string(), NO_HARNESSES_MSG.to_string()) + }; + Ok((contract_output, standard_output)) +} + +/// Print results to the terminal. +fn pretty(list_metadata: BTreeSet) -> Result<()> { + let (contract_output, standard_output) = construct_output(list_metadata, pretty_constructor)?; + println!("{}", contract_output); + println!("{}", standard_output); Ok(()) } /// Output results to a Markdown file. -fn markdown(list_metadata: ListMetadata, quiet: bool) -> Result<()> { - let table = if list_metadata.contracted_functions.is_empty() { - None - } else { - let (header, rows) = construct_contracts_table(&list_metadata); - Some(MarkdownTable::new(Some(header), rows)?) - }; - - let output = format_results(table, &list_metadata); +fn markdown(list_metadata: BTreeSet, quiet: bool) -> Result<()> { + let (contract_output, standard_output) = construct_output(list_metadata, markdown_constructor)?; let out_path = Path::new(OUTPUT_FILENAME).with_extension("md"); let mut out_file = File::create(&out_path).unwrap(); - out_file.write_all(output.as_bytes()).unwrap(); + out_file.write_all(contract_output.as_bytes()).unwrap(); + out_file.write_all(standard_output.as_bytes()).unwrap(); if !quiet { println!("Wrote list results to {}", std::fs::canonicalize(&out_path)?.display()); } @@ -66,21 +103,23 @@ fn markdown(list_metadata: ListMetadata, quiet: bool) -> Result<()> { } /// Output results as a JSON file. -fn json(list_metadata: ListMetadata, quiet: bool) -> Result<()> { +fn json(list_metadata: BTreeSet, quiet: bool) -> Result<()> { let out_path = Path::new(OUTPUT_FILENAME).with_extension("json"); let out_file = File::create(&out_path).unwrap(); let writer = BufWriter::new(out_file); + let combined_md = merge_list_metadata(list_metadata); + let json_obj = json!({ "kani-version": KANI_VERSION, "file-version": FILE_VERSION, - "standard-harnesses": &list_metadata.standard_harnesses, - "contract-harnesses": &list_metadata.contract_harnesses, - "contracts": &list_metadata.contracted_functions, + "standard-harnesses": combined_md.standard_harnesses, + "contract-harnesses": combined_md.contract_harnesses, + "contracts": combined_md.contracted_functions, "totals": { - "standard-harnesses": list_metadata.standard_harnesses_count, - "contract-harnesses": list_metadata.contract_harnesses_count, - "functions-under-contract": list_metadata.contracted_functions.len(), + "standard-harnesses": combined_md.standard_harnesses_count, + "contract-harnesses": combined_md.contract_harnesses_count, + "functions-under-contract": combined_md.contracted_functions.len(), } }); @@ -95,65 +134,95 @@ fn json(list_metadata: ListMetadata, quiet: bool) -> Result<()> { /// Construct the rows for the table of contracts information. /// Returns a tuple of the table header and the rows. -fn construct_contracts_table(list_metadata: &ListMetadata) -> (Vec, Vec>) { +fn construct_contracts_table( + list_metadata: &BTreeSet, +) -> (Vec, Vec>) { const NO_HARNESSES_MSG: &str = "NONE"; + const CRATE_NAME: &str = "Crate"; const FUNCTION_HEADER: &str = "Function"; const CONTRACT_HARNESSES_HEADER: &str = "Contract Harnesses (#[kani::proof_for_contract])"; const TOTALS_HEADER: &str = "Total"; - let header = - vec![String::new(), FUNCTION_HEADER.to_string(), CONTRACT_HARNESSES_HEADER.to_string()]; + let header = vec![ + String::new(), + CRATE_NAME.to_string(), + FUNCTION_HEADER.to_string(), + CONTRACT_HARNESSES_HEADER.to_string(), + ]; let mut rows: Vec> = vec![]; - - for cf in &list_metadata.contracted_functions { - let mut row = vec![String::new(), cf.function.clone()]; - if cf.harnesses.is_empty() { - row.push(NO_HARNESSES_MSG.to_string()); - } else { - row.push(cf.harnesses.join(", ")); + let mut functions_under_contract_total = 0; + let mut contract_harnesses_total = 0; + + for crate_md in list_metadata { + for cf in &crate_md.contracted_functions { + let mut row = vec![String::new(), crate_md.crate_name.to_string(), cf.function.clone()]; + if cf.harnesses.is_empty() { + row.push(NO_HARNESSES_MSG.to_string()); + } else { + row.push(cf.harnesses.join(", ")); + } + rows.push(row); } - rows.push(row); + functions_under_contract_total += crate_md.contracted_functions.len(); + contract_harnesses_total += crate_md.contract_harnesses_count; } let totals_row = vec![ TOTALS_HEADER.to_string(), - list_metadata.contracted_functions.len().to_string(), - list_metadata.contract_harnesses_count.to_string(), + String::new(), + functions_under_contract_total.to_string(), + contract_harnesses_total.to_string(), ]; rows.push(totals_row); (header, rows) } -/// Format results as a String -fn format_results(table: Option, list_metadata: &ListMetadata) -> String { - const CONTRACTS_SECTION: &str = "Contracts:"; - const HARNESSES_SECTION: &str = "Standard Harnesses (#[kani::proof]):"; - const NO_CONTRACTS_MSG: &str = "No contracts or contract harnesses found."; - const NO_HARNESSES_MSG: &str = "No standard harnesses found."; +fn construct_standard_table( + list_metadata: &BTreeSet, +) -> (Vec, Vec>) { + const CRATE_NAME: &str = "Crate"; + const HARNESS_HEADER: &str = "Harness"; + const TOTALS_HEADER: &str = "Total"; - let mut output: Vec = vec![]; - output.push(format!("\n{CONTRACTS_SECTION}")); + let header = vec![String::new(), CRATE_NAME.to_string(), HARNESS_HEADER.to_string()]; - if let Some(table) = table { - output.push(format!("{table}")); - } else { - output.push(NO_CONTRACTS_MSG.to_string()); - } + let mut rows: Vec> = vec![]; - output.push(format!("\n{HARNESSES_SECTION}")); - if list_metadata.standard_harnesses.is_empty() { - output.push(NO_HARNESSES_MSG.to_string()); + let mut total = 0; + + for crate_md in list_metadata { + for harnesses in crate_md.standard_harnesses.values() { + for harness in harnesses { + rows.push(vec![ + String::new(), + crate_md.crate_name.to_string(), + harness.to_string(), + ]); + } + total += harnesses.len(); + } } - let mut std_harness_index = 0; + let totals_row = vec![TOTALS_HEADER.to_string(), String::new(), total.to_string()]; + rows.push(totals_row); + + (header, rows) +} - for harnesses in (&list_metadata.standard_harnesses).values() { - for harness in harnesses { - output.push(format!("{}. {harness}", std_harness_index + 1)); - std_harness_index += 1; - } +fn format_results( + table: Option, + section_name: String, + absent_name: String, +) -> String { + let mut output: Vec = vec![]; + output.push(format!("\n{section_name}")); + + if let Some(table) = table { + output.push(format!("{table}")); + } else { + output.push(absent_name); } output.join("\n") diff --git a/tests/script-based-pre/cargo_autoharness_list/list.expected b/tests/script-based-pre/cargo_autoharness_list/list.expected index 4c9feb73d184..06eccba6fef3 100644 --- a/tests/script-based-pre/cargo_autoharness_list/list.expected +++ b/tests/script-based-pre/cargo_autoharness_list/list.expected @@ -12,15 +12,21 @@ Kani generated automatic harnesses for 3 function(s): Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). Contracts: -+-------+---------------------------+-----------------------------------------------------------------------------+ -| | Function | Contract Harnesses (#[kani::proof_for_contract]) | -+=================================================================================================================+ -| | has_recursion_gcd | my_harness, my_harness_2, kani::internal::automatic_harness | -|-------+---------------------------+-----------------------------------------------------------------------------| -| | verify::has_recursion_gcd | verify::my_harness, verify::my_harness_2, kani::internal::automatic_harness | -|-------+---------------------------+-----------------------------------------------------------------------------| -| Total | 2 | 6 | -+-------+---------------------------+-----------------------------------------------------------------------------+ ++-------+------------------------+---------------------------+-----------------------------------------------------------------------------+ +| | Crate | Function | Contract Harnesses (#[kani::proof_for_contract]) | ++==========================================================================================================================================+ +| | cargo_autoharness_list | has_recursion_gcd | my_harness, my_harness_2, kani::internal::automatic_harness | +|-------+------------------------+---------------------------+-----------------------------------------------------------------------------| +| | cargo_autoharness_list | verify::has_recursion_gcd | verify::my_harness, verify::my_harness_2, kani::internal::automatic_harness | +|-------+------------------------+---------------------------+-----------------------------------------------------------------------------| +| Total | | 2 | 6 | ++-------+------------------------+---------------------------+-----------------------------------------------------------------------------+ Standard Harnesses (#[kani::proof]): -1. f_u8 ++-------+------------------------+---------+ +| | Crate | Harness | ++==========================================+ +| | cargo_autoharness_list | f_u8 | +|-------+------------------------+---------| +| Total | | 1 | ++-------+------------------------+---------+ diff --git a/tests/script-based-pre/cargo_list_md/list.expected b/tests/script-based-pre/cargo_list_md/list.expected index 9e913345e664..fcaf72a14d00 100644 --- a/tests/script-based-pre/cargo_list_md/list.expected +++ b/tests/script-based-pre/cargo_list_md/list.expected @@ -1,14 +1,16 @@ Contracts: -| | Function | Contract Harnesses (#[kani::proof_for_contract]) | -| ----- | ----------------------------- | -------------------------------------------------------------- | -| | example::implementation::bar | example::verify::check_bar | -| | example::implementation::foo | example::verify::check_foo_u32, example::verify::check_foo_u64 | -| | example::implementation::func | example::verify::check_func | -| | example::prep::parse | NONE | -| Total | 4 | 4 | - +| | Crate | Function | Contract Harnesses (#[kani::proof_for_contract]) | +| ----- | ----- | ----------------------------- | -------------------------------------------------------------- | +| | lib | example::implementation::bar | example::verify::check_bar | +| | lib | example::implementation::foo | example::verify::check_foo_u32, example::verify::check_foo_u64 | +| | lib | example::implementation::func | example::verify::check_func | +| | lib | example::prep::parse | NONE | +| Total | | 4 | 4 | Standard Harnesses (#[kani::proof]): -1. standard_harnesses::example::verify::check_modify -2. standard_harnesses::example::verify::check_new \ No newline at end of file +| | Crate | Harness | +| ----- | ----- | ------------------------------------------------- | +| | lib | standard_harnesses::example::verify::check_modify | +| | lib | standard_harnesses::example::verify::check_new | +| Total | | 2 | diff --git a/tests/script-based-pre/kani_list_md/list.expected b/tests/script-based-pre/kani_list_md/list.expected index eb4ca335d678..1184443beaa1 100644 --- a/tests/script-based-pre/kani_list_md/list.expected +++ b/tests/script-based-pre/kani_list_md/list.expected @@ -1,14 +1,16 @@ Contracts: -| | Function | Contract Harnesses (#[kani::proof_for_contract]) | -| ----- | ----------------------------- | -------------------------------------------------------------- | -| | example::implementation::bar | example::verify::check_bar | -| | example::implementation::foo | example::verify::check_foo_u32, example::verify::check_foo_u64 | -| | example::implementation::func | example::verify::check_func | -| | example::prep::parse | NONE | -| Total | 4 | 4 | - +| | Crate | Function | Contract Harnesses (#[kani::proof_for_contract]) | +| ----- | ----- | ----------------------------- | -------------------------------------------------------------- | +| | lib | example::implementation::bar | example::verify::check_bar | +| | lib | example::implementation::foo | example::verify::check_foo_u32, example::verify::check_foo_u64 | +| | lib | example::implementation::func | example::verify::check_func | +| | lib | example::prep::parse | NONE | +| Total | | 4 | 4 | Standard Harnesses (#[kani::proof]): -1. example::verify::check_modify -2. example::verify::check_new \ No newline at end of file +| | Crate | Harness | +| ----- | ----- | ----------------------------- | +| | lib | example::verify::check_modify | +| | lib | example::verify::check_new | +| Total | | 2 | From 717e99d9a7ecd7f137115834ea14c8500d3e4c07 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Thu, 17 Apr 2025 17:33:57 -0400 Subject: [PATCH 105/161] Autoharness: Update Filtering Options (#4025) ## Summary - fda814bcdfadec64b570c9362693f6ea92bd009e: Make the autoharness filter flags work on the standard library by moving them to `kani_compiler_flags`, which ensures they're passed to all Kani compiler invocations. - c0430ec8aecb827ac29d81916081c91dc95665e6: Print the crate name in our output tables. - 093fc6b57b56e8bac9f5fb3baa8a49b8c586f7b2: **Breaking Change** to rename `--include-function` and `--exclude-function` to mention `pattern`s instead, which makes it clearer that they talk about substrings of the total paths (e.g., modules). Also implement the suggestion from https://github.com/model-checking/kani/pull/3922#discussion_r1982170163 so that the flags are no longer mutually exclusive. - 9e35fcab81ef0dcf8c0f01441b8b1a753948bdd8: Let the above flags filter on crate names as well. - 66444e40d7d12ab7ad1b118cf814f17206be9ee4: Warn if an exclude flag makes an include flag moot. ## Detail Some more context on why f933799c54b09210cb267963ff1dc431c7a9385a allows for both flags to be passed now: I realized as part of https://github.com/model-checking/kani/pull/3984 how when we call `cargo rustc` for a `cargo kani` invocation, we don't pass `--reachability` to dependencies to avoid running harnesses in them. The problem is that we can't do the same for our cargo command to build the standard library, since that uses `cargo build`, which does not have the same ability to pass flags only to the final compiler invocation and not the dependencies. So we end up passing `--reachability=AllFns` to the dependencies of the standard library as well and generate automatic harnesses for them. If we can pass both filter flags, we can run commands like `kani autoharness --std --include-pattern core --exclude-pattern miniz_oxide`, which will include functions from the `core` crate while excluding functions in the `miniz_oxide` that just happen to have the word "core" in them. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../src/reference/experimental/autoharness.md | 34 +- kani-compiler/src/args.rs | 18 +- .../src/kani_middle/codegen_units.rs | 39 +- kani-driver/src/args/autoharness_args.rs | 39 +- kani-driver/src/args/mod.rs | 1 + kani-driver/src/autoharness/mod.rs | 57 ++- kani-driver/src/call_cargo.rs | 4 +- kani-driver/src/call_single_file.rs | 4 + kani-driver/src/harness_runner.rs | 4 +- kani-driver/src/session.rs | 13 +- .../contracts.expected | 69 ++-- .../dependencies.expected | 20 +- .../exclude.expected | 35 +- .../cargo_autoharness_exclude/exclude.sh | 2 +- .../cargo_autoharness_exclude/src/lib.rs | 2 +- .../cargo_autoharness_filter/filter.expected | 386 +++++++++--------- .../harnesses_fail.expected | 52 +-- .../include.expected | 35 +- .../cargo_autoharness_include/include.sh | 2 +- .../cargo_autoharness_include/src/lib.rs | 2 +- .../cargo_autoharness_list/list.expected | 18 +- .../termination_timeout.expected | 20 +- .../termination_unwind.expected | 28 +- .../type-invariant.expected | 68 +-- .../config.yml | 3 + .../precedence.sh | 125 ++++++ .../src/lib.rs | 26 ++ 27 files changed, 667 insertions(+), 439 deletions(-) create mode 100644 tests/script-based-pre/kani_autoharness_exclude_precedence/config.yml create mode 100755 tests/script-based-pre/kani_autoharness_exclude_precedence/precedence.sh create mode 100644 tests/script-based-pre/kani_autoharness_exclude_precedence/src/lib.rs diff --git a/docs/src/reference/experimental/autoharness.md b/docs/src/reference/experimental/autoharness.md index 34d69da13738..1fa184c484bc 100644 --- a/docs/src/reference/experimental/autoharness.md +++ b/docs/src/reference/experimental/autoharness.md @@ -43,18 +43,46 @@ autoharness feature means that you are also opting into the function contracts a Kani generates and runs these harnesses internally—the user only sees the verification results. ### Options -The `autoharness` subcommand has options `--include-function` and `--exclude-function` to include and exclude particular functions. +The `autoharness` subcommand has options `--include-pattern` and `--exclude-pattern` to include and exclude particular functions. These flags look for partial matches against the fully qualified name of a function. For example, if a module `my_module` has many functions, but we are only interested in `my_module::foo` and `my_module::bar`, we can run: ``` -cargo run autoharness -Z autoharness --include-function foo --include-function bar +cargo run autoharness -Z autoharness --include-pattern my_module::foo --include-pattern my_module::bar ``` To exclude `my_module` entirely, run: ``` -cargo run autoharness -Z autoharness --exclude-function my_module +cargo run autoharness -Z autoharness --exclude-pattern my_module ``` +The selection algorithm is as follows: +- If only `--include-pattern`s are provided, include a function if it matches any of the provided patterns. +- If only `--exclude-pattern`s are provided, include a function if it does not match any of the provided patterns. +- If both are provided, include a function if it matches an include pattern *and* does not match any of the exclude patterns. Note that this implies that the exclude pattern takes precedence, i.e., if a function matches both an include pattern and an exclude pattern, it will be excluded. + +Here are some more examples: + +``` +# Include functions whose paths contain the substring foo or baz, but not foo::bar +kani autoharness -Z autoharness --include-pattern foo --include-pattern baz --exclude-pattern foo::bar + +# Include functions whose paths contain the substring foo, but not bar. +kani autoharness -Z autoharness --include-pattern foo --exclude-pattern bar + +# Include functions whose paths contain the substring foo::bar, but not bar. +# This ends up including nothing, since all foo::bar matches will also contain bar. +# Kani will emit a warning that these flags conflict. +kani autoharness -Z autoharness --include-pattern foo::bar --exclude-pattern bar + +# Include functions whose paths contain the substring foo, but not foo. +# This ends up including nothing, and Kani will emit a warning that these flags conflict. +kani autoharness -Z autoharness --include-pattern foo --exclude--pattern foo +``` + +Currently, the only supported "patterns" are substrings of the fully qualified path of the function. +However, if more sophisticated patterns (e.g., regular expressions) would be useful for you, +please let us know in a comment on [this GitHub issue](https://github.com/model-checking/kani/issues/3832). + ## Example Using the `estimate_size` example from [First Steps](../../tutorial-first-steps.md) again: ```rust diff --git a/kani-compiler/src/args.rs b/kani-compiler/src/args.rs index ce969d195675..34a913088037 100644 --- a/kani-compiler/src/args.rs +++ b/kani-compiler/src/args.rs @@ -91,16 +91,14 @@ pub struct Arguments { /// Print the final LLBC file to stdout. #[clap(long)] pub print_llbc: bool, - /// If we are running the autoharness subcommand, the functions to include - #[arg( - long = "autoharness-include-function", - num_args(1), - conflicts_with = "autoharness_excluded_functions" - )] - pub autoharness_included_functions: Vec, - /// If we are running the autoharness subcommand, the functions to exclude - #[arg(long = "autoharness-exclude-function", num_args(1))] - pub autoharness_excluded_functions: Vec, + /// If we are running the autoharness subcommand, the paths to include. + /// See kani_driver::autoharness_args for documentation. + #[arg(long = "autoharness-include-pattern", num_args(1))] + pub autoharness_included_patterns: Vec, + /// If we are running the autoharness subcommand, the paths to exclude. + /// See kani_driver::autoharness_args for documentation. + #[arg(long = "autoharness-exclude-pattern", num_args(1))] + pub autoharness_excluded_patterns: Vec, } #[derive(Debug, Clone, Copy, AsRefStr, EnumString, VariantNames, PartialEq, Eq)] diff --git a/kani-compiler/src/kani_middle/codegen_units.rs b/kani-compiler/src/kani_middle/codegen_units.rs index 7a9f494d997e..c87cd4a52fff 100644 --- a/kani-compiler/src/kani_middle/codegen_units.rs +++ b/kani-compiler/src/kani_middle/codegen_units.rs @@ -90,6 +90,7 @@ impl CodegenUnits { let (chosen, skipped) = automatic_harness_partition( tcx, args, + &crate_info.name, *kani_fns.get(&KaniModel::Any.into()).unwrap(), ); AUTOHARNESS_MD @@ -360,6 +361,7 @@ fn get_all_automatic_harnesses( fn automatic_harness_partition( tcx: TyCtxt, args: &Arguments, + crate_name: &str, kani_any_def: FnDef, ) -> (Vec, BTreeMap) { // If `filter_list` contains `name`, either as an exact match or a substring. @@ -384,7 +386,8 @@ fn automatic_harness_partition( return Some(AutoHarnessSkipReason::NoBody); } - let name = instance.name(); + // Preprend the crate name so that users can filter out entire crates using the existing function filter flags. + let name = format!("{crate_name}::{}", instance.name()); let body = instance.body().unwrap(); if is_proof_harness(tcx, instance) @@ -394,12 +397,34 @@ fn automatic_harness_partition( return Some(AutoHarnessSkipReason::KaniImpl); } - if (!args.autoharness_included_functions.is_empty() - && !filter_contains(&name, &args.autoharness_included_functions)) - || (!args.autoharness_excluded_functions.is_empty() - && filter_contains(&name, &args.autoharness_excluded_functions)) - { - return Some(AutoHarnessSkipReason::UserFilter); + match ( + args.autoharness_included_patterns.is_empty(), + args.autoharness_excluded_patterns.is_empty(), + ) { + // If no filters were specified, then continue. + (true, true) => {} + // If only --exclude-pattern was provided, filter out the function if excluded_patterns contains its name. + (true, false) => { + if filter_contains(&name, &args.autoharness_excluded_patterns) { + return Some(AutoHarnessSkipReason::UserFilter); + } + } + // If only --include-pattern was provided, filter out the function if included_patterns does not contain its name. + (false, true) => { + if !filter_contains(&name, &args.autoharness_included_patterns) { + return Some(AutoHarnessSkipReason::UserFilter); + } + } + // If both are specified, filter out the function if included_patterns does not contain its name. + // Then, filter out any functions that excluded_patterns does match. + // This order is important, since it preserves the semantics described in kani_driver::autoharness_args where exclude takes precedence over include. + (false, false) => { + if !filter_contains(&name, &args.autoharness_included_patterns) + || filter_contains(&name, &args.autoharness_excluded_patterns) + { + return Some(AutoHarnessSkipReason::UserFilter); + } + } } // Each argument of `instance` must implement Arbitrary. diff --git a/kani-driver/src/args/autoharness_args.rs b/kani-driver/src/args/autoharness_args.rs index 3dc8f6973703..c5ddd25302fb 100644 --- a/kani-driver/src/args/autoharness_args.rs +++ b/kani-driver/src/args/autoharness_args.rs @@ -9,27 +9,28 @@ use crate::args::{ValidateArgs, VerificationArgs, validate_std_path}; use clap::{Error, Parser, error::ErrorKind}; use kani_metadata::UnstableFeature; +// TODO: It would be nice if we could borrow --exact here from VerificationArgs to differentiate between partial/exact matches, +// like --harnesses does. Sharing arguments with VerificationArgs doesn't work with our current structure, though. #[derive(Debug, Parser)] pub struct CommonAutoharnessArgs { - /// If specified, only autoharness functions that match this filter. This option can be provided - /// multiple times, which will verify all functions matching any of the filters. - /// Note that this filter will match against partial names, i.e., providing the name of a module will include all functions from that module. - /// Also note that if the function specified is unable to be automatically verified, this flag will have no effect. - #[arg( - long = "include-function", - num_args(1), - value_name = "FUNCTION", - conflicts_with = "exclude_function" - )] - pub include_function: Vec, - - /// If specified, only autoharness functions that do not match this filter. This option can be provided - /// multiple times, which will verify all functions that do not match any of the filters. - /// Note that this filter will match against partial names, i.e., providing the name of a module will exclude all functions from that module. - #[arg(long = "exclude-function", num_args(1), value_name = "FUNCTION")] - pub exclude_function: Vec, - // TODO: It would be nice if we could borrow --exact here from VerificationArgs to differentiate between partial/exact matches, - // like --harnesses does. Sharing arguments with VerificationArgs doesn't work with our current structure, though. + /// Only create automatic harnesses for functions that match the given pattern. + /// This option can be provided multiple times, which will verify functions matching any of the patterns. + /// Kani considers a function to match the pattern if its fully qualified path contains PATTERN as a substring. + /// Example: `--include-pattern foo` matches all functions whose fully qualified paths contain the substring "foo". + #[arg(long = "include-pattern", num_args(1), value_name = "PATTERN")] + pub include_pattern: Vec, + + /// Only create automatic harnesses for functions that do not match the given pattern. + /// This option can be provided multiple times, which will verify functions that do not match any of the patterns. + /// Kani considers a function to match the pattern if its fully qualified path contains PATTERN as a substring. + + /// This option takes precedence over `--include-pattern`, i.e., Kani will first select all functions that match `--include-pattern`, + /// then exclude those that match `--exclude-pattern.` + /// Example: `--include-pattern foo --exclude-pattern foo::bar` creates automatic harnesses for all functions whose paths contain "foo" without "foo::bar". + /// Example: `--include-pattern foo::bar --exclude-pattern foo` makes the `--include-pattern` a no-op, since the exclude pattern is a superset of the include pattern. + #[arg(long = "exclude-pattern", num_args(1), value_name = "PATTERN")] + pub exclude_pattern: Vec, + /// Run the `list` subcommand after generating the automatic harnesses. Requires -Z list. Note that this option implies --only-codegen. #[arg(long)] pub list: bool, diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index be198df73c25..421df82f3e5f 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -178,6 +178,7 @@ pub enum CargoKaniSubcommand { List(Box), /// Create and run harnesses automatically for eligible functions. Implies -Z function-contracts and -Z loop-contracts. + /// See https://model-checking.github.io/kani/reference/experimental/autoharness.html for documentation. Autoharness(Box), } diff --git a/kani-driver/src/autoharness/mod.rs b/kani-driver/src/autoharness/mod.rs index 15ada39332d3..552c38a8128e 100644 --- a/kani-driver/src/autoharness/mod.rs +++ b/kani-driver/src/autoharness/mod.rs @@ -9,12 +9,12 @@ use crate::args::autoharness_args::{ }; use crate::args::common::UnstableFeature; use crate::call_cbmc::VerificationStatus; -use crate::call_single_file::to_rustc_arg; use crate::harness_runner::HarnessResult; use crate::list::collect_metadata::process_metadata; use crate::list::output::output_list_results; use crate::project::{Project, standalone_project, std_project}; use crate::session::KaniSession; +use crate::util::warning; use crate::{InvocationType, print_kani_version, project, verify_project}; use anyhow::Result; use comfy_table::Table as PrettyTable; @@ -56,8 +56,8 @@ fn setup_session(session: &mut KaniSession, common_autoharness_args: &CommonAuto session.enable_autoharness(); session.add_default_bounds(); session.add_auto_harness_args( - &common_autoharness_args.include_function, - &common_autoharness_args.exclude_function, + &common_autoharness_args.include_pattern, + &common_autoharness_args.exclude_pattern, ); } @@ -84,17 +84,20 @@ fn postprocess_project( /// Print automatic harness metadata to the terminal. fn print_autoharness_metadata(metadata: Vec) { let mut chosen_table = PrettyTable::new(); - chosen_table.set_header(vec!["Selected Function"]); + chosen_table.set_header(vec!["Crate", "Selected Function"]); let mut skipped_table = PrettyTable::new(); - skipped_table.set_header(vec!["Skipped Function", "Reason for Skipping"]); + skipped_table.set_header(vec!["Crate", "Skipped Function", "Reason for Skipping"]); for md in metadata { let autoharness_md = md.autoharness_md.unwrap(); - chosen_table.add_rows(autoharness_md.chosen.into_iter().map(|func| vec![func])); + chosen_table.add_rows( + autoharness_md.chosen.into_iter().map(|func| vec![md.crate_name.clone(), func]), + ); skipped_table.add_rows(autoharness_md.skipped.into_iter().filter_map(|(func, reason)| { match reason { AutoHarnessSkipReason::MissingArbitraryImpl(ref args) => Some(vec![ + md.crate_name.clone(), func, format!( "{reason} {}", @@ -106,7 +109,9 @@ fn print_autoharness_metadata(metadata: Vec) { ]), AutoHarnessSkipReason::GenericFn | AutoHarnessSkipReason::NoBody - | AutoHarnessSkipReason::UserFilter => Some(vec![func, reason.to_string()]), + | AutoHarnessSkipReason::UserFilter => { + Some(vec![md.crate_name.clone(), func, reason.to_string()]) + } // We don't report Kani implementations to the user to avoid exposing Kani functions we insert during instrumentation. // For those we don't insert during instrumentation that are in this category (manual harnesses or Kani trait implementations), // it should be obvious that we wouldn't generate harnesses, so reporting those functions as "skipped" is unlikely to be useful. @@ -151,23 +156,36 @@ fn print_skipped_table(table: &mut PrettyTable) { impl KaniSession { /// Enable autoharness mode. pub fn enable_autoharness(&mut self) { - self.auto_harness = true; + self.autoharness_compiler_flags = Some(vec![]); self.args.common_args.unstable_features.enable_feature(UnstableFeature::FunctionContracts); self.args.common_args.unstable_features.enable_feature(UnstableFeature::LoopContracts); } /// Add the compiler arguments specific to the `autoharness` subcommand. - /// TODO: this should really be appending onto the `kani_compiler_flags()` output instead of `pkg_args`. - /// It doesn't affect functionality since autoharness doesn't examine dependencies, but would still be better practice. pub fn add_auto_harness_args(&mut self, included: &[String], excluded: &[String]) { - for func in included { - self.pkg_args - .push(to_rustc_arg(vec![format!("--autoharness-include-function {}", func)])); + for include_pattern in included { + for exclude_pattern in excluded { + // Check if include pattern contains exclude pattern + // This catches cases like include="foo::bar" exclude="bar" or include="foo" exclude="foo" + if include_pattern.contains(exclude_pattern) { + warning(&format!( + "Include pattern '{}' contains exclude pattern '{}'. \ + This combination will never match any functions since all functions matching \ + the include pattern will also match the exclude pattern, and the exclude pattern takes precedence.", + include_pattern, exclude_pattern + )); + } + } + } + + let mut args = vec![]; + for pattern in included { + args.push(format!("--autoharness-include-pattern {}", pattern)); } - for func in excluded { - self.pkg_args - .push(to_rustc_arg(vec![format!("--autoharness-exclude-function {}", func)])); + for pattern in excluded { + args.push(format!("--autoharness-exclude-pattern {}", pattern)); } + self.autoharness_compiler_flags = Some(args); } /// Add global harness timeout and loop unwinding bounds if not provided. @@ -196,6 +214,7 @@ impl KaniSession { let mut verified_fns = PrettyTable::new(); verified_fns.set_header(vec![ + "Crate", "Selected Function", "Kind of Automatic Harness", "Verification Result", @@ -203,6 +222,7 @@ impl KaniSession { for success in successes { verified_fns.add_row(vec![ + success.harness.crate_name.clone(), success.harness.pretty_name.clone(), success.harness.attributes.kind.to_string(), success.result.status.to_string(), @@ -211,13 +231,16 @@ impl KaniSession { for failure in failures { verified_fns.add_row(vec![ + failure.harness.crate_name.clone(), failure.harness.pretty_name.clone(), failure.harness.attributes.kind.to_string(), failure.result.status.to_string(), ]); } - println!("{verified_fns}"); + if total > 0 { + println!("{verified_fns}"); + } if failing > 0 { println!( diff --git a/kani-driver/src/call_cargo.rs b/kani-driver/src/call_cargo.rs index 5f9f18d03563..af2e3f192e45 100644 --- a/kani-driver/src/call_cargo.rs +++ b/kani-driver/src/call_cargo.rs @@ -194,7 +194,7 @@ crate-type = ["lib"] // If you are adding a new `kani-compiler` argument, you likely want to put it `kani_compiler_flags()` instead, // unless there a reason it shouldn't be passed to dependencies. // (Note that at the time of writing, passing the other compiler args to dependencies is a no-op, since `--reachability=None` skips codegen anyway.) - self.pkg_args.push(self.reachability_arg()); + let pkg_args = vec!["--".into(), self.reachability_arg()]; let mut found_target = false; let packages = self.packages_to_verify(&self.args, &metadata)?; @@ -206,7 +206,7 @@ crate-type = ["lib"] cmd.args(&cargo_args) .args(vec!["-p", &package.id.to_string()]) .args(verification_target.to_args()) - .args(&self.pkg_args) + .args(&pkg_args) .env("RUSTC", &self.kani_compiler) // Use CARGO_ENCODED_RUSTFLAGS instead of RUSTFLAGS is preferred. See // https://doc.rust-lang.org/cargo/reference/environment-variables.html diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index 4c049ca3196b..f3e488791ddc 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -151,6 +151,10 @@ impl KaniSession { flags.push("--no-assert-contracts".into()); } + if let Some(args) = self.autoharness_compiler_flags.clone() { + flags.extend(args); + } + flags.extend(self.args.common_args.unstable_features.as_arguments().map(str::to_string)); flags diff --git a/kani-driver/src/harness_runner.rs b/kani-driver/src/harness_runner.rs index 2b52aa78d0e6..ab7f4d8bb699 100644 --- a/kani-driver/src/harness_runner.rs +++ b/kani-driver/src/harness_runner.rs @@ -298,11 +298,11 @@ impl KaniSession { self.show_coverage_summary()?; } - if self.auto_harness { + if self.autoharness_compiler_flags.is_some() { self.print_autoharness_summary(automatic)?; } - if failing > 0 && !self.auto_harness { + if failing > 0 && self.autoharness_compiler_flags.is_none() { // Failure exit code without additional error message drop(self); std::process::exit(1); diff --git a/kani-driver/src/session.rs b/kani-driver/src/session.rs index 20ebef0e2aaa..df9e7310f142 100644 --- a/kani-driver/src/session.rs +++ b/kani-driver/src/session.rs @@ -31,11 +31,9 @@ pub struct KaniSession { /// The common command-line arguments pub args: VerificationArgs, - /// Automatically verify functions in the crate, in addition to running manual harnesses. - pub auto_harness: bool, - - /// The arguments that will be passed only to the target package, not its dependencies. - pub pkg_args: Vec, + /// The autoharness-specific compiler arguments. + /// Invariant: this field is_some() iff the autoharness subcommand is enabled. + pub autoharness_compiler_flags: Option>, /// Include all publicly-visible symbols in the generated goto binary, not just those reachable from /// a proof harness. Useful when attempting to verify things that were not annotated with kani @@ -71,8 +69,7 @@ impl KaniSession { Ok(KaniSession { args, - auto_harness: false, - pkg_args: vec!["--".to_string()], + autoharness_compiler_flags: None, codegen_tests: false, kani_compiler: install.kani_compiler()?, kani_lib_c: install.kani_lib_c()?, @@ -101,7 +98,7 @@ impl KaniSession { pub fn reachability_mode(&self) -> ReachabilityMode { if self.codegen_tests { ReachabilityMode::Tests - } else if self.auto_harness { + } else if self.autoharness_compiler_flags.is_some() { ReachabilityMode::AllFns } else { ReachabilityMode::ProofHarnesses diff --git a/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected b/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected index e5aa3a29009a..2afedc9e0b0a 100644 --- a/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected +++ b/tests/script-based-pre/cargo_autoharness_contracts/contracts.expected @@ -1,24 +1,23 @@ Kani generated automatic harnesses for 7 function(s): -+---------------------------------------------+ -| Selected Function | -+=============================================+ -| should_fail::max | -|---------------------------------------------| -| should_pass::alignment::Alignment | -|---------------------------------------------| -| should_pass::alignment::Alignment::as_usize | -|---------------------------------------------| -| should_pass::div | -|---------------------------------------------| -| should_pass::has_loop_contract | -|---------------------------------------------| -| should_pass::has_recursion_gcd | -|---------------------------------------------| -| should_pass::unchecked_mul | -+---------------------------------------------+ ++-----------------------------+---------------------------------------------+ +| Crate | Selected Function | ++===========================================================================+ +| cargo_autoharness_contracts | should_fail::max | +|-----------------------------+---------------------------------------------| +| cargo_autoharness_contracts | should_pass::alignment::Alignment | +|-----------------------------+---------------------------------------------| +| cargo_autoharness_contracts | should_pass::alignment::Alignment::as_usize | +|-----------------------------+---------------------------------------------| +| cargo_autoharness_contracts | should_pass::div | +|-----------------------------+---------------------------------------------| +| cargo_autoharness_contracts | should_pass::has_loop_contract | +|-----------------------------+---------------------------------------------| +| cargo_autoharness_contracts | should_pass::has_recursion_gcd | +|-----------------------------+---------------------------------------------| +| cargo_autoharness_contracts | should_pass::unchecked_mul | ++-----------------------------+---------------------------------------------+ Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). - Autoharness: Checking function should_fail::max's contract against all possible inputs... assertion\ - Status: FAILURE\ @@ -55,23 +54,23 @@ Manual Harness Summary: No proof harnesses (functions with #[kani::proof]) were found to verify. Autoharness Summary: -+---------------------------------------------+-----------------------------+---------------------+ -| Selected Function | Kind of Automatic Harness | Verification Result | -+=================================================================================================+ -| should_pass::alignment::Alignment | #[kani::proof] | Success | -|---------------------------------------------+-----------------------------+---------------------| -| should_pass::alignment::Alignment::as_usize | #[kani::proof_for_contract] | Success | -|---------------------------------------------+-----------------------------+---------------------| -| should_pass::div | #[kani::proof_for_contract] | Success | -|---------------------------------------------+-----------------------------+---------------------| -| should_pass::has_loop_contract | #[kani::proof] | Success | -|---------------------------------------------+-----------------------------+---------------------| -| should_pass::has_recursion_gcd | #[kani::proof_for_contract] | Success | -|---------------------------------------------+-----------------------------+---------------------| -| should_pass::unchecked_mul | #[kani::proof_for_contract] | Success | -|---------------------------------------------+-----------------------------+---------------------| -| should_fail::max | #[kani::proof_for_contract] | Failure | -+---------------------------------------------+-----------------------------+---------------------+ ++-----------------------------+---------------------------------------------+-----------------------------+---------------------+ +| Crate | Selected Function | Kind of Automatic Harness | Verification Result | ++===============================================================================================================================+ +| cargo_autoharness_contracts | should_pass::alignment::Alignment | #[kani::proof] | Success | +|-----------------------------+---------------------------------------------+-----------------------------+---------------------| +| cargo_autoharness_contracts | should_pass::alignment::Alignment::as_usize | #[kani::proof_for_contract] | Success | +|-----------------------------+---------------------------------------------+-----------------------------+---------------------| +| cargo_autoharness_contracts | should_pass::div | #[kani::proof_for_contract] | Success | +|-----------------------------+---------------------------------------------+-----------------------------+---------------------| +| cargo_autoharness_contracts | should_pass::has_loop_contract | #[kani::proof] | Success | +|-----------------------------+---------------------------------------------+-----------------------------+---------------------| +| cargo_autoharness_contracts | should_pass::has_recursion_gcd | #[kani::proof_for_contract] | Success | +|-----------------------------+---------------------------------------------+-----------------------------+---------------------| +| cargo_autoharness_contracts | should_pass::unchecked_mul | #[kani::proof_for_contract] | Success | +|-----------------------------+---------------------------------------------+-----------------------------+---------------------| +| cargo_autoharness_contracts | should_fail::max | #[kani::proof_for_contract] | Failure | ++-----------------------------+---------------------------------------------+-----------------------------+---------------------+ Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). Complete - 6 successfully verified functions, 1 failures, 7 total. diff --git a/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected b/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected index 553573b70d2b..aac45d7733d6 100644 --- a/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected +++ b/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.expected @@ -1,9 +1,9 @@ Kani generated automatic harnesses for 1 function(s): -+-------------------+ -| Selected Function | -+===================+ -| yes_harness | -+-------------------+ ++--------------------------------+-------------------+ +| Crate | Selected Function | ++====================================================+ +| cargo_autoharness_dependencies | yes_harness | ++--------------------------------+-------------------+ Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). @@ -14,9 +14,9 @@ Manual Harness Summary: No proof harnesses (functions with #[kani::proof]) were found to verify. Autoharness Summary: -+-------------------+---------------------------+---------------------+ -| Selected Function | Kind of Automatic Harness | Verification Result | -+=====================================================================+ -| yes_harness | #[kani::proof] | Success | -+-------------------+---------------------------+---------------------+ ++--------------------------------+-------------------+---------------------------+---------------------+ +| Crate | Selected Function | Kind of Automatic Harness | Verification Result | ++======================================================================================================+ +| cargo_autoharness_dependencies | yes_harness | #[kani::proof] | Success | ++--------------------------------+-------------------+---------------------------+---------------------+ Complete - 1 successfully verified functions, 0 failures, 1 total. diff --git a/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected b/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected index 1b0085fbcf28..9c006d210262 100644 --- a/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected +++ b/tests/script-based-pre/cargo_autoharness_exclude/exclude.expected @@ -1,20 +1,19 @@ Kani generated automatic harnesses for 1 function(s): -+-------------------+ -| Selected Function | -+===================+ -| include::simple | -+-------------------+ ++---------------------------+-------------------+ +| Crate | Selected Function | ++===============================================+ +| cargo_autoharness_include | include::simple | ++---------------------------+-------------------+ Kani did not generate automatic harnesses for 2 function(s). If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832 -+------------------+--------------------------------+ -| Skipped Function | Reason for Skipping | -+===================================================+ -| excluded::simple | Did not match provided filters | -|------------------+--------------------------------| -| include::generic | Generic Function | -+------------------+--------------------------------+ - ++---------------------------+------------------+--------------------------------+ +| Crate | Skipped Function | Reason for Skipping | ++===============================================================================+ +| cargo_autoharness_include | excluded::simple | Did not match provided filters | +|---------------------------+------------------+--------------------------------| +| cargo_autoharness_include | include::generic | Generic Function | ++---------------------------+------------------+--------------------------------+ Autoharness: Checking function include::simple against all possible inputs... VERIFICATION:- SUCCESSFUL @@ -22,9 +21,9 @@ Manual Harness Summary: No proof harnesses (functions with #[kani::proof]) were found to verify. Autoharness Summary: -+-------------------+---------------------------+---------------------+ -| Selected Function | Kind of Automatic Harness | Verification Result | -+=====================================================================+ -| include::simple | #[kani::proof] | Success | -+-------------------+---------------------------+---------------------+ ++---------------------------+-------------------+---------------------------+---------------------+ +| Crate | Selected Function | Kind of Automatic Harness | Verification Result | ++=================================================================================================+ +| cargo_autoharness_include | include::simple | #[kani::proof] | Success | ++---------------------------+-------------------+---------------------------+---------------------+ Complete - 1 successfully verified functions, 0 failures, 1 total. diff --git a/tests/script-based-pre/cargo_autoharness_exclude/exclude.sh b/tests/script-based-pre/cargo_autoharness_exclude/exclude.sh index 481a8b73f5ee..a18bd9ad4e29 100755 --- a/tests/script-based-pre/cargo_autoharness_exclude/exclude.sh +++ b/tests/script-based-pre/cargo_autoharness_exclude/exclude.sh @@ -2,4 +2,4 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -cargo kani autoharness -Z autoharness --exclude-function exclude +cargo kani autoharness -Z autoharness --exclude-pattern exclude diff --git a/tests/script-based-pre/cargo_autoharness_exclude/src/lib.rs b/tests/script-based-pre/cargo_autoharness_exclude/src/lib.rs index 7757239126c0..39676ed697ee 100644 --- a/tests/script-based-pre/cargo_autoharness_exclude/src/lib.rs +++ b/tests/script-based-pre/cargo_autoharness_exclude/src/lib.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // // Test that the automatic harness generation feature selects functions correctly -// when --exclude-function is provided. +// when --exclude-pattern is provided. mod include { fn simple(x: u8, _y: u16) -> u8 { diff --git a/tests/script-based-pre/cargo_autoharness_filter/filter.expected b/tests/script-based-pre/cargo_autoharness_filter/filter.expected index a7bc89c2116c..f00cd9c79404 100644 --- a/tests/script-based-pre/cargo_autoharness_filter/filter.expected +++ b/tests/script-based-pre/cargo_autoharness_filter/filter.expected @@ -1,113 +1,113 @@ Kani generated automatic harnesses for 42 function(s): -+----------------------------------------------+ -| Selected Function | -+==============================================+ -| yes_harness::empty_body | -|----------------------------------------------| -| yes_harness::f_array | -|----------------------------------------------| -| yes_harness::f_bool | -|----------------------------------------------| -| yes_harness::f_char | -|----------------------------------------------| -| yes_harness::f_derives_arbitrary | -|----------------------------------------------| -| yes_harness::f_f128 | -|----------------------------------------------| -| yes_harness::f_f16 | -|----------------------------------------------| -| yes_harness::f_f32 | -|----------------------------------------------| -| yes_harness::f_f64 | -|----------------------------------------------| -| yes_harness::f_i128 | -|----------------------------------------------| -| yes_harness::f_i16 | -|----------------------------------------------| -| yes_harness::f_i32 | -|----------------------------------------------| -| yes_harness::f_i64 | -|----------------------------------------------| -| yes_harness::f_i8 | -|----------------------------------------------| -| yes_harness::f_isize | -|----------------------------------------------| -| yes_harness::f_manually_implements_arbitrary | -|----------------------------------------------| -| yes_harness::f_maybe_uninit | -|----------------------------------------------| -| yes_harness::f_multiple_args | -|----------------------------------------------| -| yes_harness::f_nonzero_i128 | -|----------------------------------------------| -| yes_harness::f_nonzero_i16 | -|----------------------------------------------| -| yes_harness::f_nonzero_i32 | -|----------------------------------------------| -| yes_harness::f_nonzero_i64 | -|----------------------------------------------| -| yes_harness::f_nonzero_i8 | -|----------------------------------------------| -| yes_harness::f_nonzero_isize | -|----------------------------------------------| -| yes_harness::f_nonzero_u128 | -|----------------------------------------------| -| yes_harness::f_nonzero_u16 | -|----------------------------------------------| -| yes_harness::f_nonzero_u32 | -|----------------------------------------------| -| yes_harness::f_nonzero_u64 | -|----------------------------------------------| -| yes_harness::f_nonzero_u8 | -|----------------------------------------------| -| yes_harness::f_nonzero_usize | -|----------------------------------------------| -| yes_harness::f_option | -|----------------------------------------------| -| yes_harness::f_phantom_data | -|----------------------------------------------| -| yes_harness::f_phantom_pinned | -|----------------------------------------------| -| yes_harness::f_result | -|----------------------------------------------| -| yes_harness::f_tuple | -|----------------------------------------------| -| yes_harness::f_u128 | -|----------------------------------------------| -| yes_harness::f_u16 | -|----------------------------------------------| -| yes_harness::f_u32 | -|----------------------------------------------| -| yes_harness::f_u64 | -|----------------------------------------------| -| yes_harness::f_u8 | -|----------------------------------------------| -| yes_harness::f_unsupported_return_type | -|----------------------------------------------| -| yes_harness::f_usize | -+----------------------------------------------+ ++--------------------------+----------------------------------------------+ +| Crate | Selected Function | ++=========================================================================+ +| cargo_autoharness_filter | yes_harness::empty_body | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_array | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_bool | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_char | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_derives_arbitrary | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_f128 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_f16 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_f32 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_f64 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_i128 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_i16 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_i32 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_i64 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_i8 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_isize | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_manually_implements_arbitrary | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_maybe_uninit | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_multiple_args | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_i128 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_i16 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_i32 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_i64 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_i8 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_isize | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_u128 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_u16 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_u32 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_u64 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_u8 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_usize | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_option | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_phantom_data | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_phantom_pinned | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_result | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_tuple | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_u128 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_u16 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_u32 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_u64 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_u8 | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_unsupported_return_type | +|--------------------------+----------------------------------------------| +| cargo_autoharness_filter | yes_harness::f_usize | ++--------------------------+----------------------------------------------+ Kani did not generate automatic harnesses for 8 function(s). If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832 -+----------------------------------------+------------------------------------------------------------------------------+ -| Skipped Function | Reason for Skipping | -+=======================================================================================================================+ -| no_harness::doesnt_implement_arbitrary | Missing Arbitrary implementation for argument(s) x: DoesntImplementArbitrary | -|----------------------------------------+------------------------------------------------------------------------------| -| no_harness::unsupported_const_pointer | Missing Arbitrary implementation for argument(s) _y: *const i32 | -|----------------------------------------+------------------------------------------------------------------------------| -| no_harness::unsupported_generic | Generic Function | -|----------------------------------------+------------------------------------------------------------------------------| -| no_harness::unsupported_mut_pointer | Missing Arbitrary implementation for argument(s) _y: *mut i32 | -|----------------------------------------+------------------------------------------------------------------------------| -| no_harness::unsupported_no_arg_name | Missing Arbitrary implementation for argument(s) _: &() | -|----------------------------------------+------------------------------------------------------------------------------| -| no_harness::unsupported_ref | Missing Arbitrary implementation for argument(s) _y: &i32 | -|----------------------------------------+------------------------------------------------------------------------------| -| no_harness::unsupported_slice | Missing Arbitrary implementation for argument(s) _y: &[u8] | -|----------------------------------------+------------------------------------------------------------------------------| -| no_harness::unsupported_vec | Missing Arbitrary implementation for argument(s) _y: std::vec::Vec | -+----------------------------------------+------------------------------------------------------------------------------+ ++--------------------------+----------------------------------------+------------------------------------------------------------------------------+ +| Crate | Skipped Function | Reason for Skipping | ++==================================================================================================================================================+ +| cargo_autoharness_filter | no_harness::doesnt_implement_arbitrary | Missing Arbitrary implementation for argument(s) x: DoesntImplementArbitrary | +|--------------------------+----------------------------------------+------------------------------------------------------------------------------| +| cargo_autoharness_filter | no_harness::unsupported_const_pointer | Missing Arbitrary implementation for argument(s) _y: *const i32 | +|--------------------------+----------------------------------------+------------------------------------------------------------------------------| +| cargo_autoharness_filter | no_harness::unsupported_generic | Generic Function | +|--------------------------+----------------------------------------+------------------------------------------------------------------------------| +| cargo_autoharness_filter | no_harness::unsupported_mut_pointer | Missing Arbitrary implementation for argument(s) _y: *mut i32 | +|--------------------------+----------------------------------------+------------------------------------------------------------------------------| +| cargo_autoharness_filter | no_harness::unsupported_no_arg_name | Missing Arbitrary implementation for argument(s) _: &() | +|--------------------------+----------------------------------------+------------------------------------------------------------------------------| +| cargo_autoharness_filter | no_harness::unsupported_ref | Missing Arbitrary implementation for argument(s) _y: &i32 | +|--------------------------+----------------------------------------+------------------------------------------------------------------------------| +| cargo_autoharness_filter | no_harness::unsupported_slice | Missing Arbitrary implementation for argument(s) _y: &[u8] | +|--------------------------+----------------------------------------+------------------------------------------------------------------------------| +| cargo_autoharness_filter | no_harness::unsupported_vec | Missing Arbitrary implementation for argument(s) _y: std::vec::Vec | ++--------------------------+----------------------------------------+------------------------------------------------------------------------------+ Autoharness: Checking function yes_harness::f_tuple against all possible inputs... Autoharness: Checking function yes_harness::f_maybe_uninit against all possible inputs... @@ -156,91 +156,91 @@ Manual Harness Summary: No proof harnesses (functions with #[kani::proof]) were found to verify. Autoharness Summary: -+----------------------------------------------+---------------------------+---------------------+ -| Selected Function | Kind of Automatic Harness | Verification Result | -+================================================================================================+ -| yes_harness::empty_body | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_array | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_bool | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_char | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_derives_arbitrary | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_f128 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_f16 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_f32 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_f64 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_i128 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_i16 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_i32 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_i64 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_i8 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_isize | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_manually_implements_arbitrary | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_maybe_uninit | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_multiple_args | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_i128 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_i16 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_i32 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_i64 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_i8 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_isize | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_u128 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_u16 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_u32 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_u64 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_u8 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_nonzero_usize | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_option | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_phantom_data | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_phantom_pinned | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_result | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_tuple | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_u128 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_u16 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_u32 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_u64 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_u8 | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_unsupported_return_type | #[kani::proof] | Success | -|----------------------------------------------+---------------------------+---------------------| -| yes_harness::f_usize | #[kani::proof] | Success | -+----------------------------------------------+---------------------------+---------------------+ ++--------------------------+----------------------------------------------+---------------------------+---------------------+ +| Crate | Selected Function | Kind of Automatic Harness | Verification Result | ++===========================================================================================================================+ +| cargo_autoharness_filter | yes_harness::empty_body | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_array | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_bool | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_char | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_derives_arbitrary | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_f128 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_f16 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_f32 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_f64 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_i128 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_i16 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_i32 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_i64 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_i8 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_isize | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_manually_implements_arbitrary | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_maybe_uninit | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_multiple_args | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_i128 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_i16 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_i32 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_i64 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_i8 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_isize | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_u128 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_u16 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_u32 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_u64 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_u8 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_nonzero_usize | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_option | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_phantom_data | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_phantom_pinned | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_result | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_tuple | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_u128 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_u16 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_u32 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_u64 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_u8 | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_unsupported_return_type | #[kani::proof] | Success | +|--------------------------+----------------------------------------------+---------------------------+---------------------| +| cargo_autoharness_filter | yes_harness::f_usize | #[kani::proof] | Success | ++--------------------------+----------------------------------------------+---------------------------+---------------------+ Complete - 42 successfully verified functions, 0 failures, 42 total. diff --git a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected index d9969a0f38c9..b139d5777928 100644 --- a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected +++ b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.expected @@ -1,17 +1,17 @@ Kani generated automatic harnesses for 5 function(s): -+-------------------------+ -| Selected Function | -+=========================+ -| integer_overflow | -|-------------------------| -| oob_safe_array_access | -|-------------------------| -| oob_unsafe_array_access | -|-------------------------| -| panic | -|-------------------------| -| unchecked_mul | -+-------------------------+ ++----------------------------------+-------------------------+ +| Crate | Selected Function | ++============================================================+ +| cargo_autoharness_harnesses_fail | integer_overflow | +|----------------------------------+-------------------------| +| cargo_autoharness_harnesses_fail | oob_safe_array_access | +|----------------------------------+-------------------------| +| cargo_autoharness_harnesses_fail | oob_unsafe_array_access | +|----------------------------------+-------------------------| +| cargo_autoharness_harnesses_fail | panic | +|----------------------------------+-------------------------| +| cargo_autoharness_harnesses_fail | unchecked_mul | ++----------------------------------+-------------------------+ Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). @@ -73,19 +73,19 @@ Verification failed for - oob_safe_array_access_harness Complete - 0 successfully verified harnesses, 5 failures, 5 total. Autoharness Summary: -+-------------------------+---------------------------+---------------------+ -| Selected Function | Kind of Automatic Harness | Verification Result | -+===========================================================================+ -| integer_overflow | #[kani::proof] | Failure | -|-------------------------+---------------------------+---------------------| -| oob_safe_array_access | #[kani::proof] | Failure | -|-------------------------+---------------------------+---------------------| -| oob_unsafe_array_access | #[kani::proof] | Failure | -|-------------------------+---------------------------+---------------------| -| panic | #[kani::proof] | Failure | -|-------------------------+---------------------------+---------------------| -| unchecked_mul | #[kani::proof] | Failure | -+-------------------------+---------------------------+---------------------+ ++----------------------------------+-------------------------+---------------------------+---------------------+ +| Crate | Selected Function | Kind of Automatic Harness | Verification Result | ++==============================================================================================================+ +| cargo_autoharness_harnesses_fail | integer_overflow | #[kani::proof] | Failure | +|----------------------------------+-------------------------+---------------------------+---------------------| +| cargo_autoharness_harnesses_fail | oob_safe_array_access | #[kani::proof] | Failure | +|----------------------------------+-------------------------+---------------------------+---------------------| +| cargo_autoharness_harnesses_fail | oob_unsafe_array_access | #[kani::proof] | Failure | +|----------------------------------+-------------------------+---------------------------+---------------------| +| cargo_autoharness_harnesses_fail | panic | #[kani::proof] | Failure | +|----------------------------------+-------------------------+---------------------------+---------------------| +| cargo_autoharness_harnesses_fail | unchecked_mul | #[kani::proof] | Failure | ++----------------------------------+-------------------------+---------------------------+---------------------+ Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). Complete - 0 successfully verified functions, 5 failures, 5 total. diff --git a/tests/script-based-pre/cargo_autoharness_include/include.expected b/tests/script-based-pre/cargo_autoharness_include/include.expected index 1b0085fbcf28..9c006d210262 100644 --- a/tests/script-based-pre/cargo_autoharness_include/include.expected +++ b/tests/script-based-pre/cargo_autoharness_include/include.expected @@ -1,20 +1,19 @@ Kani generated automatic harnesses for 1 function(s): -+-------------------+ -| Selected Function | -+===================+ -| include::simple | -+-------------------+ ++---------------------------+-------------------+ +| Crate | Selected Function | ++===============================================+ +| cargo_autoharness_include | include::simple | ++---------------------------+-------------------+ Kani did not generate automatic harnesses for 2 function(s). If you believe that the provided reason is incorrect and Kani should have generated an automatic harness, please comment on this issue: https://github.com/model-checking/kani/issues/3832 -+------------------+--------------------------------+ -| Skipped Function | Reason for Skipping | -+===================================================+ -| excluded::simple | Did not match provided filters | -|------------------+--------------------------------| -| include::generic | Generic Function | -+------------------+--------------------------------+ - ++---------------------------+------------------+--------------------------------+ +| Crate | Skipped Function | Reason for Skipping | ++===============================================================================+ +| cargo_autoharness_include | excluded::simple | Did not match provided filters | +|---------------------------+------------------+--------------------------------| +| cargo_autoharness_include | include::generic | Generic Function | ++---------------------------+------------------+--------------------------------+ Autoharness: Checking function include::simple against all possible inputs... VERIFICATION:- SUCCESSFUL @@ -22,9 +21,9 @@ Manual Harness Summary: No proof harnesses (functions with #[kani::proof]) were found to verify. Autoharness Summary: -+-------------------+---------------------------+---------------------+ -| Selected Function | Kind of Automatic Harness | Verification Result | -+=====================================================================+ -| include::simple | #[kani::proof] | Success | -+-------------------+---------------------------+---------------------+ ++---------------------------+-------------------+---------------------------+---------------------+ +| Crate | Selected Function | Kind of Automatic Harness | Verification Result | ++=================================================================================================+ +| cargo_autoharness_include | include::simple | #[kani::proof] | Success | ++---------------------------+-------------------+---------------------------+---------------------+ Complete - 1 successfully verified functions, 0 failures, 1 total. diff --git a/tests/script-based-pre/cargo_autoharness_include/include.sh b/tests/script-based-pre/cargo_autoharness_include/include.sh index 7879013c5717..a731584349bf 100755 --- a/tests/script-based-pre/cargo_autoharness_include/include.sh +++ b/tests/script-based-pre/cargo_autoharness_include/include.sh @@ -2,4 +2,4 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -cargo kani autoharness -Z autoharness --include-function include +cargo kani autoharness -Z autoharness --include-pattern cargo_autoharness_include::include diff --git a/tests/script-based-pre/cargo_autoharness_include/src/lib.rs b/tests/script-based-pre/cargo_autoharness_include/src/lib.rs index 68b30bf7afe8..135f86f76874 100644 --- a/tests/script-based-pre/cargo_autoharness_include/src/lib.rs +++ b/tests/script-based-pre/cargo_autoharness_include/src/lib.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // // Test that the automatic harness generation feature selects functions correctly -// when --include-function is provided. +// when --include-pattern is provided. // Each function inside this module matches the filter. mod include { diff --git a/tests/script-based-pre/cargo_autoharness_list/list.expected b/tests/script-based-pre/cargo_autoharness_list/list.expected index 06eccba6fef3..5837278d0f00 100644 --- a/tests/script-based-pre/cargo_autoharness_list/list.expected +++ b/tests/script-based-pre/cargo_autoharness_list/list.expected @@ -1,13 +1,13 @@ Kani generated automatic harnesses for 3 function(s): -+---------------------------+ -| Selected Function | -+===========================+ -| f_u8 | -|---------------------------| -| has_recursion_gcd | -|---------------------------| -| verify::has_recursion_gcd | -+---------------------------+ ++------------------------+---------------------------+ +| Crate | Selected Function | ++====================================================+ +| cargo_autoharness_list | f_u8 | +|------------------------+---------------------------| +| cargo_autoharness_list | has_recursion_gcd | +|------------------------+---------------------------| +| cargo_autoharness_list | verify::has_recursion_gcd | ++------------------------+---------------------------+ Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). diff --git a/tests/script-based-pre/cargo_autoharness_termination_timeout/termination_timeout.expected b/tests/script-based-pre/cargo_autoharness_termination_timeout/termination_timeout.expected index 1729bc2b823c..26fd6606f31a 100644 --- a/tests/script-based-pre/cargo_autoharness_termination_timeout/termination_timeout.expected +++ b/tests/script-based-pre/cargo_autoharness_termination_timeout/termination_timeout.expected @@ -1,9 +1,9 @@ Kani generated automatic harnesses for 1 function(s): -+-----------------------+ -| Selected Function | -+=======================+ -| check_harness_timeout | -+-----------------------+ ++---------------------------------------+-----------------------+ +| Crate | Selected Function | ++===============================================================+ +| cargo_autoharness_termination_timeout | check_harness_timeout | ++---------------------------------------+-----------------------+ Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). Autoharness: Checking function check_harness_timeout against all possible inputs... @@ -15,10 +15,10 @@ Manual Harness Summary: No proof harnesses (functions with #[kani::proof]) were found to verify. Autoharness Summary: -+-----------------------+---------------------------+---------------------+ -| Selected Function | Kind of Automatic Harness | Verification Result | -+=========================================================================+ -| check_harness_timeout | #[kani::proof] | Failure | -+-----------------------+---------------------------+---------------------+ ++---------------------------------------+-----------------------+---------------------------+---------------------+ +| Crate | Selected Function | Kind of Automatic Harness | Verification Result | ++=================================================================================================================+ +| cargo_autoharness_termination_timeout | check_harness_timeout | #[kani::proof] | Failure | ++---------------------------------------+-----------------------+---------------------------+---------------------+ Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). diff --git a/tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.expected b/tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.expected index 1e198bb02988..6d0c229e436a 100644 --- a/tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.expected +++ b/tests/script-based-pre/cargo_autoharness_termination_unwind/termination_unwind.expected @@ -1,11 +1,11 @@ Kani generated automatic harnesses for 2 function(s): -+-------------------+ -| Selected Function | -+===================+ -| gcd_recursion | -|-------------------| -| infinite_loop | -+-------------------+ ++--------------------------------------+-------------------+ +| Crate | Selected Function | ++==========================================================+ +| cargo_autoharness_termination_unwind | gcd_recursion | +|--------------------------------------+-------------------| +| cargo_autoharness_termination_unwind | infinite_loop | ++--------------------------------------+-------------------+ Skipped Functions: None. Kani generated automatic harnesses for all functions in the available crate(s). Autoharness: Checking function gcd_recursion against all possible inputs... @@ -38,12 +38,12 @@ Manual Harness Summary: No proof harnesses (functions with #[kani::proof]) were found to verify. Autoharness Summary: -+-------------------+---------------------------+---------------------+ -| Selected Function | Kind of Automatic Harness | Verification Result | -+=====================================================================+ -| gcd_recursion | #[kani::proof] | Failure | -|-------------------+---------------------------+---------------------| -| infinite_loop | #[kani::proof] | Failure | -+-------------------+---------------------------+---------------------+ ++--------------------------------------+-------------------+---------------------------+---------------------+ +| Crate | Selected Function | Kind of Automatic Harness | Verification Result | ++============================================================================================================+ +| cargo_autoharness_termination_unwind | gcd_recursion | #[kani::proof] | Failure | +|--------------------------------------+-------------------+---------------------------+---------------------| +| cargo_autoharness_termination_unwind | infinite_loop | #[kani::proof] | Failure | ++--------------------------------------+-------------------+---------------------------+---------------------+ Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). diff --git a/tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.expected b/tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.expected index bd15576d5369..cc8e9b32ca14 100644 --- a/tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.expected +++ b/tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.expected @@ -1,21 +1,21 @@ Kani generated automatic harnesses for 7 function(s): -+----------------------------+ -| Selected Function | -+============================+ -| Duration::abs_diff | -|----------------------------| -| Duration::checked_add | -|----------------------------| -| Duration::checked_sub | -|----------------------------| -| Duration::new | -|----------------------------| -| Nanoseconds | -|----------------------------| -| Nanoseconds::as_inner | -|----------------------------| -| Nanoseconds::new_unchecked | -+----------------------------+ ++----------------------------------+----------------------------+ +| Crate | Selected Function | ++===============================================================+ +| cargo_autoharness_type_invariant | Duration::abs_diff | +|----------------------------------+----------------------------| +| cargo_autoharness_type_invariant | Duration::checked_add | +|----------------------------------+----------------------------| +| cargo_autoharness_type_invariant | Duration::checked_sub | +|----------------------------------+----------------------------| +| cargo_autoharness_type_invariant | Duration::new | +|----------------------------------+----------------------------| +| cargo_autoharness_type_invariant | Nanoseconds | +|----------------------------------+----------------------------| +| cargo_autoharness_type_invariant | Nanoseconds::as_inner | +|----------------------------------+----------------------------| +| cargo_autoharness_type_invariant | Nanoseconds::new_unchecked | ++----------------------------------+----------------------------+ Autoharness: Checking function Nanoseconds::new_unchecked's contract against all possible inputs... Nanoseconds::new_unchecked\ @@ -88,23 +88,23 @@ std::option::expect_failed\ Autoharness Summary: -+----------------------------+-----------------------------+---------------------+ -| Selected Function | Kind of Automatic Harness | Verification Result | -+================================================================================+ -| Duration::abs_diff | #[kani::proof] | Success | -|----------------------------+-----------------------------+---------------------| -| Duration::checked_add | #[kani::proof_for_contract] | Success | -|----------------------------+-----------------------------+---------------------| -| Duration::checked_sub | #[kani::proof_for_contract] | Success | -|----------------------------+-----------------------------+---------------------| -| Nanoseconds | #[kani::proof] | Success | -|----------------------------+-----------------------------+---------------------| -| Nanoseconds::as_inner | #[kani::proof] | Success | -|----------------------------+-----------------------------+---------------------| -| Nanoseconds::new_unchecked | #[kani::proof_for_contract] | Success | -|----------------------------+-----------------------------+---------------------| -| Duration::new | #[kani::proof_for_contract] | Failure | -+----------------------------+-----------------------------+---------------------+ ++----------------------------------+----------------------------+-----------------------------+---------------------+ +| Crate | Selected Function | Kind of Automatic Harness | Verification Result | ++===================================================================================================================+ +| cargo_autoharness_type_invariant | Duration::abs_diff | #[kani::proof] | Success | +|----------------------------------+----------------------------+-----------------------------+---------------------| +| cargo_autoharness_type_invariant | Duration::checked_add | #[kani::proof_for_contract] | Success | +|----------------------------------+----------------------------+-----------------------------+---------------------| +| cargo_autoharness_type_invariant | Duration::checked_sub | #[kani::proof_for_contract] | Success | +|----------------------------------+----------------------------+-----------------------------+---------------------| +| cargo_autoharness_type_invariant | Nanoseconds | #[kani::proof] | Success | +|----------------------------------+----------------------------+-----------------------------+---------------------| +| cargo_autoharness_type_invariant | Nanoseconds::as_inner | #[kani::proof] | Success | +|----------------------------------+----------------------------+-----------------------------+---------------------| +| cargo_autoharness_type_invariant | Nanoseconds::new_unchecked | #[kani::proof_for_contract] | Success | +|----------------------------------+----------------------------+-----------------------------+---------------------| +| cargo_autoharness_type_invariant | Duration::new | #[kani::proof_for_contract] | Failure | ++----------------------------------+----------------------------+-----------------------------+---------------------+ Note that `kani autoharness` sets default --harness-timeout of 60s and --default-unwind of 20. If verification failed because of timing out or too low of an unwinding bound, try passing larger values for these arguments (or, if possible, writing a loop contract). Complete - 6 successfully verified functions, 1 failures, 7 total. diff --git a/tests/script-based-pre/kani_autoharness_exclude_precedence/config.yml b/tests/script-based-pre/kani_autoharness_exclude_precedence/config.yml new file mode 100644 index 000000000000..16a0fe88bccd --- /dev/null +++ b/tests/script-based-pre/kani_autoharness_exclude_precedence/config.yml @@ -0,0 +1,3 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: precedence.sh diff --git a/tests/script-based-pre/kani_autoharness_exclude_precedence/precedence.sh b/tests/script-based-pre/kani_autoharness_exclude_precedence/precedence.sh new file mode 100755 index 000000000000..38b92b88c740 --- /dev/null +++ b/tests/script-based-pre/kani_autoharness_exclude_precedence/precedence.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +# Test that --include-pattern and --exclude-pattern work as expected when provided together. + +set -e + +# Define all function paths +FUNCTIONS=( + "foo::foo_function" + "foo::bar::bar_function" + "foo::bar::foo_bar_function" + "foo::baz::foo_baz_function" + "other::regular_function" + "other::with_bar_name" + "foo_top_level" + "bar_top_level" +) + +# Check if a function appears in the "Selected Function" table +check_selected() { + local output="$1" + local function_name="$2" + if echo "$output" | grep -q "| $function_name *|"; then + return 0 + else + return 1 + fi +} + +# Check if a function appears in the "Skipped Function" table +check_skipped() { + local output="$1" + local function_name="$2" + if echo "$output" | grep -q "| $function_name *|.*Did not match provided filters"; then + return 0 + else + return 1 + fi +} + +# Check that the warning message gets printed for the patterns that are mutually exclusive (no functions get selected) +check_warning() { + local output="$1" + local should_warn="$2" + local warning_present=$(echo "$output" | grep -c "warning: Include pattern" || true) + + if [ "$should_warn" = true ] && [ "$warning_present" -eq 0 ]; then + echo "ERROR: expected printed warning about conflicting --include-pattern and --exclude-pattern flags" + return 1 + elif [ "$should_warn" = false ] && [ "$warning_present" -gt 0 ]; then + echo "ERROR: Got unexpected warning message" + return 1 + fi + return 0 +} + + +# Helper function to verify functions against include/exclude patterns +verify_functions() { + local output="$1" + local include_pattern="$2" + local exclude_pattern="$3" + + for func in "${FUNCTIONS[@]}"; do + # If the function name matches the include pattern and not the exclude pattern, it should be selected + if echo "$func" | grep -q "$include_pattern" && ! echo "$func" | grep -q "$exclude_pattern"; then + if ! check_selected "$output" "$func"; then + echo "ERROR: Expected $func to be selected" + exit 1 + fi + # Otherwise, it should be skipped + else + if ! check_skipped "$output" "$func"; then + echo "ERROR: Expected $func to be skipped" + exit 1 + fi + fi + done +} + +# Test cases +test_cases=( + "include 'foo' exclude 'foo::bar'" + "include 'foo' exclude 'bar'" + "include 'foo::bar' exclude 'bar'" + "include 'foo' exclude 'foo'" +) + +include_patterns=( + "foo" + "foo" + "foo::bar" + "foo" +) + +exclude_patterns=( + "foo::bar" + "bar" + "bar" + "foo" +) + +# Whether each test case should produce a warning about no functions being selected +should_warn=( + false + false + true + true +) + +for i in "${!test_cases[@]}"; do + echo "Testing: ${test_cases[$i]}" + output=$(kani autoharness -Z autoharness src/lib.rs --include-pattern "${include_patterns[$i]}" --exclude-pattern "${exclude_patterns[$i]}" --only-codegen) + echo "$output" + + if ! check_warning "$output" "${should_warn[$i]}"; then + exit 1 + fi + + verify_functions "$output" "${include_patterns[$i]}" "${exclude_patterns[$i]}" +done + +echo "All tests passed!" \ No newline at end of file diff --git a/tests/script-based-pre/kani_autoharness_exclude_precedence/src/lib.rs b/tests/script-based-pre/kani_autoharness_exclude_precedence/src/lib.rs new file mode 100644 index 000000000000..8fb3d7957ae3 --- /dev/null +++ b/tests/script-based-pre/kani_autoharness_exclude_precedence/src/lib.rs @@ -0,0 +1,26 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +mod foo { + fn foo_function() {} + + mod bar { + fn bar_function() {} + + fn foo_bar_function() {} + } + + mod baz { + fn foo_baz_function() {} + } +} + +mod other { + fn regular_function() {} + + fn with_bar_name() {} +} + +fn foo_top_level() {} + +fn bar_top_level() {} From 484cd308415b5522ab69033ad4b3069f974894b8 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Thu, 17 Apr 2025 19:21:48 -0400 Subject: [PATCH 106/161] CI Formatting Fixes (#4028) The majority of the changes in this PR were made automatically by running the command specified in 5314a5d772cf4e8f152334dcbaa081336b631e02, so the actual diff to review is much smaller than it initially appears. 1. If `--check` was provided to`kani-fmt.sh`, it would run `cargo fmt --check` but not `rustfmt --check`. This meant that our regression would catch formatting errors from `cargo fmt` but not `rustfmt` (it would just format the problematic files, which isn't useful in CI--we want it to fail instead). So add the `--check` flag to the `rustfmt` calls as well. 2. Run clippy with `kani_sysroot` feature enabled as well to catch more formatting errors. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .github/workflows/format-check.yml | 1 + .../src/irep/goto_binary_serde.rs | 18 +- cprover_bindings/src/irep/symbol_table.rs | 6 + .../codegen/intrinsic.rs | 2 +- .../codegen_cprover_gotoc/codegen/operand.rs | 2 +- .../codegen_cprover_gotoc/codegen/rvalue.rs | 6 +- .../src/codegen_cprover_gotoc/codegen/span.rs | 6 +- .../codegen/statement.rs | 16 +- .../compiler_interface.rs | 25 +-- kani-compiler/src/kani_middle/attributes.rs | 22 +-- .../src/kani_middle/codegen_units.rs | 6 +- kani-compiler/src/kani_middle/coercion.rs | 18 +- .../points_to/points_to_analysis.rs | 2 +- kani-compiler/src/kani_middle/reachability.rs | 2 +- kani-compiler/src/kani_middle/resolve.rs | 6 +- .../kani_middle/resolve/type_resolution.rs | 2 +- .../src/kani_middle/transform/automatic.rs | 2 +- .../delayed_ub/instrumentation_visitor.rs | 2 +- .../kani_middle/transform/check_uninit/mod.rs | 24 +-- .../check_uninit/ptr_uninit/uninit_visitor.rs | 10 +- .../transform/check_uninit/ty_layout.rs | 10 +- .../src/kani_middle/transform/check_values.rs | 8 +- .../src/kani_middle/transform/contracts.rs | 8 +- .../kani_middle/transform/kani_intrinsics.rs | 12 +- .../src/kani_middle/transform/mod.rs | 15 +- kani-driver/src/args/mod.rs | 4 +- kani-driver/src/call_goto_instrument.rs | 6 +- kani-driver/src/cbmc_output_parser.rs | 4 +- kani-driver/src/cbmc_property_renderer.rs | 4 +- .../src/concrete_playback/test_generator.rs | 19 +- kani-driver/src/harness_runner.rs | 6 +- kani-driver/src/project.rs | 2 +- kani-driver/src/session.rs | 2 +- library/kani_macros/src/derive.rs | 22 +-- .../src/sysroot/contracts/assert.rs | 2 +- .../src/sysroot/contracts/check.rs | 7 +- .../src/sysroot/contracts/helpers.rs | 9 +- .../kani_macros/src/sysroot/contracts/mod.rs | 2 +- .../src/sysroot/contracts/replace.rs | 4 +- .../src/sysroot/contracts/shared.rs | 6 +- .../src/sysroot/loop_contracts/mod.rs | 168 ++++++++---------- scripts/kani-fmt.sh | 13 +- .../loop-contract/loop_with_true_invariant.rs | 4 +- tools/build-kani/src/main.rs | 6 +- tools/build-kani/src/sysroot.rs | 6 +- tools/compiletest/src/header.rs | 6 + tools/compiletest/src/main.rs | 12 +- tools/compiletest/src/runtest.rs | 5 +- tools/kani-cov/src/args.rs | 6 +- tools/kani-cov/src/merge.rs | 4 +- tools/scanner/src/analysis.rs | 2 +- 51 files changed, 286 insertions(+), 276 deletions(-) diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml index c75b457073b8..a764132034e9 100644 --- a/.github/workflows/format-check.yml +++ b/.github/workflows/format-check.yml @@ -48,6 +48,7 @@ jobs: - name: 'Run Clippy' run: | cargo clippy --workspace -- -D warnings + RUSTFLAGS="--cfg=kani_sysroot" cargo clippy --workspace -- -D warnings - name: 'Print Clippy Statistics' run: | diff --git a/cprover_bindings/src/irep/goto_binary_serde.rs b/cprover_bindings/src/irep/goto_binary_serde.rs index 56578cc87aa4..8bc036b3c07f 100644 --- a/cprover_bindings/src/irep/goto_binary_serde.rs +++ b/cprover_bindings/src/irep/goto_binary_serde.rs @@ -275,7 +275,7 @@ impl IrepNumbering { return self.inv_cache.index[*number]; } // This is where the key gets its unique number assigned. - let number = self.inv_cache.add_key(&key); + let number = self.inv_cache.add_key(key); self.cache.insert(key.clone(), number); self.inv_cache.index[number] } @@ -449,17 +449,17 @@ where self.write_usize_varenc(num); if self.is_first_write_irep(num) { - let id = &self.numbering.id(&irep); + let id = &self.numbering.id(irep); self.write_numbered_string_ref(id); - for sub_idx in 0..(self.numbering.nof_sub(&irep)) { + for sub_idx in 0..(self.numbering.nof_sub(irep)) { self.write_u8(b'S'); - self.write_numbered_irep_ref(&self.numbering.sub(&irep, sub_idx)); + self.write_numbered_irep_ref(&self.numbering.sub(irep, sub_idx)); } - for named_sub_idx in 0..(self.numbering.nof_named_sub(&irep)) { + for named_sub_idx in 0..(self.numbering.nof_named_sub(irep)) { self.write_u8(b'N'); - let (k, v) = self.numbering.named_sub(&irep, named_sub_idx); + let (k, v) = self.numbering.named_sub(irep, named_sub_idx); self.write_numbered_string_ref(&k); self.write_numbered_irep_ref(&v); } @@ -1339,7 +1339,7 @@ mod tests { let mut writer = BufWriter::new(&mut vec); let mut serializer = GotoBinarySerializer::new(&mut writer); for string in strings.iter() { - serializer.write_string_ref(&string); + serializer.write_string_ref(string); } println!("Serializer stats {:?}", serializer.get_stats()); } @@ -1365,12 +1365,12 @@ mod tests { let mut serializer = GotoBinarySerializer::new(&mut writer); // Number an irep - let num1 = serializer.numbering.number_irep(&irep1); + let num1 = serializer.numbering.number_irep(irep1); // Number an structurally different irep let identifiers2 = vec!["foo", "bar", "baz", "different", "zab", "rab", "oof"]; let irep2 = &fold_with_op(&identifiers2, IrepId::And); - let num2 = serializer.numbering.number_irep(&irep2); + let num2 = serializer.numbering.number_irep(irep2); // Check that they have the different numbers. assert_ne!(num1, num2); diff --git a/cprover_bindings/src/irep/symbol_table.rs b/cprover_bindings/src/irep/symbol_table.rs index 7e1ae6b40ec6..cee110075ff5 100644 --- a/cprover_bindings/src/irep/symbol_table.rs +++ b/cprover_bindings/src/irep/symbol_table.rs @@ -12,6 +12,12 @@ pub struct SymbolTable { } /// Constructors +impl Default for SymbolTable { + fn default() -> Self { + Self::new() + } +} + impl SymbolTable { pub fn new() -> SymbolTable { SymbolTable { symbol_table: BTreeMap::new() } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index bf3671bb3a16..579e2b083f05 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -564,7 +564,7 @@ impl GotocCtx<'_> { self.intrinsics_typecheck_fail(span, "ctpop", "integer type", arg_rust_ty) } else { let loc = self.codegen_span_stable(span); - self.codegen_expr_to_place_stable(&target_place, arg.popcount(), loc) + self.codegen_expr_to_place_stable(target_place, arg.popcount(), loc) } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs index 1a110d8dab24..0cfa0e6c1352 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs @@ -335,7 +335,7 @@ impl<'tcx> GotocCtx<'tcx> { let typ = self.codegen_ty_stable(ty); let operation_name = "C string literal"; self.codegen_unimplemented_expr( - &operation_name, + operation_name, typ, loc, "https://github.com/model-checking/kani/issues/2549", diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index f2327b51b6ad..a3aa20f05b1b 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -731,7 +731,7 @@ impl GotocCtx<'_> { } } } - AggregateKind::Coroutine(_, _, _) => self.codegen_rvalue_coroutine(&operands, res_ty), + AggregateKind::Coroutine(_, _, _) => self.codegen_rvalue_coroutine(operands, res_ty), AggregateKind::CoroutineClosure(_, _) => { let ty = self.codegen_ty_stable(res_ty); self.codegen_unimplemented_expr( @@ -751,10 +751,10 @@ impl GotocCtx<'_> { Rvalue::Use(p) => self.codegen_operand_stable(p), Rvalue::Repeat(op, sz) => self.codegen_rvalue_repeat(op, sz, loc), Rvalue::Ref(_, _, p) | Rvalue::AddressOf(_, p) => { - let place_ref = self.codegen_place_ref_stable(&p, loc); + let place_ref = self.codegen_place_ref_stable(p, loc); let place_ref_type = place_ref.typ().clone(); match self.codegen_raw_ptr_deref_validity_check( - &p, + p, place_ref.clone(), self.place_ty_stable(p), &loc, diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs index 549fbcf3447d..178e0b7c5d07 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs @@ -59,10 +59,8 @@ impl GotocCtx<'_> { let arg = parse_word(attr).expect( "incorrect value passed to `disable_checks`, expected a single identifier", ); - *PRAGMAS.get(arg.as_str()).expect(format!( - "attempting to disable an unexisting check, the possible options are {:?}", - PRAGMAS.keys() - ).as_str()) + *PRAGMAS.get(arg.as_str()).unwrap_or_else(|| panic!("attempting to disable an unexisting check, the possible options are {:?}", + PRAGMAS.keys())) }) .collect::>() .leak() // This is to preserve `Location` being Copy, but could blow up the memory utilization of compiler. diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 788e5f6ec31d..eabc861b8fb7 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -148,7 +148,7 @@ impl GotocCtx<'_> { // Pack the operands and their types, then call `codegen_copy` let fargs = operands.iter().map(|op| self.codegen_operand_stable(op)).collect::>(); - let farg_types = operands.map(|op| self.operand_ty_stable(&op)); + let farg_types = operands.map(|op| self.operand_ty_stable(op)); self.codegen_copy("copy_nonoverlapping", true, fargs, &farg_types, None, location) } // https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/mir/enum.NonDivergingIntrinsic.html#variant.Assume @@ -168,7 +168,7 @@ impl GotocCtx<'_> { let instance = self.current_fn().instance_stable(); let counter_data = format!("{coverage_opaque:?} ${function_name}$"); let maybe_source_region = - region_from_coverage_opaque(self.tcx, &coverage_opaque, instance); + region_from_coverage_opaque(self.tcx, coverage_opaque, instance); if let Some((source_region, file_name)) = maybe_source_region { let coverage_stmt = self.codegen_coverage(&counter_data, stmt.span, source_region, &file_name); @@ -621,8 +621,8 @@ impl GotocCtx<'_> { if def.as_intrinsic().unwrap().must_be_overridden() || !instance.has_body() { return self.codegen_funcall_of_intrinsic( instance, - &args, - &destination, + args, + destination, target.map(|bb| bb), span, ); @@ -638,10 +638,10 @@ impl GotocCtx<'_> { let mut fargs = if args.is_empty() || fn_def.fn_sig().unwrap().value.abi != Abi::RustCall { - self.codegen_funcall_args(&fn_abi, &args) + self.codegen_funcall_args(&fn_abi, args) } else { let (untupled, first_args) = args.split_last().unwrap(); - let mut fargs = self.codegen_funcall_args(&fn_abi, &first_args); + let mut fargs = self.codegen_funcall_args(&fn_abi, first_args); fargs.append( &mut self.codegen_untupled_args(untupled, &fn_abi.args[first_args.len()..]), ); @@ -688,11 +688,11 @@ impl GotocCtx<'_> { self.tcx .fn_abi_of_fn_ptr( TypingEnv::fully_monomorphized() - .as_query_input((fn_sig_internal, &List::empty())), + .as_query_input((fn_sig_internal, List::empty())), ) .unwrap(), ); - let fargs = self.codegen_funcall_args(&fn_ptr_abi, &args); + let fargs = self.codegen_funcall_args(&fn_ptr_abi, args); let func_expr = self.codegen_operand_stable(func).dereference(); // Actually generate the function call and return. Stmt::block( diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index ef32d99042c1..23bfe06ba32a 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -201,12 +201,12 @@ impl GotocCodegenBackend { // No output should be generated if user selected no_codegen. if !tcx.sess.opts.unstable_opts.no_codegen && tcx.sess.opts.output_types.should_codegen() { let pretty = self.queries.lock().unwrap().args().output_pretty_json; - write_file(&symtab_goto, ArtifactType::PrettyNameMap, &pretty_name_map, pretty); + write_file(symtab_goto, ArtifactType::PrettyNameMap, &pretty_name_map, pretty); write_goto_binary_file(symtab_goto, &gcx.symbol_table); - write_file(&symtab_goto, ArtifactType::TypeMap, &type_map, pretty); + write_file(symtab_goto, ArtifactType::TypeMap, &type_map, pretty); // If they exist, write out vtable virtual call function pointer restrictions if let Some(restrictions) = vtable_restrictions { - write_file(&symtab_goto, ArtifactType::VTableRestriction, &restrictions, pretty); + write_file(symtab_goto, ArtifactType::VTableRestriction, &restrictions, pretty); } } @@ -294,7 +294,7 @@ impl CodegenBackend for GotocCodegenBackend { // We reset the body cache for now because each codegen unit has different // configurations that affect how we transform the instance body. for harness in &unit.harnesses { - let transformer = BodyTransformation::new(&queries, tcx, &unit); + let transformer = BodyTransformation::new(&queries, tcx, unit); let model_path = units.harness_model_path(*harness).unwrap(); let is_automatic_harness = units.is_automatic_harness(harness); let contract_metadata = @@ -355,14 +355,15 @@ impl CodegenBackend for GotocCodegenBackend { for (test_fn, test_desc) in harnesses.iter().zip(descriptions.iter()) { let instance = if let MonoItem::Fn(instance) = test_fn { instance } else { continue }; - let metadata = - gen_test_metadata(tcx, *test_desc, *instance, &base_filename); + let metadata = gen_test_metadata(tcx, *test_desc, *instance, base_filename); let test_model_path = &metadata.goto_file.as_ref().unwrap(); - std::fs::copy(&model_path, test_model_path).expect(&format!( - "Failed to copy {} to {}", - model_path.display(), - test_model_path.display() - )); + std::fs::copy(&model_path, test_model_path).unwrap_or_else(|_| { + panic!( + "Failed to copy {} to {}", + model_path.display(), + test_model_path.display() + ) + }); results.harnesses.push(metadata); } } @@ -405,7 +406,7 @@ impl CodegenBackend for GotocCodegenBackend { // To avoid overriding the metadata for its verification, we skip this step when // reachability is None, even because there is nothing to record. write_file( - &base_filename, + base_filename, ArtifactType::Metadata, &results.generate_metadata(), queries.args().output_pretty_json, diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index 20d5f3ad6a4b..53f8ea408618 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -357,19 +357,19 @@ impl<'tcx> KaniAttributes<'tcx> { } match kind { KaniAttributeKind::ShouldPanic => { - expect_single(self.tcx, kind, &attrs); + expect_single(self.tcx, kind, attrs); attrs.iter().for_each(|attr| { expect_no_args(self.tcx, kind, attr); }) } KaniAttributeKind::Recursion => { - expect_single(self.tcx, kind, &attrs); + expect_single(self.tcx, kind, attrs); attrs.iter().for_each(|attr| { expect_no_args(self.tcx, kind, attr); }) } KaniAttributeKind::Solver => { - expect_single(self.tcx, kind, &attrs); + expect_single(self.tcx, kind, attrs); attrs.iter().for_each(|attr| { parse_solver(self.tcx, attr); }) @@ -378,7 +378,7 @@ impl<'tcx> KaniAttributes<'tcx> { parse_stubs(self.tcx, self.item, attrs); } KaniAttributeKind::Unwind => { - expect_single(self.tcx, kind, &attrs); + expect_single(self.tcx, kind, attrs); attrs.iter().for_each(|attr| { parse_unwind(self.tcx, attr); }) @@ -389,7 +389,7 @@ impl<'tcx> KaniAttributes<'tcx> { "`proof` and `proof_for_contract` may not be used on the same function.".to_string(), ); } - expect_single(self.tcx, kind, &attrs); + expect_single(self.tcx, kind, attrs); attrs.iter().for_each(|attr| self.check_proof_attribute(kind, attr)) } KaniAttributeKind::Unstable => attrs.iter().for_each(|attr| { @@ -401,7 +401,7 @@ impl<'tcx> KaniAttributes<'tcx> { "`proof` and `proof_for_contract` may not be used on the same function.".to_string(), ); } - expect_single(self.tcx, kind, &attrs); + expect_single(self.tcx, kind, attrs); attrs.iter().for_each(|attr| self.check_proof_attribute(kind, attr)) } KaniAttributeKind::StubVerified => { @@ -721,7 +721,7 @@ pub fn test_harness_name(tcx: TyCtxt, def: &impl CrateDef) -> String { let def_id = rustc_internal::internal(tcx, def.def_id()); let attrs = tcx.get_attrs_unchecked(def_id); let marker = attr::find_by_name(attrs, rustc_span::symbol::sym::rustc_test_marker).unwrap(); - parse_str_value(&marker).unwrap() + parse_str_value(marker).unwrap() } /// Expect the contents of this attribute to be of the format #[attribute = @@ -749,9 +749,9 @@ fn expect_single<'a>( kind: KaniAttributeKind, attributes: &'a Vec<&'a Attribute>, ) -> &'a Attribute { - let attr = attributes - .first() - .expect(&format!("expected at least one attribute {} in {attributes:?}", kind.as_ref())); + let attr = attributes.first().unwrap_or_else(|| { + panic!("expected at least one attribute {} in {attributes:?}", kind.as_ref()) + }); if attributes.len() > 1 { tcx.dcx().span_err( attr.span(), @@ -1061,7 +1061,7 @@ fn syn_attr(tcx: TyCtxt, attr: &Attribute) -> syn::Attribute { /// Parse a stable attribute using `syn`. fn syn_attr_stable(attr: &AttributeStable) -> syn::Attribute { let parser = syn::Attribute::parse_outer; - parser.parse_str(&attr.as_str()).unwrap().pop().unwrap() + parser.parse_str(attr.as_str()).unwrap().pop().unwrap() } /// Return a more user-friendly string for path by trying to remove unneeded whitespace. diff --git a/kani-compiler/src/kani_middle/codegen_units.rs b/kani-compiler/src/kani_middle/codegen_units.rs index c87cd4a52fff..3faa1a03d77c 100644 --- a/kani-compiler/src/kani_middle/codegen_units.rs +++ b/kani-compiler/src/kani_middle/codegen_units.rs @@ -318,7 +318,7 @@ fn get_all_manual_harnesses( harnesses .into_iter() .map(|harness| { - let metadata = gen_proof_metadata(tcx, harness, &base_filename); + let metadata = gen_proof_metadata(tcx, harness, base_filename); (harness, metadata) }) .collect::>() @@ -347,7 +347,7 @@ fn get_all_automatic_harnesses( .unwrap(); let metadata = gen_automatic_proof_metadata( tcx, - &base_filename, + base_filename, &fn_to_verify, harness.mangled_name(), ); @@ -441,7 +441,7 @@ fn automatic_harness_partition( &kani_any_body.blocks[0].terminator.kind { if let Some((def, args)) = func.ty(body.arg_locals()).unwrap().kind().fn_def() { - Instance::resolve(def, &args).is_ok() + Instance::resolve(def, args).is_ok() } else { false } diff --git a/kani-compiler/src/kani_middle/coercion.rs b/kani-compiler/src/kani_middle/coercion.rs index afb0ff5e6c73..5fe1e50948a5 100644 --- a/kani-compiler/src/kani_middle/coercion.rs +++ b/kani-compiler/src/kani_middle/coercion.rs @@ -90,14 +90,12 @@ pub fn extract_unsize_casting<'tcx>( .last() .unwrap(); // Extract the pointee type that is being coerced. - let src_pointee_ty = extract_pointee(tcx, coerce_info.src_ty).expect(&format!( - "Expected source to be a pointer. Found {:?} instead", - coerce_info.src_ty - )); - let dst_pointee_ty = extract_pointee(tcx, coerce_info.dst_ty).expect(&format!( - "Expected destination to be a pointer. Found {:?} instead", - coerce_info.dst_ty - )); + let src_pointee_ty = extract_pointee(tcx, coerce_info.src_ty).unwrap_or_else(|| { + panic!("Expected source to be a pointer. Found {:?} instead", coerce_info.src_ty) + }); + let dst_pointee_ty = extract_pointee(tcx, coerce_info.dst_ty).unwrap_or_else(|| { + panic!("Expected destination to be a pointer. Found {:?} instead", coerce_info.dst_ty) + }); // Find the tail of the coercion that determines the type of metadata to be stored. let (src_base_ty, dst_base_ty) = tcx.struct_lockstep_tails_for_codegen( src_pointee_ty, @@ -222,8 +220,8 @@ impl Iterator for CoerceUnsizedIterator<'_> { let coerce_index = coerce_index.as_usize(); assert!(coerce_index < src_fields.len()); - self.src_ty = Some(src_fields[coerce_index].ty_with_args(&src_args)); - self.dst_ty = Some(dst_fields[coerce_index].ty_with_args(&dst_args)); + self.src_ty = Some(src_fields[coerce_index].ty_with_args(src_args)); + self.dst_ty = Some(dst_fields[coerce_index].ty_with_args(dst_args)); Some(src_fields[coerce_index].name.clone()) } _ => { diff --git a/kani-compiler/src/kani_middle/points_to/points_to_analysis.rs b/kani-compiler/src/kani_middle/points_to/points_to_analysis.rs index 2008ef1eb0b8..9276ae03e744 100644 --- a/kani-compiler/src/kani_middle/points_to/points_to_analysis.rs +++ b/kani-compiler/src/kani_middle/points_to/points_to_analysis.rs @@ -361,7 +361,7 @@ fn try_resolve_instance<'tcx>( tcx, TypingEnv::fully_monomorphized(), *def, - &args, + args, DUMMY_SP, )) } diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 237fbf5f8b41..534af39c94e4 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -443,7 +443,7 @@ impl MirVisitor for MonoItemsFnCollector<'_, '_> { return; } }; - self.collect_allocation(&allocation); + self.collect_allocation(allocation); } /// Collect function calls. diff --git a/kani-compiler/src/kani_middle/resolve.rs b/kani-compiler/src/kani_middle/resolve.rs index ea51e80fbf81..6dada3ce6aa2 100644 --- a/kani-compiler/src/kani_middle/resolve.rs +++ b/kani-compiler/src/kani_middle/resolve.rs @@ -59,14 +59,14 @@ pub fn resolve_fn_path<'tcx>( match &path.qself { // Qualified path for a trait method implementation, like `::bar`. Some(QSelf { ty: syn_ty, position, .. }) if *position > 0 => { - let ty = type_resolution::resolve_ty(tcx, current_module, &syn_ty)?; + let ty = type_resolution::resolve_ty(tcx, current_module, syn_ty)?; let def_id = resolve_path(tcx, current_module, &path.path)?; validate_kind!(tcx, def_id, "function / method", DefKind::Fn | DefKind::AssocFn)?; Ok(FnResolution::FnImpl { def: stable_fn_def(tcx, def_id).unwrap(), ty }) } // Qualified path for a primitive type, such as `<[u8]::sort>`. Some(QSelf { ty: syn_ty, .. }) if type_resolution::is_type_primitive(syn_ty) => { - let ty = type_resolution::resolve_ty(tcx, current_module, &syn_ty)?; + let ty = type_resolution::resolve_ty(tcx, current_module, syn_ty)?; let resolved = resolve_in_primitive(tcx, ty, path.path.segments.iter())?; if resolved.segments.is_empty() { Ok(FnResolution::Fn(stable_fn_def(tcx, resolved.base).unwrap())) @@ -76,7 +76,7 @@ pub fn resolve_fn_path<'tcx>( } // Qualified path for a non-primitive type, such as `::foo>`. Some(QSelf { ty: syn_ty, .. }) => { - let ty = type_resolution::resolve_ty(tcx, current_module, &syn_ty)?; + let ty = type_resolution::resolve_ty(tcx, current_module, syn_ty)?; let def_id = resolve_in_user_type(tcx, ty, path.path.segments.iter())?; validate_kind!(tcx, def_id, "function / method", DefKind::Fn | DefKind::AssocFn)?; Ok(FnResolution::Fn(stable_fn_def(tcx, def_id).unwrap())) diff --git a/kani-compiler/src/kani_middle/resolve/type_resolution.rs b/kani-compiler/src/kani_middle/resolve/type_resolution.rs index c8951bf1e263..ea6ac94ee317 100644 --- a/kani-compiler/src/kani_middle/resolve/type_resolution.rs +++ b/kani-compiler/src/kani_middle/resolve/type_resolution.rs @@ -76,7 +76,7 @@ pub fn resolve_ty<'tcx>( let elems = tuple .elems .iter() - .map(|elem| resolve_ty(tcx, current_module, &elem)) + .map(|elem| resolve_ty(tcx, current_module, elem)) .collect::, _>>()?; Ok(Ty::new_tuple(&elems)) } diff --git a/kani-compiler/src/kani_middle/transform/automatic.rs b/kani-compiler/src/kani_middle/transform/automatic.rs index 76d21043355a..2eaf82078287 100644 --- a/kani-compiler/src/kani_middle/transform/automatic.rs +++ b/kani-compiler/src/kani_middle/transform/automatic.rs @@ -82,7 +82,7 @@ impl TransformPass for AutomaticHarnessPass { // and then resolve `fn_to_verify`. let kind = instance.args().0[0].expect_ty().kind(); let (def, args) = kind.fn_def().unwrap(); - let fn_to_verify = Instance::resolve(def, &args).unwrap(); + let fn_to_verify = Instance::resolve(def, args).unwrap(); let fn_to_verify_body = fn_to_verify.body().unwrap(); let mut harness_body = MutableBody::from(body); diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/instrumentation_visitor.rs b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/instrumentation_visitor.rs index ea50654ddcec..1b6ce5228a10 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/instrumentation_visitor.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/instrumentation_visitor.rs @@ -123,7 +123,7 @@ impl MirVisitor for InstrumentationVisitor<'_, '_> { let needs_get = { self.points_to .resolve_place_stable(place.clone(), self.current_instance, self.tcx) - .intersection(&self.analysis_targets) + .intersection(self.analysis_targets) .next() .is_some() }; diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs b/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs index e7178e2a7eb0..9736d3d43005 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs @@ -214,7 +214,7 @@ impl<'a> UninitInstrumenter<'a> { let ptr_operand = operation.mk_operand(body, &mut statements, source); let terminator = match pointee_info.layout() { PointeeLayout::Sized { layout } => { - let layout_operand = mk_layout_operand(body, &mut statements, source, &layout); + let layout_operand = mk_layout_operand(body, &mut statements, source, layout); // Depending on whether accessing the known number of elements in the slice, need to // pass is as an argument. let (diagnostic, args) = match &operation { @@ -232,7 +232,7 @@ impl<'a> UninitInstrumenter<'a> { _ => unreachable!(), }; let is_ptr_initialized_instance = resolve_mem_init_fn( - get_mem_init_fn_def(diagnostic, &mut self.mem_init_fn_cache), + get_mem_init_fn_def(diagnostic, self.mem_init_fn_cache), layout.len(), *pointee_info.ty(), ); @@ -263,12 +263,12 @@ impl<'a> UninitInstrumenter<'a> { _ => unreachable!(), }; let is_ptr_initialized_instance = resolve_mem_init_fn( - get_mem_init_fn_def(diagnostic, &mut self.mem_init_fn_cache), + get_mem_init_fn_def(diagnostic, self.mem_init_fn_cache), element_layout.len(), slicee_ty, ); let layout_operand = - mk_layout_operand(body, &mut statements, source, &element_layout); + mk_layout_operand(body, &mut statements, source, element_layout); Terminator { kind: TerminatorKind::Call { func: Operand::Copy(Place::from(body.new_local( @@ -342,7 +342,7 @@ impl<'a> UninitInstrumenter<'a> { let value = operation.expect_value(); let terminator = match pointee_info.layout() { PointeeLayout::Sized { layout } => { - let layout_operand = mk_layout_operand(body, &mut statements, source, &layout); + let layout_operand = mk_layout_operand(body, &mut statements, source, layout); // Depending on whether writing to the known number of elements in the slice, need to // pass is as an argument. let (diagnostic, args) = match &operation { @@ -376,7 +376,7 @@ impl<'a> UninitInstrumenter<'a> { _ => unreachable!(), }; let set_ptr_initialized_instance = resolve_mem_init_fn( - get_mem_init_fn_def(diagnostic, &mut self.mem_init_fn_cache), + get_mem_init_fn_def(diagnostic, self.mem_init_fn_cache), layout.len(), *pointee_info.ty(), ); @@ -407,12 +407,12 @@ impl<'a> UninitInstrumenter<'a> { _ => unreachable!(), }; let set_ptr_initialized_instance = resolve_mem_init_fn( - get_mem_init_fn_def(diagnostic, &mut self.mem_init_fn_cache), + get_mem_init_fn_def(diagnostic, self.mem_init_fn_cache), element_layout.len(), slicee_ty, ); let layout_operand = - mk_layout_operand(body, &mut statements, source, &element_layout); + mk_layout_operand(body, &mut statements, source, element_layout); Terminator { kind: TerminatorKind::Call { func: Operand::Copy(Place::from(body.new_local( @@ -471,7 +471,7 @@ impl<'a> UninitInstrumenter<'a> { }), ]; let set_ptr_initialized_instance = resolve_mem_init_fn( - get_mem_init_fn_def(diagnostic, &mut self.mem_init_fn_cache), + get_mem_init_fn_def(diagnostic, self.mem_init_fn_cache), layout.len(), *pointee_info.ty(), ); @@ -509,7 +509,7 @@ impl<'a> UninitInstrumenter<'a> { }; let layout_size = pointee_info.layout().maybe_size().unwrap(); let copy_init_state_instance = resolve_mem_init_fn( - get_mem_init_fn_def(KANI_COPY_INIT_STATE, &mut self.mem_init_fn_cache), + get_mem_init_fn_def(KANI_COPY_INIT_STATE, self.mem_init_fn_cache), layout_size, *pointee_info.ty(), ); @@ -547,7 +547,7 @@ impl<'a> UninitInstrumenter<'a> { _ => unreachable!(), }; let argument_operation_instance = resolve_mem_init_fn( - get_mem_init_fn_def(diagnostic, &mut self.mem_init_fn_cache), + get_mem_init_fn_def(diagnostic, self.mem_init_fn_cache), layout_size, *pointee_info.ty(), ); @@ -595,7 +595,7 @@ impl<'a> UninitInstrumenter<'a> { let mut statements = vec![]; let layout_size = pointee_info.layout().maybe_size().unwrap(); let copy_init_state_instance = resolve_mem_init_fn( - get_mem_init_fn_def(KANI_COPY_INIT_STATE_SINGLE, &mut self.mem_init_fn_cache), + get_mem_init_fn_def(KANI_COPY_INIT_STATE_SINGLE, self.mem_init_fn_cache), layout_size, *pointee_info.ty(), ); diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs b/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs index 9ae4ffd79ad6..c286b36c5bd5 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs @@ -138,7 +138,7 @@ impl MirVisitor for CheckUninitVisitor { // if it points to initialized memory. if *projection_elem == ProjectionElem::Deref { if let TyKind::RigidTy(RigidTy::RawPtr(..)) = - place_to_add_projections.ty(&&self.locals).unwrap().kind() + place_to_add_projections.ty(&self.locals).unwrap().kind() { self.push_target(MemoryInitOp::Check { operand: Operand::Copy(place_to_add_projections.clone()), @@ -147,7 +147,7 @@ impl MirVisitor for CheckUninitVisitor { } place_to_add_projections.projection.push(projection_elem.clone()); } - if place_without_deref.ty(&&self.locals).unwrap().kind().is_raw_ptr() { + if place_without_deref.ty(&self.locals).unwrap().kind().is_raw_ptr() { self.push_target(MemoryInitOp::Set { operand: Operand::Copy(place_without_deref), value: true, @@ -407,13 +407,13 @@ impl MirVisitor for CheckUninitVisitor { } TerminatorKind::Drop { place, .. } => { self.super_terminator(term, location); - let place_ty = place.ty(&&self.locals).unwrap(); + let place_ty = place.ty(&self.locals).unwrap(); // When drop is codegen'ed for types that could define their own dropping // behavior, a reference is taken to the place which is later implicitly coerced // to a pointer. Hence, we need to bless this pointer as initialized. match place - .ty(&&self.locals) + .ty(&self.locals) .unwrap() .kind() .rigid() @@ -529,7 +529,7 @@ impl MirVisitor for CheckUninitVisitor { } CastKind::Transmute => { let operand_ty = operand.ty(&self.locals).unwrap(); - if !tys_layout_compatible_to_size(&operand_ty, &ty) { + if !tys_layout_compatible_to_size(&operand_ty, ty) { // If transmuting between two types of incompatible layouts, padding // bytes are exposed, which is UB. self.push_target(MemoryInitOp::TriviallyUnsafe { diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/ty_layout.rs b/kani-compiler/src/kani_middle/transform/check_uninit/ty_layout.rs index 73dce8c2e898..20a98d1b8e91 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/ty_layout.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/ty_layout.rs @@ -210,7 +210,7 @@ fn data_bytes_for_ty( Ok(result) } FieldsShape::Arbitrary { ref offsets } => { - match ty.kind().rigid().expect(&format!("unexpected type: {ty:?}")) { + match ty.kind().rigid().unwrap_or_else(|| panic!("unexpected type: {ty:?}")) { RigidTy::Adt(def, args) => { match def.kind() { AdtKind::Enum => { @@ -224,7 +224,7 @@ fn data_bytes_for_ty( let mut fields_data_bytes = vec![]; for idx in layout.fields.fields_by_offset_order() { let field_offset = offsets[idx].bytes(); - let field_ty = fields[idx].ty_with_args(&args); + let field_ty = fields[idx].ty_with_args(args); fields_data_bytes.append(&mut data_bytes_for_ty( machine_info, field_ty, @@ -241,7 +241,7 @@ fn data_bytes_for_ty( // Retrieve data bytes for the tag. let tag_size = match tag { Scalar::Initialized { value, .. } => { - value.size(&machine_info) + value.size(machine_info) } Scalar::Union { .. } => { unreachable!("Enum tag should not be a union.") @@ -266,7 +266,7 @@ fn data_bytes_for_ty( }; for field_idx in variant.fields.fields_by_offset_order() { let field_offset = field_offsets[field_idx].bytes(); - let field_ty = fields[field_idx].ty_with_args(&args); + let field_ty = fields[field_idx].ty_with_args(args); field_data_bytes_for_variant.append( &mut data_bytes_for_ty( machine_info, @@ -321,7 +321,7 @@ fn data_bytes_for_ty( let fields = def.variants_iter().next().unwrap().fields(); for idx in layout.fields.fields_by_offset_order() { let field_offset = offsets[idx].bytes(); - let field_ty = fields[idx].ty_with_args(&args); + let field_ty = fields[idx].ty_with_args(args); struct_data_bytes.append(&mut data_bytes_for_ty( machine_info, field_ty, diff --git a/kani-compiler/src/kani_middle/transform/check_values.rs b/kani-compiler/src/kani_middle/transform/check_values.rs index b536de1e2695..26c337446ad6 100644 --- a/kani-compiler/src/kani_middle/transform/check_values.rs +++ b/kani-compiler/src/kani_middle/transform/check_values.rs @@ -953,7 +953,7 @@ pub fn ty_validity_per_offset( Ok(result) } FieldsShape::Arbitrary { ref offsets } => { - match ty.kind().rigid().expect(&format!("unexpected type: {ty:?}")) { + match ty.kind().rigid().unwrap_or_else(|| panic!("unexpected type: {ty:?}")) { RigidTy::Adt(def, args) => { match def.kind() { AdtKind::Enum => { @@ -967,7 +967,7 @@ pub fn ty_validity_per_offset( let mut fields_validity = vec![]; for idx in layout.fields.fields_by_offset_order() { let field_offset = offsets[idx].bytes(); - let field_ty = fields[idx].ty_with_args(&args); + let field_ty = fields[idx].ty_with_args(args); fields_validity.append(&mut ty_validity_per_offset( machine_info, field_ty, @@ -989,7 +989,7 @@ pub fn ty_validity_per_offset( let fields = ty_variants[index].fields(); for field_idx in variant.fields.fields_by_offset_order() { let field_offset = offsets[field_idx].bytes(); - let field_ty = fields[field_idx].ty_with_args(&args); + let field_ty = fields[field_idx].ty_with_args(args); fields_validity.append(&mut ty_validity_per_offset( machine_info, field_ty, @@ -1015,7 +1015,7 @@ pub fn ty_validity_per_offset( let fields = def.variants_iter().next().unwrap().fields(); for idx in layout.fields.fields_by_offset_order() { let field_offset = offsets[idx].bytes(); - let field_ty = fields[idx].ty_with_args(&args); + let field_ty = fields[idx].ty_with_args(args); struct_validity.append(&mut ty_validity_per_offset( machine_info, field_ty, diff --git a/kani-compiler/src/kani_middle/transform/contracts.rs b/kani-compiler/src/kani_middle/transform/contracts.rs index f7ca09b93a41..e3ca9a53c66b 100644 --- a/kani-compiler/src/kani_middle/transform/contracts.rs +++ b/kani-compiler/src/kani_middle/transform/contracts.rs @@ -491,10 +491,10 @@ impl FunctionWithContractPass { fn mark_unused(&mut self, tcx: TyCtxt, fn_def: FnDef, body: &Body, mode: ContractMode) { let contract = KaniAttributes::for_def_id(tcx, fn_def.def_id()).contract_attributes().unwrap(); - let recursion_closure = find_closure(tcx, fn_def, &body, contract.recursion_check.as_str()); - let check_closure = find_closure(tcx, fn_def, &body, contract.checked_with.as_str()); - let replace_closure = find_closure(tcx, fn_def, &body, contract.replaced_with.as_str()); - let assert_closure = find_closure(tcx, fn_def, &body, contract.asserted_with.as_str()); + let recursion_closure = find_closure(tcx, fn_def, body, contract.recursion_check.as_str()); + let check_closure = find_closure(tcx, fn_def, body, contract.checked_with.as_str()); + let replace_closure = find_closure(tcx, fn_def, body, contract.replaced_with.as_str()); + let assert_closure = find_closure(tcx, fn_def, body, contract.asserted_with.as_str()); match mode { ContractMode::Original => { // No contract instrumentation needed. Add all closures to the list of unused. diff --git a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs index 343fc2fe0c92..1e6cb58b7642 100644 --- a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs +++ b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs @@ -240,7 +240,7 @@ impl IntrinsicGeneratorPass { *pointee_info.ty(), ); let layout_operand = - mk_layout_operand(&mut new_body, &mut statements, &mut source, &layout); + mk_layout_operand(&mut new_body, &mut statements, &mut source, layout); let terminator = Terminator { kind: TerminatorKind::Call { @@ -283,7 +283,7 @@ impl IntrinsicGeneratorPass { &mut new_body, &mut statements, &mut source, - &element_layout, + element_layout, ); let terminator = Terminator { kind: TerminatorKind::Call { @@ -314,7 +314,7 @@ impl IntrinsicGeneratorPass { &mut source, InsertPosition::Before, None, - &reason, + reason, ); } PointeeLayout::Union { .. } => { @@ -326,7 +326,7 @@ impl IntrinsicGeneratorPass { &mut source, InsertPosition::Before, None, - &reason, + reason, ); } }; @@ -565,7 +565,7 @@ impl IntrinsicGeneratorPass { fn return_model( &mut self, new_body: &mut MutableBody, - mut source: &mut SourceInstruction, + source: &mut SourceInstruction, model: KaniModel, args: &GenericArgs, operands: Vec, @@ -574,7 +574,7 @@ impl IntrinsicGeneratorPass { let size_of_dyn = Instance::resolve(*def, args).unwrap(); new_body.insert_call( &size_of_dyn, - &mut source, + source, InsertPosition::Before, operands, Place::from(RETURN_LOCAL), diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index fbe9bebf42f8..164730499efb 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -78,10 +78,10 @@ impl BodyTransformation { transformer.add_pass(queries, AutomaticHarnessPass::new(unit, queries)); transformer.add_pass(queries, FnStubPass::new(&unit.stubs)); transformer.add_pass(queries, ExternFnStubPass::new(&unit.stubs)); - transformer.add_pass(queries, FunctionWithContractPass::new(tcx, queries, &unit)); + transformer.add_pass(queries, FunctionWithContractPass::new(tcx, queries, unit)); // This has to come after the contract pass since we want this to only replace the closure // body that is relevant for this harness. - transformer.add_pass(queries, AnyModifiesPass::new(tcx, queries, &unit)); + transformer.add_pass(queries, AnyModifiesPass::new(tcx, queries, unit)); transformer.add_pass( queries, ValidValuePass { @@ -103,10 +103,9 @@ impl BodyTransformation { mem_init_fn_cache: queries.kani_functions().clone(), }, ); - transformer - .add_pass(queries, IntrinsicGeneratorPass::new(unsupported_check_type, &queries)); - transformer.add_pass(queries, LoopContractPass::new(tcx, queries, &unit)); - transformer.add_pass(queries, RustcIntrinsicsPass::new(&queries)); + transformer.add_pass(queries, IntrinsicGeneratorPass::new(unsupported_check_type, queries)); + transformer.add_pass(queries, LoopContractPass::new(tcx, queries, unit)); + transformer.add_pass(queries, RustcIntrinsicsPass::new(queries)); transformer } @@ -140,7 +139,7 @@ impl BodyTransformation { } fn add_pass(&mut self, query_db: &QueryDb, pass: P) { - if pass.is_enabled(&query_db) { + if pass.is_enabled(query_db) { match P::transformation_type() { TransformationType::Instrumentation => self.inst_passes.push(Box::new(pass)), TransformationType::Stubbing => self.stub_passes.push(Box::new(pass)), @@ -220,7 +219,7 @@ impl GlobalPasses { } fn add_global_pass(&mut self, query_db: &QueryDb, pass: P) { - if pass.is_enabled(&query_db) { + if pass.is_enabled(query_db) { self.global_passes.push(Box::new(pass)) } } diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index 421df82f3e5f..c2e4acc96310 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -957,12 +957,12 @@ mod tests { } fn check(args: &str, feature: Option, pred: fn(StandaloneArgs) -> bool) { - let mut res = parse_unstable_disabled(&args); + let mut res = parse_unstable_disabled(args); if let Some(unstable) = feature { // Should fail without -Z unstable-options. assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); // Should succeed with -Z unstable-options. - res = parse_unstable_enabled(&args, unstable); + res = parse_unstable_enabled(args, unstable); } assert!(res.is_ok()); assert!(pred(res.unwrap())); diff --git a/kani-driver/src/call_goto_instrument.rs b/kani-driver/src/call_goto_instrument.rs index 09b8cbb18fc8..905b22c116c6 100644 --- a/kani-driver/src/call_goto_instrument.rs +++ b/kani-driver/src/call_goto_instrument.rs @@ -26,9 +26,9 @@ impl KaniSession { // We actually start by calling goto-cc to start the specialization: self.specialize_to_proof_harness(input, output, &harness.mangled_name)?; - let restrictions = project.get_harness_artifact(&harness, ArtifactType::VTableRestriction); + let restrictions = project.get_harness_artifact(harness, ArtifactType::VTableRestriction); if let Some(restrictions_path) = restrictions { - self.apply_vtable_restrictions(&output, restrictions_path)?; + self.apply_vtable_restrictions(output, restrictions_path)?; } // Run sanity checks in the model generated by kani-compiler before any goto-instrument @@ -66,7 +66,7 @@ impl KaniSession { let c_demangled = alter_extension(output, "demangled.c"); let prett_name_map = - project.get_harness_artifact(&harness, ArtifactType::PrettyNameMap).unwrap(); + project.get_harness_artifact(harness, ArtifactType::PrettyNameMap).unwrap(); self.demangle_c(prett_name_map, &c_outfile, &c_demangled)?; if !self.args.common_args.quiet { println!("Demangled GotoC code written to {}", c_demangled.to_string_lossy()) diff --git a/kani-driver/src/cbmc_output_parser.rs b/kani-driver/src/cbmc_output_parser.rs index 79a2c20732b4..749bb9abec1e 100644 --- a/kani-driver/src/cbmc_output_parser.rs +++ b/kani-driver/src/cbmc_output_parser.rs @@ -773,8 +773,8 @@ mod tests { } ] }"#; - let parser_item: Result = serde_json::from_str(&data); - let result_struct: Result = serde_json::from_str(&data); + let parser_item: Result = serde_json::from_str(data); + let result_struct: Result = serde_json::from_str(data); assert!(parser_item.is_ok()); assert!(result_struct.is_ok()); } diff --git a/kani-driver/src/cbmc_property_renderer.rs b/kani-driver/src/cbmc_property_renderer.rs index 95a2175a2eac..3245e9d0ccb2 100644 --- a/kani-driver/src/cbmc_property_renderer.rs +++ b/kani-driver/src/cbmc_property_renderer.rs @@ -557,8 +557,8 @@ fn has_check_failure(properties: &Vec, description: &str) -> bool { // Determines if there were unwinding assertion failures in a set of properties fn has_unwinding_assertion_failures(properties: &Vec) -> bool { - has_check_failure(&properties, UNWINDING_ASSERT_DESC) - || has_check_failure(&properties, UNWINDING_ASSERT_REC_DESC) + has_check_failure(properties, UNWINDING_ASSERT_DESC) + || has_check_failure(properties, UNWINDING_ASSERT_REC_DESC) } /// Replaces the description of all properties from functions with a missing diff --git a/kani-driver/src/concrete_playback/test_generator.rs b/kani-driver/src/concrete_playback/test_generator.rs index d397c5f00dff..2cf741c23f82 100644 --- a/kani-driver/src/concrete_playback/test_generator.rs +++ b/kani-driver/src/concrete_playback/test_generator.rs @@ -46,7 +46,7 @@ impl KaniSession { .iter() .map(|(prop, concrete_items)| { let pretty_name = harness.get_harness_name_unqualified(); - format_unit_test(&pretty_name, &concrete_items, gen_test_doc(harness, prop)) + format_unit_test(pretty_name, concrete_items, gen_test_doc(harness, prop)) }) .collect(); unit_tests.dedup_by(|a, b| a.name == b.name); @@ -86,10 +86,12 @@ impl KaniSession { harness.original_end_line, unit_tests, ) - .expect(&format!( - "Failed to modify source code for the file `{}`", - &harness.original_file - )); + .unwrap_or_else(|_| { + panic!( + "Failed to modify source code for the file `{}`", + &harness.original_file + ) + }); } } verification_result.generated_concrete_test = true; @@ -413,7 +415,7 @@ mod concrete_vals_extractor { let trace = property .trace .as_ref() - .expect(&format!("Missing trace for {}", property.property_name())); + .unwrap_or_else(|| panic!("Missing trace for {}", property.property_name())); let concrete_items: Vec = trace.iter().filter_map(&extract_from_trace_item).collect(); @@ -452,8 +454,9 @@ mod concrete_vals_extractor { str_chunk_len, 8, "Tried to read a chunk of 8 bits of actually read {str_chunk_len} bits" ); - let next_byte = u8::from_str_radix(str_chunk, 2) - .expect(&format!("Couldn't convert the string chunk `{str_chunk}` to u8")); + let next_byte = u8::from_str_radix(str_chunk, 2).unwrap_or_else(|_| { + panic!("Couldn't convert the string chunk `{str_chunk}` to u8") + }); next_num.push(next_byte); } diff --git a/kani-driver/src/harness_runner.rs b/kani-driver/src/harness_runner.rs index ab7f4d8bb699..a35eee8513d4 100644 --- a/kani-driver/src/harness_runner.rs +++ b/kani-driver/src/harness_runner.rs @@ -72,12 +72,12 @@ impl<'pr> HarnessRunner<'_, 'pr> { .enumerate() .map(|(idx, harness)| -> Result> { let goto_file = - self.project.get_harness_artifact(&harness, ArtifactType::Goto).unwrap(); + self.project.get_harness_artifact(harness, ArtifactType::Goto).unwrap(); - self.sess.instrument_model(goto_file, goto_file, &self.project, &harness)?; + self.sess.instrument_model(goto_file, goto_file, self.project, harness)?; if self.sess.args.synthesize_loop_contracts { - self.sess.synthesize_loop_contracts(goto_file, &goto_file, &harness)?; + self.sess.synthesize_loop_contracts(goto_file, goto_file, harness)?; } let result = self.sess.check_harness(goto_file, harness)?; diff --git a/kani-driver/src/project.rs b/kani-driver/src/project.rs index 72c407cb7048..80a4b050adff 100644 --- a/kani-driver/src/project.rs +++ b/kani-driver/src/project.rs @@ -232,7 +232,7 @@ impl<'a> StandaloneProjectBuilder<'a> { } else { input.canonicalize().unwrap().parent().unwrap().to_path_buf() }; - let crate_name = if let Some(name) = krate_name { name } else { crate_name(&input) }; + let crate_name = if let Some(name) = krate_name { name } else { crate_name(input) }; let metadata = standalone_artifact(&outdir, &crate_name, Metadata); Ok(StandaloneProjectBuilder { outdir, diff --git a/kani-driver/src/session.rs b/kani-driver/src/session.rs index df9e7310f142..2448c593b47d 100644 --- a/kani-driver/src/session.rs +++ b/kani-driver/src/session.rs @@ -210,7 +210,7 @@ async fn run_terminal_timeout( cmd.stderr(std::process::Stdio::null()); } if verbosity.verbose() { - println!("[Kani] Running: `{}`", render_command(&cmd.as_std()).to_string_lossy()); + println!("[Kani] Running: `{}`", render_command(cmd.as_std()).to_string_lossy()); } let program = cmd.as_std().get_program().to_string_lossy().to_string(); let result = with_timer( diff --git a/library/kani_macros/src/derive.rs b/library/kani_macros/src/derive.rs index f8eaf8354280..539f8b671798 100644 --- a/library/kani_macros/src/derive.rs +++ b/library/kani_macros/src/derive.rs @@ -50,9 +50,9 @@ pub fn expand_derive_arbitrary(item: proc_macro::TokenStream) -> proc_macro::Tok let item_name = &derive_item.ident; let kani_path = kani_path!(); - let body = fn_any_body(&item_name, &derive_item.data); + let body = fn_any_body(item_name, &derive_item.data); // Get the safety constraints (if any) to produce type-safe values - let safety_conds_opt = safety_conds_opt(&item_name, &derive_item, trait_name); + let safety_conds_opt = safety_conds_opt(item_name, &derive_item, trait_name); // Add a bound `T: Arbitrary` to every type parameter T. let generics = add_trait_bound_arbitrary(derive_item.generics); @@ -60,7 +60,7 @@ pub fn expand_derive_arbitrary(item: proc_macro::TokenStream) -> proc_macro::Tok let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let expanded = if let Some(safety_conds) = safety_conds_opt { - let field_refs = field_refs(&item_name, &derive_item.data); + let field_refs = field_refs(item_name, &derive_item.data); quote! { // The generated implementation. impl #impl_generics #kani_path::Arbitrary for #item_name #ty_generics #where_clause { @@ -399,8 +399,8 @@ fn safe_body_with_calls( derive_input: &DeriveInput, trait_name: &str, ) -> TokenStream { - let safety_conds_opt = safety_conds_opt(&item_name, &derive_input, trait_name); - let safe_body_default = safe_body_default(&item_name, &derive_input.data); + let safety_conds_opt = safety_conds_opt(item_name, derive_input, trait_name); + let safe_body_default = safe_body_default(item_name, &derive_input.data); if let Some(safety_conds) = safety_conds_opt { quote! { #safe_body_default && #safety_conds } @@ -415,8 +415,8 @@ pub fn expand_derive_invariant(item: proc_macro::TokenStream) -> proc_macro::Tok let item_name = &derive_item.ident; let kani_path = kani_path!(); - let safe_body = safe_body_with_calls(&item_name, &derive_item, trait_name); - let field_refs = field_refs(&item_name, &derive_item.data); + let safe_body = safe_body_with_calls(item_name, &derive_item, trait_name); + let field_refs = field_refs(item_name, &derive_item.data); // Add a bound `T: Invariant` to every type parameter T. let generics = add_trait_bound_invariant(derive_item.generics); @@ -446,9 +446,9 @@ fn safety_conds_opt( trait_name: &str, ) -> Option { let has_item_safety_constraint = - has_item_safety_constraint(&item_name, &derive_input, trait_name); + has_item_safety_constraint(item_name, derive_input, trait_name); - let has_field_safety_constraints = has_field_safety_constraints(&item_name, &derive_input.data); + let has_field_safety_constraints = has_field_safety_constraints(item_name, &derive_input.data); if has_item_safety_constraint && has_field_safety_constraints { abort!(Span::call_site(), "Cannot derive `{}` for `{}`", trait_name, item_name; @@ -458,9 +458,9 @@ fn safety_conds_opt( } if has_item_safety_constraint { - Some(safe_body_from_struct_attr(&item_name, &derive_input, trait_name)) + Some(safe_body_from_struct_attr(item_name, derive_input, trait_name)) } else if has_field_safety_constraints { - Some(safe_body_from_fields_attr(&item_name, &derive_input.data, trait_name)) + Some(safe_body_from_fields_attr(item_name, &derive_input.data, trait_name)) } else { None } diff --git a/library/kani_macros/src/sysroot/contracts/assert.rs b/library/kani_macros/src/sysroot/contracts/assert.rs index 777fa6722587..d954c10b6425 100644 --- a/library/kani_macros/src/sysroot/contracts/assert.rs +++ b/library/kani_macros/src/sysroot/contracts/assert.rs @@ -44,7 +44,7 @@ impl<'a> ContractConditionsHandler<'a> { fn initial_assert_stmts(&self) -> Vec { let body_wrapper_ident = Ident::new("body_wrapper", Span::call_site()); let output = &self.annotated_fn.sig.output; - let return_type = return_type_to_type(&output); + let return_type = return_type_to_type(output); let stmts = &self.annotated_fn.block.stmts; let result = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site()); diff --git a/library/kani_macros/src/sysroot/contracts/check.rs b/library/kani_macros/src/sysroot/contracts/check.rs index 6a473a2060d2..a1a174360895 100644 --- a/library/kani_macros/src/sysroot/contracts/check.rs +++ b/library/kani_macros/src/sysroot/contracts/check.rs @@ -123,9 +123,10 @@ impl<'a> ContractConditionsHandler<'a> { /// First find the modifies body and expand that. Then expand the rest of the body. pub fn expand_check(&self, closure: &mut Stmt) { let body = closure_body(closure); - self.expand_modifies(find_contract_closure(&mut body.block.stmts, "wrapper").expect( - &format!("Internal Failure: Expected to find `wrapper` closure, but found none"), - )); + self.expand_modifies( + find_contract_closure(&mut body.block.stmts, "wrapper") + .expect("Internal Failure: Expected to find `wrapper` closure, but found none"), + ); *body = syn::parse2(self.make_check_body(mem::take(&mut body.block.stmts))).unwrap(); } diff --git a/library/kani_macros/src/sysroot/contracts/helpers.rs b/library/kani_macros/src/sysroot/contracts/helpers.rs index becb07d43033..c79a5520ceb3 100644 --- a/library/kani_macros/src/sysroot/contracts/helpers.rs +++ b/library/kani_macros/src/sysroot/contracts/helpers.rs @@ -94,8 +94,9 @@ pub fn find_contract_closure<'a>( /// /// Panic if no closure was found. pub fn expect_closure<'a>(stmts: &'a mut [Stmt], name: &'static str) -> &'a mut Stmt { - find_contract_closure(stmts, name) - .expect(&format!("Internal Failure: Expected to find `{name}` closure, but found none")) + find_contract_closure(stmts, name).unwrap_or_else(|| { + panic!("Internal Failure: Expected to find `{name}` closure, but found none") + }) } /// Find a closure inside a match block. @@ -112,7 +113,9 @@ pub fn expect_closure_in_match<'a>(stmts: &'a mut [Stmt], name: &'static str) -> None } }); - closure.expect(&format!("Internal Failure: Expected to find `{name}` closure, but found none")) + closure.unwrap_or_else(|| { + panic!("Internal Failure: Expected to find `{name}` closure, but found none") + }) } /// Extract the body of a closure declaration. diff --git a/library/kani_macros/src/sysroot/contracts/mod.rs b/library/kani_macros/src/sysroot/contracts/mod.rs index 5c70cc0cc581..cc792614e2d5 100644 --- a/library/kani_macros/src/sysroot/contracts/mod.rs +++ b/library/kani_macros/src/sysroot/contracts/mod.rs @@ -37,7 +37,7 @@ //! contracts need to be analyzed. It will do the following: //! 1. Annotate the function with extra `kanitool` attributes //! 2. Generate closures for each contract processing scenario (recursive check, simple check, -//! replacement, and regular execution). +//! replacement, and regular execution). //! //! Subsequent expansions will detect the existence of the extra `kanitool` attributes, //! and they will only expand the body of the closures generated in the initial phase. diff --git a/library/kani_macros/src/sysroot/contracts/replace.rs b/library/kani_macros/src/sysroot/contracts/replace.rs index bb2ba01406c0..5a450b3ca9d4 100644 --- a/library/kani_macros/src/sysroot/contracts/replace.rs +++ b/library/kani_macros/src/sysroot/contracts/replace.rs @@ -24,7 +24,7 @@ impl<'a> ContractConditionsHandler<'a> { // https://github.com/model-checking/kani/issues/3667 let redefs = self.arg_redefinitions(false); let redefs_block: Block = syn::parse_quote!({#redefs}); - vec![ + [ vec![syn::parse_quote!( let #result : #return_type = kani::any_modifies(); )], @@ -128,7 +128,7 @@ impl<'a> ContractConditionsHandler<'a> { let sig = &self.annotated_fn.sig; let output = &sig.output; let before = self.initial_replace_stmts(); - let body = self.expand_replace_body(&before, &vec![]); + let body = self.expand_replace_body(&before, &[]); quote!( #[kanitool::is_contract_generated(replace)] diff --git a/library/kani_macros/src/sysroot/contracts/shared.rs b/library/kani_macros/src/sysroot/contracts/shared.rs index 833303307af8..816a3623f6b2 100644 --- a/library/kani_macros/src/sysroot/contracts/shared.rs +++ b/library/kani_macros/src/sysroot/contracts/shared.rs @@ -119,8 +119,8 @@ pub fn try_as_result_assign(stmt: &syn::Stmt) -> Option<&syn::LocalInit> { pub fn build_ensures(data: &ExprClosure) -> (TokenStream2, Expr) { let mut remembers_exprs = HashMap::new(); let mut vis = OldVisitor { t: OldLifter::new(), remembers_exprs: &mut remembers_exprs }; - let mut expr = &mut data.clone(); - vis.visit_expr_closure_mut(&mut expr); + let expr = &mut data.clone(); + vis.visit_expr_closure_mut(expr); let remembers_stmts: TokenStream2 = remembers_exprs .iter() @@ -174,7 +174,7 @@ impl syn::visit_mut::VisitMut for OldVisitor<'_, T> { qself: None, path: Path { leading_colon: None, segments }, }) if segments.len() == 1 - && segments.first().map_or(false, |sgm| sgm.ident == "old") => + && segments.first().is_some_and(|sgm| sgm.ident == "old") => { let first_segment = segments.first().unwrap(); assert_spanned_err!(first_segment.arguments.is_empty(), first_segment); diff --git a/library/kani_macros/src/sysroot/loop_contracts/mod.rs b/library/kani_macros/src/sysroot/loop_contracts/mod.rs index 8b49c660ef1d..d1dbfd06aaa8 100644 --- a/library/kani_macros/src/sysroot/loop_contracts/mod.rs +++ b/library/kani_macros/src/sysroot/loop_contracts/mod.rs @@ -12,88 +12,82 @@ use syn::token::AndAnd; use syn::{BinOp, Block, Expr, ExprBinary, Ident, Stmt, parse_quote, visit_mut::VisitMut}; /* -Transform the loop to support on_entry(expr) : the value of expr before entering the loop -1. For each on_entry(expr) in the loop variant, replace it with a newly generated "memory" variable old_k -2. Add the declaration of i before the loop: let old_k = expr -For example: -#[kani::loop_invariant(on_entry(x+y) = x + y -1)] -while(....) - -is transformed into -let old_1 = x + y -#[kani::loop_invariant(old_1 = x + y -1)] -while(....) - -Then the loop_invartiant is transformed - -*/ - -/* -Transform the loop to support prev(expr) : the value of expr at the end of the previous iteration -Semantic: If the loop has at least 1 iteration: prev(expr) is the value of expr at the end of the previous iteration. Otherwise, just remove the loop (without check for the invariant too). - -Transformation: basically, if the loop has at least 1 iteration (loop_quard is satisfied at the beginning), we unfold the loop once, declare the variables for prev values and update them at the beginning of the loop body. -Otherwise, we remove the loop. -If there is a prev(expr) in the loop_invariant: -1. Firstly, add an if block whose condition is the loop_quard, inside its body add/do the followings: -2. For each prev(expr) in the loop variant, replace it with a newly generated "memory" variable prev_k -3. Add the declaration of prev_k before the loop: let mut prev_k = expr -4. Define a mut closure whose body is exactly the loop body, but replace all continue/break statements with return true/false statements, - then add a final return true statement at the end of it -5. Add an if statement with condition to be the that closure's call (the same as run the loop once): - True block: add the loop with expanded macros (see next section) and inside the loop body: - add the assignment statements (exactly the same as the declarations without the "let mut") on the top to update the "memory" variables - Else block: Add the assertion for the loop_invariant (not includes the loop_quard): check if the loop_invariant holds after the first iteration. - -For example: -#[kani::loop_invariant(prev(x+y) = x + y -1 && ...)] -while(loop_guard) -{ - loop_body -} - -is transformed into - -assert!(loop_guard); -let mut prev_1 = x + y; -let mut loop_body_closure = || { - loop_body_replaced //replace breaks/continues in loop_body with returns -}; -if loop_body_closure(){ - #[kani::loop_invariant(prev_1 = x + y -1)] + Transform the loop to support on_entry(expr) : the value of expr before entering the loop + 1. For each on_entry(expr) in the loop variant, replace it with a newly generated "memory" variable old_k + 2. Add the declaration of i before the loop: let old_k = expr + For example: + #[kani::loop_invariant(on_entry(x+y) = x + y -1)] + while(....) + + is transformed into + let old_1 = x + y + #[kani::loop_invariant(old_1 = x + y -1)] + while(....) + + Then the loop_invartiant is transformed. + + Transform the loop to support prev(expr) : the value of expr at the end of the previous iteration + Semantic: If the loop has at least 1 iteration: prev(expr) is the value of expr at the end of the previous iteration. Otherwise, just remove the loop (without check for the invariant too). + + Transformation: basically, if the loop has at least 1 iteration (loop_quard is satisfied at the beginning), we unfold the loop once, declare the variables for prev values and update them at the beginning of the loop body. + Otherwise, we remove the loop. + If there is a prev(expr) in the loop_invariant: + 1. Firstly, add an if block whose condition is the loop_quard, inside its body add/do the followings: + 2. For each prev(expr) in the loop variant, replace it with a newly generated "memory" variable prev_k + 3. Add the declaration of prev_k before the loop: let mut prev_k = expr + 4. Define a mut closure whose body is exactly the loop body, but replace all continue/break statements with return true/false statements, + then add a final return true statement at the end of it + 5. Add an if statement with condition to be the that closure's call (the same as run the loop once): + True block: add the loop with expanded macros (see next section) and inside the loop body: + add the assignment statements (exactly the same as the declarations without the "let mut") on the top to update the "memory" variables + Else block: Add the assertion for the loop_invariant (not includes the loop_quard): check if the loop_invariant holds after the first iteration. + + For example: + #[kani::loop_invariant(prev(x+y) = x + y -1 && ...)] while(loop_guard) { - prev_1 = x + y; loop_body } -} -else{ - assert!(prev_1 = x + y -1 && ...); -} + is transformed into -*/ + assert!(loop_guard); + let mut prev_1 = x + y; + let mut loop_body_closure = || { + loop_body_replaced //replace breaks/continues in loop_body with returns + }; + if loop_body_closure(){ + #[kani::loop_invariant(prev_1 = x + y -1)] + while(loop_guard) + { + prev_1 = x + y; + loop_body + } + } + else{ + assert!(prev_1 = x + y -1 && ...); + } -/// After that: -/// Expand loop contracts macros. -/// -/// A while loop of the form -/// ``` rust -/// while guard { -/// body -/// } -/// ``` -/// will be annotated as -/// ``` rust -/// #[inline(never)] -/// #[kanitool::fn_marker = "kani_register_loop_contract"] -/// const fn kani_register_loop_contract_id T>(f: F) -> T { -/// unreachable!() -/// } -/// while kani_register_loop_contract_id(|| -> bool {inv};) && guard { -/// body -/// } -/// ``` + Finally, expand the loop contract macro. + + A while loop of the form + ``` rust + while guard { + body + } + ``` + will be annotated as + ``` rust + #[inline(never)] + #[kanitool::fn_marker = "kani_register_loop_contract"] + const fn kani_register_loop_contract_id T>(f: F) -> T { + unreachable!() + } + while kani_register_loop_contract_id(|| -> bool {inv};) && guard { + body + } + ``` +*/ struct TransformationResult { transformed_expr: Expr, @@ -111,12 +105,7 @@ struct CallReplacer { // This impl replaces any function call of a function name : old_name with a newly generated variable. impl CallReplacer { fn new(old_name: &str, var_prefix: String) -> Self { - Self { - old_name: old_name.to_string(), - replacements: Vec::new(), - counter: 0, - var_prefix: var_prefix, - } + Self { old_name: old_name.to_string(), replacements: Vec::new(), counter: 0, var_prefix } } fn generate_var_name(&mut self) -> proc_macro2::Ident { @@ -168,12 +157,9 @@ fn transform_function_calls( let mut newreplace: Vec<(Expr, Ident)> = Vec::new(); for (call, var) in replacer.replacements { - match call { - Expr::Call(call_expr) => { - let insideexpr = call_expr.args[0].clone(); - newreplace.push((insideexpr, var.clone())); - } - _ => {} + if let Expr::Call(call_expr) = call { + let insideexpr = call_expr.args[0].clone(); + newreplace.push((insideexpr, var.clone())); } } @@ -230,11 +216,9 @@ fn transform_break_continue(block: &mut Block) { return (true, None); }; // Add semicolon to the last statement if it's an expression without semicolon - if let Some(last_stmt) = block.stmts.last_mut() { - if let Stmt::Expr(expr, ref mut semi) = last_stmt { - if semi.is_none() { - *semi = Some(Default::default()); - } + if let Some(Stmt::Expr(_, ref mut semi)) = block.stmts.last_mut() { + if semi.is_none() { + *semi = Some(Default::default()); } } block.stmts.push(return_stmt); diff --git a/scripts/kani-fmt.sh b/scripts/kani-fmt.sh index 6a136f244ff8..d4ddfaf19b58 100755 --- a/scripts/kani-fmt.sh +++ b/scripts/kani-fmt.sh @@ -12,11 +12,20 @@ set -o nounset ROOT_FOLDER=$(git rev-parse --show-toplevel) cd ${ROOT_FOLDER} +# Parse arguments to check for --check flag +check_flag="" +for arg in "$@"; do + if [ "$arg" = "--check" ]; then + check_flag="--check" + break + fi +done + # Verify crates. error=0 # Check all crates. Only fail at the end. -cargo fmt "$@" || error=1 +cargo fmt ${check_flag} || error=1 # Check test source files. TESTS=("tests" "docs/src/tutorial") @@ -37,7 +46,7 @@ for suite in "${TESTS[@]}"; do set +f; unset IFS # Note: We set the configuration file here because some submodules have # their own configuration file. - rustfmt --config-path rustfmt.toml "${files[@]}" || error=1 + rustfmt --config-path rustfmt.toml ${check_flag} "${files[@]}" || error=1 done exit $error diff --git a/tests/expected/loop-contract/loop_with_true_invariant.rs b/tests/expected/loop-contract/loop_with_true_invariant.rs index 7b03acb795b6..be03534e0a09 100644 --- a/tests/expected/loop-contract/loop_with_true_invariant.rs +++ b/tests/expected/loop-contract/loop_with_true_invariant.rs @@ -9,10 +9,10 @@ #![feature(proc_macro_hygiene)] #[kani::proof] -fn main(){ +fn main() { let mut i = 100; #[kani::loop_invariant(true)] while i > 1 { i /= 2; - } + } } diff --git a/tools/build-kani/src/main.rs b/tools/build-kani/src/main.rs index 3ff017e37509..1e37cba165a0 100644 --- a/tools/build-kani/src/main.rs +++ b/tools/build-kani/src/main.rs @@ -24,7 +24,7 @@ fn main() -> Result<()> { parser::Commands::BuildDev(build_parser) => { let bin_folder = &build_bin(&build_parser.args)?; if !build_parser.skip_libs { - build_lib(&bin_folder)?; + build_lib(bin_folder)?; } Ok(()) } @@ -105,8 +105,8 @@ fn bundle_kani(dir: &Path) -> Result<()> { // 4. Pre-compiled library files cp_dir(&kani_sysroot_lib(), dir)?; - cp_dir(&kani_playback_lib().parent().unwrap(), dir)?; - cp_dir(&kani_no_core_lib().parent().unwrap(), dir)?; + cp_dir(kani_playback_lib().parent().unwrap(), dir)?; + cp_dir(kani_no_core_lib().parent().unwrap(), dir)?; // 5. Record the exact toolchain and rustc version we use std::fs::write(dir.join("rust-toolchain-version"), env!("RUSTUP_TOOLCHAIN"))?; diff --git a/tools/build-kani/src/sysroot.rs b/tools/build-kani/src/sysroot.rs index 139dce794f13..bb8708a14f68 100644 --- a/tools/build-kani/src/sysroot.rs +++ b/tools/build-kani/src/sysroot.rs @@ -180,13 +180,13 @@ fn copy_artifacts(artifacts: &[Artifact], sysroot_lib: &Path, copy_std: bool) -> fs::create_dir_all(sysroot_lib)?; // Copy Kani libraries into sysroot top folder. - copy_libs(&artifacts, &sysroot_lib, &is_kani_lib); + copy_libs(artifacts, sysroot_lib, &is_kani_lib); // Copy standard libraries into rustlib//lib/ folder. if copy_std { let std_path = path_buf!(&sysroot_lib, "rustlib", build_target(), "lib"); - fs::create_dir_all(&std_path).expect(&format!("Failed to create {std_path:?}")); - copy_libs(&artifacts, &std_path, &is_std_lib); + fs::create_dir_all(&std_path).unwrap_or_else(|_| panic!("Failed to create {std_path:?}")); + copy_libs(artifacts, &std_path, &is_std_lib); } Ok(()) } diff --git a/tools/compiletest/src/header.rs b/tools/compiletest/src/header.rs index f49d4f64ce47..06f5637b35c4 100644 --- a/tools/compiletest/src/header.rs +++ b/tools/compiletest/src/header.rs @@ -24,6 +24,12 @@ pub struct TestProps { pub kani_panic_step: Option, } +impl Default for TestProps { + fn default() -> Self { + Self::new() + } +} + impl TestProps { pub fn new() -> Self { TestProps { diff --git a/tools/compiletest/src/main.rs b/tools/compiletest/src/main.rs index c0e599b725cf..8c0a2dd0b08d 100644 --- a/tools/compiletest/src/main.rs +++ b/tools/compiletest/src/main.rs @@ -125,9 +125,9 @@ pub fn parse_config(args: Vec) -> Config { match m.opt_str(nm) { Some(s) => PathBuf::from(&s), None => { - let mut root_folder = top_level().expect( - format!("Cannot find root directory. Please provide --{nm} option.").as_str(), - ); + let mut root_folder = top_level().unwrap_or_else(|| { + panic!("Cannot find root directory. Please provide --{nm} option.") + }); default.iter().for_each(|f| root_folder.push(f)); root_folder } @@ -147,9 +147,9 @@ pub fn parse_config(args: Vec) -> Config { let run_ignored = matches.opt_present("ignored"); let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode"); let timeout = matches.opt_str("timeout").map(|val| { - Duration::from_secs(u64::from_str(&val).expect(&format!( - "Unexpected timeout format. Expected a positive number but found {val}" - ))) + Duration::from_secs(u64::from_str(&val).unwrap_or_else(|_| { + panic!("Unexpected timeout format. Expected a positive number but found {val}") + })) }); Config { diff --git a/tools/compiletest/src/runtest.rs b/tools/compiletest/src/runtest.rs index d31e895eab6f..3467b092bb76 100644 --- a/tools/compiletest/src/runtest.rs +++ b/tools/compiletest/src/runtest.rs @@ -476,8 +476,9 @@ impl TestCx<'_> { (None, _) => { /* Test passed. Do nothing*/ } (Some(_), true) => { // Fix output but still fail the test so users know which ones were updated - fs::write(expected_path, output) - .expect(&format!("Failed to update file {}", expected_path.display())); + fs::write(expected_path, output).unwrap_or_else(|_| { + panic!("Failed to update file {}", expected_path.display()) + }); self.fatal_proc_rec( &format!("updated `{}` file, please review", expected_path.display()), proc_res, diff --git a/tools/kani-cov/src/args.rs b/tools/kani-cov/src/args.rs index c83b3516f388..11cfd5d7a73a 100644 --- a/tools/kani-cov/src/args.rs +++ b/tools/kani-cov/src/args.rs @@ -131,9 +131,9 @@ pub fn validate_args(args: &Args) -> Result<()> { } match args.command.as_ref().unwrap() { - Subcommand::Merge(merge_args) => merge::validate_merge_args(&merge_args)?, - Subcommand::Summary(summary_args) => summary::validate_summary_args(&summary_args)?, - Subcommand::Report(report_args) => report::validate_report_args(&report_args)?, + Subcommand::Merge(merge_args) => merge::validate_merge_args(merge_args)?, + Subcommand::Summary(summary_args) => summary::validate_summary_args(summary_args)?, + Subcommand::Report(report_args) => report::validate_report_args(report_args)?, }; Ok(()) diff --git a/tools/kani-cov/src/merge.rs b/tools/kani-cov/src/merge.rs index 2e8452393fb0..06a78d8f3845 100644 --- a/tools/kani-cov/src/merge.rs +++ b/tools/kani-cov/src/merge.rs @@ -45,11 +45,11 @@ fn parse_raw_results(paths: &Vec) -> Result> { let mut raw_results = Vec::with_capacity(paths.len()); for path in paths { let filename = path.to_string_lossy(); - let file = File::open(path).expect(&format!("could not open file {filename}")); + let file = File::open(path).unwrap_or_else(|_| panic!("could not open file {filename}")); let reader = BufReader::new(file); let result = serde_json::from_reader(reader) - .expect(&format!("could not deserialize file {filename}")); + .unwrap_or_else(|_| panic!("could not deserialize file {filename}")); raw_results.push(result); } Ok(raw_results) diff --git a/tools/scanner/src/analysis.rs b/tools/scanner/src/analysis.rs index 1c3907ae1918..7c8b8d7468da 100644 --- a/tools/scanner/src/analysis.rs +++ b/tools/scanner/src/analysis.rs @@ -222,7 +222,7 @@ impl OverallStats { .map(|def| { ( def.name(), - if recursions.recursive_fns.contains(&def) { "recursive" } else { "" }, + if recursions.recursive_fns.contains(def) { "recursive" } else { "" }, ) }) .collect::>(), From a61a52b02884696c60589090363be16c1b5fabab Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Fri, 18 Apr 2025 09:17:22 -0700 Subject: [PATCH 107/161] Update toolchain to 2025-04-14 (#4018) Keeping it as a draft till we figure out what's causing `tests/kani/FunctionContracts/modify_slice_elem.rs` to fail. Resolves #4009 Relevant upstream changes: - https://github.com/rust-lang/rust/commit/9eca59a94021089905c482011a2d88ee9b862a73: Removes the option wrapper from `DefPathData::TypeNs`. - (Pointed out by @tautschnig) The combination of https://github.com/rust-lang/rust/commit/78e9621390 and https://github.com/rust-lang/rust/commit/aadfd810f6: these cause us to now assign a non-deterministic value when using contracts causing a spurious failure in `tests/kani/FunctionContracts/modify_slice_elem.rs`. We've made this a fixme test till we figure out a fix. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs | 6 +++--- rust-toolchain.toml | 2 +- tests/expected/uninit/intrinsics/expected | 4 ---- .../{modify_slice_elem.rs => modify_slice_elem_fixme.rs} | 3 +++ tests/perf/smol_str/src/lib.rs | 8 +++++--- 5 files changed, 12 insertions(+), 11 deletions(-) rename tests/kani/FunctionContracts/{modify_slice_elem.rs => modify_slice_elem_fixme.rs} (84%) diff --git a/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs b/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs index 219bc48c4d91..546a3aa4f127 100644 --- a/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs +++ b/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs @@ -813,7 +813,7 @@ impl<'a, 'tcx> Context<'a, 'tcx> { let disambiguator = CharonDisambiguator::new(data.disambiguator as usize); use rustc_hir::definitions::DefPathData; match &data.data { - DefPathData::TypeNs(Some(symbol)) => { + DefPathData::TypeNs(symbol) => { error_assert!(self, span, data.disambiguator == 0); // Sanity check name.push(CharonPathElem::Ident(symbol.to_string(), disambiguator)); } @@ -956,7 +956,7 @@ impl<'a, 'tcx> Context<'a, 'tcx> { let disambiguator = CharonDisambiguator::new(data.disambiguator as usize); use rustc_hir::definitions::DefPathData; match &data.data { - DefPathData::TypeNs(Some(symbol)) => { + DefPathData::TypeNs(symbol) => { error_assert!(self, span, data.disambiguator == 0); // Sanity check name.push(CharonPathElem::Ident(symbol.to_string(), disambiguator)); } @@ -1063,7 +1063,7 @@ impl<'a, 'tcx> Context<'a, 'tcx> { let disambiguator = CharonDisambiguator::new(data.disambiguator as usize); use rustc_hir::definitions::DefPathData; match &data.data { - DefPathData::TypeNs(Some(symbol)) => { + DefPathData::TypeNs(symbol) => { error_assert!(self, span, data.disambiguator == 0); // Sanity check name.push(CharonPathElem::Ident(symbol.to_string(), disambiguator)); } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c1527c24328a..e3f058231d91 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-04-07" +channel = "nightly-2025-04-14" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/expected/uninit/intrinsics/expected b/tests/expected/uninit/intrinsics/expected index d19d694df75e..e2ca608475a1 100644 --- a/tests/expected/uninit/intrinsics/expected +++ b/tests/expected/uninit/intrinsics/expected @@ -1,7 +1,3 @@ -std::ptr::read::>.unsupported_construct.\ - - Status: FAILURE\ - - Description: "Interaction between raw pointers and unions is not yet supported." - check_typed_swap_nonoverlapping.safety_check.\ - Status: FAILURE\ - Description: "Undefined Behavior: Reading from an uninitialized pointer of type `*mut u8`" diff --git a/tests/kani/FunctionContracts/modify_slice_elem.rs b/tests/kani/FunctionContracts/modify_slice_elem_fixme.rs similarity index 84% rename from tests/kani/FunctionContracts/modify_slice_elem.rs rename to tests/kani/FunctionContracts/modify_slice_elem_fixme.rs index 50df13b45af3..385db9983d88 100644 --- a/tests/kani/FunctionContracts/modify_slice_elem.rs +++ b/tests/kani/FunctionContracts/modify_slice_elem_fixme.rs @@ -2,6 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Check that Kani correctly verifies the contract that modifies slices. //! +//! This started failing with the 2025-04-05 toolchain upgrade +//! Tracking issue: https://github.com/model-checking/kani/issues/4029 +//! //! Note that this test used to crash while parsing the annotations. // kani-flags: -Zfunction-contracts extern crate kani; diff --git a/tests/perf/smol_str/src/lib.rs b/tests/perf/smol_str/src/lib.rs index 7fe771c1fc07..01f1881bad03 100644 --- a/tests/perf/smol_str/src/lib.rs +++ b/tests/perf/smol_str/src/lib.rs @@ -5,9 +5,11 @@ //! #[kani::proof] -#[kani::unwind(4)] +#[kani::unwind(13)] fn check_new() { - let data: [char; 3] = kani::any(); - let input: String = data.iter().collect(); + let data: [u8; 12] = kani::any(); + let res = String::from_utf8(data.into()); + kani::assume(res.is_ok()); + let input: String = res.unwrap(); smol_str::SmolStr::new(&input); } From 34e7c72345b7d4e548802bc5b3cf4c2b8c775629 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Fri, 18 Apr 2025 13:08:38 -0700 Subject: [PATCH 108/161] Upgrade toolchain to 2025-04-18 (#4031) By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- rust-toolchain.toml | 2 +- tools/scanner/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e3f058231d91..5cfcd0a002ef 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-04-14" +channel = "nightly-2025-04-18" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tools/scanner/src/lib.rs b/tools/scanner/src/lib.rs index 7f9555781ccf..fe2f30acb435 100644 --- a/tools/scanner/src/lib.rs +++ b/tools/scanner/src/lib.rs @@ -23,7 +23,7 @@ extern crate stable_mir; use crate::analysis::OverallStats; use rustc_middle::ty::TyCtxt; use rustc_session::config::OutputType; -use rustc_smir::{run_with_tcx, rustc_internal}; +use rustc_smir::run_with_tcx; use stable_mir::CompilerError; use std::ops::ControlFlow; use std::path::{Path, PathBuf}; From cb452e482b055ff0717238f6c19303999caa953a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 18 Apr 2025 23:47:23 -0700 Subject: [PATCH 109/161] Automatic toolchain upgrade to nightly-2025-04-19 (#4033) Update Rust toolchain from nightly-2025-04-18 to nightly-2025-04-19 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5cfcd0a002ef..dda17d7d0113 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-04-18" +channel = "nightly-2025-04-19" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 3ad11331531d468b2396192e006c15f889f3ef0f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 20 Apr 2025 22:49:56 +0200 Subject: [PATCH 110/161] Automatic toolchain upgrade to nightly-2025-04-20 (#4034) Update Rust toolchain from nightly-2025-04-19 to nightly-2025-04-20 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index dda17d7d0113..b6032f57860c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-04-19" +channel = "nightly-2025-04-20" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From a13041bb9322328e1cbab73557fd3fa8ed6421bb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 07:42:05 +0000 Subject: [PATCH 111/161] Automatic cargo update to 2025-04-21 (#4036) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88b7e64ba579..c5b22f2f13fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,9 +113,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "assert_cmd" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" +checksum = "2bd389a4b2970a01282ee455294913c0a43724daedcd1a24c3eb0ec1c1320b66" dependencies = [ "anstyle", "bstr", @@ -276,9 +276,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.36" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" dependencies = [ "clap_builder", "clap_derive", @@ -286,9 +286,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.36" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" dependencies = [ "anstream", "anstyle", @@ -794,9 +794,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ad87c89110f55e4cd4dc2893a9790820206729eaf221555f742d540b0724a0" +checksum = "59ec30f7142be6fe14e1b021f50b85db8df2d4324ea6e91ec3e5dcde092021d0" dependencies = [ "jiff-static", "log", @@ -807,9 +807,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d076d5b64a7e2fe6f0743f02c43ca4a6725c0f904203bfe276a5b3e793103605" +checksum = "526b834d727fd59d37b076b0c3236d9adde1b1729a4361e20b2026f738cc1dbe" dependencies = [ "proc-macro2", "quote", @@ -940,9 +940,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "linear-map" @@ -1326,9 +1326,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -1619,9 +1619,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] From aa7b39173a2d59dc946f79d2144b2f21f93e1445 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 13:04:46 -0700 Subject: [PATCH 112/161] Bump tests/perf/s2n-quic from `0413d9a` to `f42521d` (#4038) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `0413d9a` to `f42521d`.
Commits
  • f42521d fix(s2n-quic-dc): prioritize recovery ACK ranges over stream (#2614)
  • e6edaee fix(s2n-quic-dc): arm recv idle timer on init (#2613)
  • 7a59ead fix(s2n-quic-dc): only send PTO packets when there are outstanding stream seg...
  • dfde88c fix(s2n-quic-dc): correctly propagate stream flow errors (#2610)
  • ae715eb fix(s2n-quic-dc): only actively poll RPC response (#2608)
  • 0668ca4 fix(s2n-quic-dc): remove send inflight_timer (#2609)
  • e64af49 doc: automatically update CI artifacts (#2606)
  • See full diff in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 0413d9a618b9..f42521d87640 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 0413d9a618b9dfe53dd7ed3539a24f940a15e7af +Subproject commit f42521d8764046e9b8bb10551367e4b5d8b98716 From 228c5a5f078ff7b46d67724ed8362865266d709f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:56:59 +0200 Subject: [PATCH 113/161] Automatic toolchain upgrade to nightly-2025-04-21 (#4035) Update Rust toolchain from nightly-2025-04-20 to nightly-2025-04-21 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index b6032f57860c..69d50778fe52 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-04-20" +channel = "nightly-2025-04-21" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From cf92ce661a216fafba4cf809f2d52caa8cad4a32 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 23:19:46 +0200 Subject: [PATCH 114/161] Automatic toolchain upgrade to nightly-2025-04-22 (#4039) Update Rust toolchain from nightly-2025-04-21 to nightly-2025-04-22 without any other source changes. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 69d50778fe52..8135d86a6f81 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-04-21" +channel = "nightly-2025-04-22" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 9de733acecf73440a9cde189ab3cd3ddcdabab08 Mon Sep 17 00:00:00 2001 From: Samuel Thomas Date: Tue, 22 Apr 2025 16:35:14 -0700 Subject: [PATCH 115/161] Introduce BoundedArbitrary trait and macro for bounded proofs (#4000) This PR introduces the trait `BoundedArbitrary` which allows for creation of symbolic values for types up to some bound. This is useful for doing bounded proofs on types like strings and vecs. I wrote a more detailed overview of what this PR provides as a new section in the book. The implementation of BoundedArbitrary for the container types that I provide are pretty basic and experimental at the moment. Any pointers here would be useful, and we will probably need to iterate on their implementations based on actual performance testing. I added a test case that should fail verification using the `cover!` macro, however, I'm not sure that I used this correctly. Let me know if I should make this an expect test, or something else. I slightly restructured how we deal with the the `std/core` usage in `kani_core::kani_lib`. Previously we passed in either `std` or `core` to `kani_core::generate_arbitrary` and `generate_arbitrary` renamed the import with `use $core as core_path`. However, I couldn't use that same strategy for `kani_core::generate_bounded_arbitrary` because the import names would clash. So I lifted the `use $core as core_path` out of the `kani_core::generate_*` macros. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Sammy Thomas --- docs/src/SUMMARY.md | 1 + docs/src/reference/bounded_arbitrary.md | 81 +++++++++ library/kani/src/bounded_arbitrary.rs | 62 +++++++ library/kani/src/lib.rs | 1 + library/kani_core/src/arbitrary.rs | 3 +- library/kani_core/src/bounded_arbitrary.rs | 37 +++++ library/kani_core/src/lib.rs | 26 ++- library/kani_macros/src/derive.rs | 40 ++--- library/kani_macros/src/derive_bounded.rs | 155 ++++++++++++++++++ library/kani_macros/src/lib.rs | 33 ++++ tests/README.md | 1 + .../bounded-arbitrary/hash/hash.expected | 10 ++ tests/expected/bounded-arbitrary/hash/hash.rs | 23 +++ .../bounded-arbitrary/option/option.expected | 5 + .../bounded-arbitrary/option/option.rs | 18 ++ .../bounded-arbitrary/result/result.expected | 5 + .../bounded-arbitrary/result/result.rs | 23 +++ .../reverse_vec/vec.expected | 8 + .../bounded-arbitrary/reverse_vec/vec.rs | 56 +++++++ .../bounded-arbitrary/string/string.expected | 6 + .../bounded-arbitrary/string/string.rs | 15 ++ .../derive-bounded-arbitrary/enum.expected | 5 + .../expected/derive-bounded-arbitrary/enum.rs | 45 +++++ .../derive-bounded-arbitrary/struct.expected | 5 + .../derive-bounded-arbitrary/struct.rs | 26 +++ 25 files changed, 663 insertions(+), 27 deletions(-) create mode 100644 docs/src/reference/bounded_arbitrary.md create mode 100644 library/kani/src/bounded_arbitrary.rs create mode 100644 library/kani_core/src/bounded_arbitrary.rs create mode 100644 library/kani_macros/src/derive_bounded.rs create mode 100644 tests/expected/bounded-arbitrary/hash/hash.expected create mode 100644 tests/expected/bounded-arbitrary/hash/hash.rs create mode 100644 tests/expected/bounded-arbitrary/option/option.expected create mode 100644 tests/expected/bounded-arbitrary/option/option.rs create mode 100644 tests/expected/bounded-arbitrary/result/result.expected create mode 100644 tests/expected/bounded-arbitrary/result/result.rs create mode 100644 tests/expected/bounded-arbitrary/reverse_vec/vec.expected create mode 100644 tests/expected/bounded-arbitrary/reverse_vec/vec.rs create mode 100644 tests/expected/bounded-arbitrary/string/string.expected create mode 100644 tests/expected/bounded-arbitrary/string/string.rs create mode 100644 tests/expected/derive-bounded-arbitrary/enum.expected create mode 100644 tests/expected/derive-bounded-arbitrary/enum.rs create mode 100644 tests/expected/derive-bounded-arbitrary/struct.expected create mode 100644 tests/expected/derive-bounded-arbitrary/struct.rs diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 8cd6a1ebb7b0..0b618c577021 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -17,6 +17,7 @@ - [Reference](./reference.md) - [Attributes](./reference/attributes.md) + - [Bounded Non-deterministic variables](./reference/bounded_arbitrary.md) - [Experimental features](./reference/experimental/experimental-features.md) - [Automatic Harness Generation](./reference/experimental/autoharness.md) - [Coverage](./reference/experimental/coverage.md) diff --git a/docs/src/reference/bounded_arbitrary.md b/docs/src/reference/bounded_arbitrary.md new file mode 100644 index 000000000000..03b7acf629e1 --- /dev/null +++ b/docs/src/reference/bounded_arbitrary.md @@ -0,0 +1,81 @@ +# Bounded Non-deterministic variables + +This is an experimental feature that allows you to bound otherwise unbounded types. For example, `Vec` does not have an `Arbitrary` implementation because vectors can grow arbitrarily in size. One way of handling proofs about such types is to make the problem easier, and only prove a property up to some bound. Of course, the proof is only valid up to the bound, but can still be useful in providing confidence that your code is correct. + +## Example + +As a toy example, let's prove, up to some bound, that reversing a vector twice gives you back the original vector. Here's a reversing function: + +```rust +fn reverse_vector(mut input: Vec) -> Vec { + let mut reversed = vec![]; + for _ in 0..input.len() { + reversed.push(input.pop().unwrap()); + } + reversed +} +``` + +We can use `BoundedAny` to write a proof harness: + +```rust +#[kani::proof] +#[kani::unwind(17)] +fn check_reverse_is_its_own_inverse() { + // We use BoundedAny to construct a vector that has at most length 16 + let input: Vec = kani::bounded_any::<_, 16>(); + + let double_reversed = reverse_vector(reverse_vector(input.clone())); + + // we assert that every value in the input is the same as the value in the + // doubly reversed list + for i in 0..input.len() { + assert_eq!(input[i], double_reversed[i]) + } +} +``` + +Then, with `kani` we can prove that our reverse function is indeed its own inverse, for vectors up to size 16. + +## Proof Incompleteness + +It's very important to note, that this is **not** a complete proof that this function is correct. To drive this point home, consider this bad implementation of `reverse_vector`: + +```rust +fn bad_reverse_vector(mut input: Vec) -> Vec { + let mut reversed = vec![]; + for i in 0..input.len() { + if i < 16 { + reversed.push(input.pop().unwrap()); + } else { + reversed.push(T::default()) + } + } + reversed +} +``` + +Now the same harness as before is still successful! Even though this implementation is obviously wrong. If only we had tried a slightly bigger bound... + +So, while bounded proofs can be useful, beware that they are also incomplete. It might be worth-while to test multiple bounds. + +## Custom Bounded Arbitrary implementations + +Kani provides several implementations of `BoundedArbitrary`, but you can also implement `BoundedArbitrary` for yourself. + +We provide a derive macro that should work in most cases: + +```rust +#[derive(BoundedArbitrary)] +struct MyVector { + #[bounded] + vector: Vec, + capacity: usize +} +``` + +You must specify which fields should be bounded using the `#[bounded]` attribute. All other fields must derive `Arbitrary`. + +### Limitations + +Currently you can only specify a single bound for the entire type, and all bounded fields use the same bound. If different bounds would be useful, let us know through [filing an issue](https://github.com/model-checking/kani/issues/new/choose) and we can probably lift this restriction. diff --git a/library/kani/src/bounded_arbitrary.rs b/library/kani/src/bounded_arbitrary.rs new file mode 100644 index 000000000000..09486df9a13e --- /dev/null +++ b/library/kani/src/bounded_arbitrary.rs @@ -0,0 +1,62 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This module introduces implementations for some std containers. + +use kani::{Arbitrary, BoundedArbitrary}; + +// This implementation overlaps with `kani::any_vec` in `kani/library/kani/src/vec.rs`. +// This issue `https://github.com/model-checking/kani/issues/4027` tracks deprecating +// `kani::any_vec` in favor of this implementation. +impl BoundedArbitrary for Vec { + fn bounded_any() -> Self { + let real_length = kani::any_where(|&size| size <= N); + let array: [T; N] = kani::any(); + let mut vec = Vec::from(array); + vec.truncate(real_length); + vec + } +} + +impl BoundedArbitrary for String { + fn bounded_any() -> Self { + let bytes: [u8; N] = kani::any(); + + if let Some(s) = bytes.utf8_chunks().next() { s.valid().into() } else { String::new() } + } +} + +impl BoundedArbitrary + for std::collections::HashMap> +where + K: Arbitrary + std::cmp::Eq + std::hash::Hash, + V: Arbitrary, +{ + fn bounded_any() -> Self { + let mut hash_map = std::collections::HashMap::default(); + for _ in 0..N { + // this check seems to perform better than 0..kany::any_where(|l| *l <= N) + if bool::any() { + hash_map.insert(K::any(), V::any()); + } + } + hash_map + } +} + +impl BoundedArbitrary + for std::collections::HashSet> +where + V: Arbitrary + std::cmp::Eq + std::hash::Hash, +{ + fn bounded_any() -> Self { + let mut hash_set = std::collections::HashSet::default(); + for _ in 0..N { + // this check seems to perform better than 0..kany::any_where(|l| *l <= N) + if bool::any() { + hash_set.insert(V::any()); + } + } + hash_set + } +} diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index b284a3544b67..7c7a6a756917 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -26,6 +26,7 @@ extern crate self as kani; pub mod arbitrary; +pub mod bounded_arbitrary; #[cfg(feature = "concrete_playback")] mod concrete_playback; pub mod futures; diff --git a/library/kani_core/src/arbitrary.rs b/library/kani_core/src/arbitrary.rs index 3ef474b65364..18b84a72e8e5 100644 --- a/library/kani_core/src/arbitrary.rs +++ b/library/kani_core/src/arbitrary.rs @@ -14,11 +14,10 @@ mod slice; #[macro_export] #[allow(clippy::crate_in_macro_def)] macro_rules! generate_arbitrary { - ($core:path) => { + () => { use core_path::marker::{PhantomData, PhantomPinned}; use core_path::mem::MaybeUninit; use core_path::ptr::{self, addr_of_mut}; - use $core as core_path; pub trait Arbitrary where diff --git a/library/kani_core/src/bounded_arbitrary.rs b/library/kani_core/src/bounded_arbitrary.rs new file mode 100644 index 000000000000..a35bc1b53b1b --- /dev/null +++ b/library/kani_core/src/bounded_arbitrary.rs @@ -0,0 +1,37 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This module introduces the `BoundedArbitrary` trait. + +#[macro_export] +#[allow(clippy::crate_in_macro_def)] +macro_rules! generate_bounded_arbitrary { + () => { + use core_path::ops::Deref; + + pub trait BoundedArbitrary { + fn bounded_any() -> Self; + } + + impl BoundedArbitrary for Option + where + T: BoundedArbitrary, + { + fn bounded_any() -> Self { + let opt: Option<()> = any(); + opt.map(|_| T::bounded_any::()) + } + } + + impl BoundedArbitrary for Result + where + T: BoundedArbitrary, + E: BoundedArbitrary, + { + fn bounded_any() -> Self { + let res: Result<(), ()> = any(); + res.map(|_| T::bounded_any::()).map_err(|_| E::bounded_any::()) + } + } + }; +} diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index 51b243c9ad33..36c76c1bab56 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -21,6 +21,7 @@ #![feature(f128)] mod arbitrary; +mod bounded_arbitrary; mod float; mod mem; mod mem_init; @@ -41,9 +42,12 @@ macro_rules! kani_lib { #[unstable(feature = "kani", issue = "none")] pub mod kani { // We need to list them all today because there is conflict with unstable. + use core as core_path; pub use kani_core::*; - kani_core::kani_intrinsics!(core); - kani_core::generate_arbitrary!(core); + + kani_core::kani_intrinsics!(); + kani_core::generate_arbitrary!(); + kani_core::generate_bounded_arbitrary!(); kani_core::generate_models!(); pub mod float { @@ -62,8 +66,11 @@ macro_rules! kani_lib { (kani) => { pub use kani_core::*; - kani_core::kani_intrinsics!(std); - kani_core::generate_arbitrary!(std); + use std as core_path; + + kani_core::kani_intrinsics!(); + kani_core::generate_arbitrary!(); + kani_core::generate_bounded_arbitrary!(); kani_core::generate_models!(); pub mod float { @@ -138,7 +145,7 @@ macro_rules! kani_lib { #[allow(clippy::crate_in_macro_def)] #[macro_export] macro_rules! kani_intrinsics { - ($core:tt) => { + () => { /// Creates an assumption that will be valid after this statement run. Note that the assumption /// will only be applied for paths that follow the assumption. If the assumption doesn't hold, the /// program will exit successfully. @@ -253,6 +260,15 @@ macro_rules! kani_intrinsics { T::any() } + /// Creates a symbolic value *bounded* by `N`. Bounded means `|T| <= N`. The type + /// implementing BoundedArbitrary decides exactly what size means for them. + /// + /// *Note*: Any proof using a bounded symbolic value is only valid up to that bound. + #[inline(always)] + pub fn bounded_any() -> T { + T::bounded_any::() + } + /// This function is only used for function contract instrumentation. /// It behaves exaclty like `kani::any()`, except it will check for the trait bounds /// at compilation time. It allows us to avoid type checking errors while using function diff --git a/library/kani_macros/src/derive.rs b/library/kani_macros/src/derive.rs index 539f8b671798..cfc3b1df8fc9 100644 --- a/library/kani_macros/src/derive.rs +++ b/library/kani_macros/src/derive.rs @@ -19,23 +19,23 @@ use syn::{ }; #[cfg(feature = "no_core")] -macro_rules! kani_path { - ($span:expr) => { - quote_spanned! { $span => core::kani } - }; - () => { - quote! { core::kani } - }; +pub(crate) fn kani_path() -> TokenStream { + quote! { core::kani } } #[cfg(not(feature = "no_core"))] -macro_rules! kani_path { - ($span:expr) => { - quote_spanned! { $span => kani } - }; - () => { - quote! { kani } - }; +pub(crate) fn kani_path() -> TokenStream { + quote! { kani } +} + +#[cfg(feature = "no_core")] +pub(crate) fn kani_path_spanned(span: Span) -> TokenStream { + quote_spanned! { span=> core::kani } +} + +#[cfg(not(feature = "no_core"))] +pub(crate) fn kani_path_spanned(span: Span) -> TokenStream { + quote_spanned! { span=> kani } } /// Generate the Arbitrary implementation for the given type. @@ -48,7 +48,7 @@ pub fn expand_derive_arbitrary(item: proc_macro::TokenStream) -> proc_macro::Tok let trait_name = "Arbitrary"; let derive_item = parse_macro_input!(item as DeriveInput); let item_name = &derive_item.ident; - let kani_path = kani_path!(); + let kani_path = kani_path(); let body = fn_any_body(item_name, &derive_item.data); // Get the safety constraints (if any) to produce type-safe values @@ -87,7 +87,7 @@ pub fn expand_derive_arbitrary(item: proc_macro::TokenStream) -> proc_macro::Tok /// Add a bound `T: Arbitrary` to every type parameter T. fn add_trait_bound_arbitrary(mut generics: Generics) -> Generics { - let kani_path = kani_path!(); + let kani_path = kani_path(); generics.params.iter_mut().for_each(|param| { if let GenericParam::Type(type_param) = param { type_param.bounds.push(parse_quote!(#kani_path::Arbitrary)); @@ -251,7 +251,7 @@ fn init_symbolic_item(ident: &Ident, fields: &Fields) -> TokenStream { // Self(kani::any(), kani::any(), ..., kani::any()); let init = fields.unnamed.iter().map(|field| { let span = field.span(); - let kani_path = kani_path!(span); + let kani_path = kani_path_spanned(span); quote_spanned! {span=> #kani_path::any() } @@ -385,7 +385,7 @@ fn fn_any_enum(ident: &Ident, data: &DataEnum) -> TokenStream { } }); - let kani_path = kani_path!(); + let kani_path = kani_path(); quote! { match #kani_path::any() { #(#arms)* @@ -413,7 +413,7 @@ pub fn expand_derive_invariant(item: proc_macro::TokenStream) -> proc_macro::Tok let trait_name = "Invariant"; let derive_item = parse_macro_input!(item as DeriveInput); let item_name = &derive_item.ident; - let kani_path = kani_path!(); + let kani_path = kani_path(); let safe_body = safe_body_with_calls(item_name, &derive_item, trait_name); let field_refs = field_refs(item_name, &derive_item.data); @@ -501,7 +501,7 @@ fn has_field_safety_constraints_inner(_ident: &Ident, fields: &Fields) -> bool { /// Add a bound `T: Invariant` to every type parameter T. pub fn add_trait_bound_invariant(mut generics: Generics) -> Generics { - let kani_path = kani_path!(); + let kani_path = kani_path(); generics.params.iter_mut().for_each(|param| { if let GenericParam::Type(type_param) = param { type_param.bounds.push(parse_quote!(#kani_path::Invariant)); diff --git a/library/kani_macros/src/derive_bounded.rs b/library/kani_macros/src/derive_bounded.rs new file mode 100644 index 000000000000..b95624594453 --- /dev/null +++ b/library/kani_macros/src/derive_bounded.rs @@ -0,0 +1,155 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use proc_macro_error2::abort; +use proc_macro2::{Span, TokenStream}; +use quote::quote; + +use crate::derive::kani_path; + +/// Generate the `DeriveArbitrary` implementation for the given type. +/// +/// Fields of the given type marked with `#[bounded]` will use +/// `BoundedArbitrary::bounded_any()` while other fields fall back to `kani::any()` +/// +/// Current limitation: Generic bounds are restricted to `T: kani::Arbitrary` rather than +/// `T: kani::BoundedArbitrary`. This is the right thing to do when the generic is +/// used in some other container that you want to be bounded; like in the following +/// example: +/// +/// ```rust +/// #[derive(BoundedArbitrary)] +/// struct MyVec { +/// #[bounded] +/// vec: Vec, +/// cap: usize +/// } +/// ``` +/// +/// However, if you use the generic raw in a field and want it to be bounded, this +/// won't work. The following doesn't compile: +/// +/// ```rust +/// #[derive(BoundedArbitrary)] +/// struct Foo { +/// #[bounded] +/// bar: T +/// } +/// ``` +/// +/// TODO: have the generic bound change based on how it's used if we can detect this +/// automatically, otherwise support an attribute on the generic. +pub(crate) fn expand_derive_bounded_arbitrary( + item: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let parsed = syn::parse_macro_input!(item as syn::DeriveInput); + + let constructor = match &parsed.data { + syn::Data::Struct(data_struct) => { + generate_type_constructor(quote!(Self), &data_struct.fields) + } + syn::Data::Enum(data_enum) => enum_constructor(&parsed.ident, data_enum), + syn::Data::Union(data_union) => union_constructor(&parsed.ident, data_union), + }; + + // add `T: Arbitrary` bounds for generics + let (generics, clauses) = quote_generics(&parsed.generics); + let name = &parsed.ident; + + // generate the implementation + let kani_path = kani_path(); + quote! { + impl #generics #kani_path::BoundedArbitrary for #name #generics + #clauses + { + fn bounded_any() -> Self { + #constructor + } + } + } + .into() +} + +/// Generates the call to construct the given type like so: +/// +/// ``` +/// Foo(kani::any::(), String::bounded_any::()) +/// Foo { +/// x: kani::any::(), +/// y: kani::bounded_any::() +/// } +/// ``` +fn generate_type_constructor(type_name: TokenStream, fields: &syn::Fields) -> TokenStream { + let field_calls = fields.iter().map(generate_any_call); + if fields.iter().all(|f| f.ident.is_some()) { + quote!(#type_name { #(#field_calls),* }) + } else { + quote!(#type_name( #(#field_calls),* )) + } +} + +/// Generates a `match` case to construct each variant of the given type. Uses a +/// symbolic `usize` to decide which variant to construct. +fn enum_constructor(ident: &syn::Ident, data_enum: &syn::DataEnum) -> TokenStream { + let variant_constructors = data_enum.variants.iter().map(|variant| { + let variant_name = &variant.ident; + generate_type_constructor(quote!(#ident::#variant_name), &variant.fields) + }); + let n_variants = data_enum.variants.len(); + let cases = variant_constructors.enumerate().map(|(idx, var_constr)| { + if idx < n_variants - 1 { quote!(#idx => #var_constr) } else { quote!(_ => #var_constr) } + }); + + let kani_path = kani_path(); + quote! { + match #kani_path::any() { + #(#cases),* , + } + } +} + +fn union_constructor(ident: &syn::Ident, _data_union: &syn::DataUnion) -> TokenStream { + abort!(Span::call_site(), "Cannot derive `BoundedArbitrary` for `{}` union", ident; + note = ident.span() => + "`#[derive(BoundedArbitrary)]` cannot be used for unions such as `{}`", ident + ) +} + +/// Generate the necessary generic parameter declarations and generic bounds for a +/// type. +/// +/// ```rust +/// impl BoundedArbitrary for Foo +/// where +/// A: Arbitrary +/// B: Arbitrary +/// { +/// ... +/// } +/// ``` +fn quote_generics(generics: &syn::Generics) -> (TokenStream, TokenStream) { + let kani_path = kani_path(); + let params = generics.type_params().map(|param| quote!(#param)).collect::>(); + let where_clauses = generics.type_params().map(|param| quote!(#param : #kani_path::Arbitrary)); + if !params.is_empty() { + (quote!(<#(#params),*>), quote!(where #(#where_clauses),*)) + } else { + Default::default() + } +} + +/// Generates a symbolic value based on whether the field has the `#[bounded]` +/// attribute. If the field is not bounded, generate `kani::any()` otherwise generate +/// `kani::bounded_any()`. +fn generate_any_call(field: &syn::Field) -> TokenStream { + let ty = &field.ty; + let kani_path = kani_path(); + let any_call = if field.attrs.iter().any(|attr| attr.path().is_ident("bounded")) { + quote!(#kani_path::bounded_any::<#ty, N>()) + } else { + quote!(#kani_path::any::<#ty>()) + }; + + let ident_tok = field.ident.as_ref().map(|ident| quote!(#ident: )); + quote!(#ident_tok #any_call) +} diff --git a/library/kani_macros/src/lib.rs b/library/kani_macros/src/lib.rs index f571e4592c4c..7693eb2c38f9 100644 --- a/library/kani_macros/src/lib.rs +++ b/library/kani_macros/src/lib.rs @@ -10,6 +10,7 @@ #![feature(proc_macro_diagnostic)] #![feature(proc_macro_span)] mod derive; +mod derive_bounded; // proc_macro::quote is nightly-only, so we'll cobble things together instead use proc_macro::TokenStream; @@ -207,6 +208,38 @@ pub fn derive_arbitrary(item: TokenStream) -> TokenStream { derive::expand_derive_arbitrary(item) } +/// Allow users to generate `BoundedArbitrary` implementations by using the +/// `#[derive(BoundedArbitrary)]` macro. +/// +/// ## Specifying bounded fields +/// +/// By default, every field of a struct is required to derive `Arbitrary`, not +/// `BoundedArbitrary`. This is because there is no general way to determine if a +/// particular field should be bounded or not. You must annotate the fields you want +/// to be bounded using the `#[bounded]` attribute. +/// +/// For example, we only want the vector field in the following definition to be +/// bounded. +/// +/// ```rust +/// #[derive(BoundedArbitrary)] +/// struct MyVector { +/// #[bounded] +/// vector: Vec, +/// capacity: usize +/// } +/// ``` +/// +/// ## Safety +/// +/// Using `BoundedArbitrary` makes proofs incomplete. It is useful for increasing +/// confidence that some code is correct, but does not prove that absolutely. +#[proc_macro_error] +#[proc_macro_derive(BoundedArbitrary, attributes(bounded))] +pub fn derive_bounded_arbitrary(item: TokenStream) -> TokenStream { + derive_bounded::expand_derive_bounded_arbitrary(item) +} + /// Allow users to auto generate `Invariant` implementations by using /// `#[derive(Invariant)]` macro. /// diff --git a/tests/README.md b/tests/README.md index 67cae9791d2c..971012b95f0c 100644 --- a/tests/README.md +++ b/tests/README.md @@ -2,3 +2,4 @@ You can see more details about each test suite in this folder in the [Kani testing suites](https://model-checking.github.io/kani/regression-testing.html#kani-testing-suites). + diff --git a/tests/expected/bounded-arbitrary/hash/hash.expected b/tests/expected/bounded-arbitrary/hash/hash.expected new file mode 100644 index 000000000000..7b5dde8cb438 --- /dev/null +++ b/tests/expected/bounded-arbitrary/hash/hash.expected @@ -0,0 +1,10 @@ +Checking harness check_hashset... + + ** 2 of 2 cover properties satisfied + +Checking harness check_hashmap... + + ** 2 of 2 cover properties satisfied + +Manual Harness Summary: +Complete - 2 successfully verified harnesses, 0 failures, 2 total. \ No newline at end of file diff --git a/tests/expected/bounded-arbitrary/hash/hash.rs b/tests/expected/bounded-arbitrary/hash/hash.rs new file mode 100644 index 000000000000..3f676d133a4c --- /dev/null +++ b/tests/expected/bounded-arbitrary/hash/hash.rs @@ -0,0 +1,23 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This file tests whether we can generate a bounded Hashmap/Hashset that has any possible size between 0-BOUND + +#[kani::proof] +#[kani::unwind(5)] +fn check_hashmap() { + // A larger bound causes this to take a long time + const BOUND: usize = 1; + let hash_map: std::collections::HashMap = kani::bounded_any::<_, BOUND>(); + kani::cover!(hash_map.len() == 0); + kani::cover!(hash_map.len() == 1); +} + +#[kani::proof] +#[kani::unwind(5)] +fn check_hashset() { + const BOUND: usize = 1; + let hash_set: std::collections::HashSet = kani::bounded_any::<_, BOUND>(); + kani::cover!(hash_set.len() == 0); + kani::cover!(hash_set.len() == 1); +} diff --git a/tests/expected/bounded-arbitrary/option/option.expected b/tests/expected/bounded-arbitrary/option/option.expected new file mode 100644 index 000000000000..0d1521b75d9a --- /dev/null +++ b/tests/expected/bounded-arbitrary/option/option.expected @@ -0,0 +1,5 @@ +Checking harness check_option... + +** 6 of 6 cover properties satisfied + +Complete - 1 successfully verified harnesses, 0 failures, 1 total. \ No newline at end of file diff --git a/tests/expected/bounded-arbitrary/option/option.rs b/tests/expected/bounded-arbitrary/option/option.rs new file mode 100644 index 000000000000..6acb7be8f3aa --- /dev/null +++ b/tests/expected/bounded-arbitrary/option/option.rs @@ -0,0 +1,18 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This file tests whether we can generate a bounded option (`T: BoundedArbitrary` in `Option`) that correctly +//! represents None or Some(t) where t is bounded by N. + +#[kani::proof] +fn check_option() { + let my_option: Option> = kani::bounded_any::<_, 4>(); + kani::cover!(my_option.is_none()); + if let Some(inner) = my_option { + kani::cover!(inner.len() == 0); + kani::cover!(inner.len() == 1); + kani::cover!(inner.len() == 2); + kani::cover!(inner.len() == 3); + kani::cover!(inner.len() == 4); + } +} diff --git a/tests/expected/bounded-arbitrary/result/result.expected b/tests/expected/bounded-arbitrary/result/result.expected new file mode 100644 index 000000000000..517f0b5a99df --- /dev/null +++ b/tests/expected/bounded-arbitrary/result/result.expected @@ -0,0 +1,5 @@ +Checking harness check_result... + +** 10 of 10 cover properties satisfied + +Complete - 1 successfully verified harnesses, 0 failures, 1 total. \ No newline at end of file diff --git a/tests/expected/bounded-arbitrary/result/result.rs b/tests/expected/bounded-arbitrary/result/result.rs new file mode 100644 index 000000000000..c095c2cf3833 --- /dev/null +++ b/tests/expected/bounded-arbitrary/result/result.rs @@ -0,0 +1,23 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#[kani::proof] +fn check_result() { + let my_result: Result, Vec> = kani::bounded_any::<_, 4>(); + match my_result { + Ok(inner_vec) => { + kani::cover!(inner_vec.len() == 0); + kani::cover!(inner_vec.len() == 1); + kani::cover!(inner_vec.len() == 2); + kani::cover!(inner_vec.len() == 3); + kani::cover!(inner_vec.len() == 4); + } + Err(inner_vec) => { + kani::cover!(inner_vec.len() == 0); + kani::cover!(inner_vec.len() == 1); + kani::cover!(inner_vec.len() == 2); + kani::cover!(inner_vec.len() == 3); + kani::cover!(inner_vec.len() == 4); + } + } +} diff --git a/tests/expected/bounded-arbitrary/reverse_vec/vec.expected b/tests/expected/bounded-arbitrary/reverse_vec/vec.expected new file mode 100644 index 000000000000..af714c630c4b --- /dev/null +++ b/tests/expected/bounded-arbitrary/reverse_vec/vec.expected @@ -0,0 +1,8 @@ +Checking harness check_reverse_is_its_own_inverse_should_fail...\ + +Checking harness check_reverse_is_its_own_inverse_incomplete...\ + +Checking harness check_reverse_is_its_own_inverse...\ + +Verification failed for - check_reverse_is_its_own_inverse_should_fail +Complete - 2 successfully verified harnesses, 1 failures, 3 total. \ No newline at end of file diff --git a/tests/expected/bounded-arbitrary/reverse_vec/vec.rs b/tests/expected/bounded-arbitrary/reverse_vec/vec.rs new file mode 100644 index 000000000000..252b1edf1f42 --- /dev/null +++ b/tests/expected/bounded-arbitrary/reverse_vec/vec.rs @@ -0,0 +1,56 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that users can use BoundedArbitrary to perform bounded verification of functions that use Vec. + +extern crate kani; + +fn reverse_vector(mut input: Vec) -> Vec { + let mut reversed = vec![]; + for _ in 0..input.len() { + reversed.push(input.pop().unwrap()); + } + reversed +} + +fn bad_reverse_vector(mut input: Vec) -> Vec { + let mut reversed = vec![]; + for i in 0..input.len() { + if i < N { + reversed.push(input.pop().unwrap()); + } else { + reversed.push(T::default()) + } + } + reversed +} + +#[kani::proof] +#[kani::unwind(5)] +fn check_reverse_is_its_own_inverse() { + let input: Vec = kani::bounded_any::<_, 4>(); + let double_reversed = reverse_vector(reverse_vector(input.clone())); + for i in 0..input.len() { + assert_eq!(input[i], double_reversed[i]) + } +} + +#[kani::proof] +#[kani::unwind(17)] +fn check_reverse_is_its_own_inverse_incomplete() { + let input: Vec = kani::bounded_any::<_, 16>(); + let double_reversed = bad_reverse_vector::<_, 16>(bad_reverse_vector::<_, 16>(input.clone())); + for i in 0..input.len() { + assert_eq!(input[i], double_reversed[i]) + } +} + +#[kani::proof] +#[kani::unwind(6)] +fn check_reverse_is_its_own_inverse_should_fail() { + let input: Vec = kani::bounded_any::<_, 5>(); + let double_reversed = bad_reverse_vector::<_, 4>(bad_reverse_vector::<_, 4>(input.clone())); + for i in 0..input.len() { + assert_eq!(input[i], double_reversed[i]) + } +} diff --git a/tests/expected/bounded-arbitrary/string/string.expected b/tests/expected/bounded-arbitrary/string/string.expected new file mode 100644 index 000000000000..0451aadd20e5 --- /dev/null +++ b/tests/expected/bounded-arbitrary/string/string.expected @@ -0,0 +1,6 @@ +Checking harness check_string... + +** 6 of 6 cover properties satisfied + +Manual Harness Summary: +Complete - 1 successfully verified harnesses, 0 failures, 1 total. \ No newline at end of file diff --git a/tests/expected/bounded-arbitrary/string/string.rs b/tests/expected/bounded-arbitrary/string/string.rs new file mode 100644 index 000000000000..721de20c1d4c --- /dev/null +++ b/tests/expected/bounded-arbitrary/string/string.rs @@ -0,0 +1,15 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#[kani::proof] +#[kani::unwind(6)] +fn check_string() { + let s: String = kani::bounded_any::<_, 5>(); + + kani::cover!(s == String::from("")); + kani::cover!(s == String::from("a")); + kani::cover!(s == String::from("ab")); + kani::cover!(s == String::from("abc")); + kani::cover!(s == String::from("abcd")); + kani::cover!(s == String::from("abcde")); +} diff --git a/tests/expected/derive-bounded-arbitrary/enum.expected b/tests/expected/derive-bounded-arbitrary/enum.expected new file mode 100644 index 000000000000..96b9bc8624da --- /dev/null +++ b/tests/expected/derive-bounded-arbitrary/enum.expected @@ -0,0 +1,5 @@ +Checking harness check_enum... + + ** 15 of 15 cover properties satisfied + +Complete - 1 successfully verified harnesses, 0 failures, 1 total. \ No newline at end of file diff --git a/tests/expected/derive-bounded-arbitrary/enum.rs b/tests/expected/derive-bounded-arbitrary/enum.rs new file mode 100644 index 000000000000..67ef8a71c8bb --- /dev/null +++ b/tests/expected/derive-bounded-arbitrary/enum.rs @@ -0,0 +1,45 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that derive BoundedArbitrary macro works on enums + +#[allow(unused)] +#[derive(kani::BoundedArbitrary)] +enum Enum { + A(#[bounded] String), + B(#[bounded] Vec, usize), + C { + #[bounded] + x: Vec, + y: bool, + }, +} + +#[kani::proof] +#[kani::unwind(6)] +fn check_enum() { + let any_enum: Enum = kani::bounded_any::<_, 4>(); + match any_enum { + Enum::A(s) => { + kani::cover!(s.len() == 0); + kani::cover!(s.len() == 1); + kani::cover!(s.len() == 2); + kani::cover!(s.len() == 3); + kani::cover!(s.len() == 4); + } + Enum::B(v, _) => { + kani::cover!(v.len() == 0); + kani::cover!(v.len() == 1); + kani::cover!(v.len() == 2); + kani::cover!(v.len() == 3); + kani::cover!(v.len() == 4); + } + Enum::C { x, y: _ } => { + kani::cover!(x.len() == 0); + kani::cover!(x.len() == 1); + kani::cover!(x.len() == 2); + kani::cover!(x.len() == 3); + kani::cover!(x.len() == 4); + } + } +} diff --git a/tests/expected/derive-bounded-arbitrary/struct.expected b/tests/expected/derive-bounded-arbitrary/struct.expected new file mode 100644 index 000000000000..d0eaf7a6d8b5 --- /dev/null +++ b/tests/expected/derive-bounded-arbitrary/struct.expected @@ -0,0 +1,5 @@ +Checking harness check_my_vec... + + ** 5 of 5 cover properties satisfied + +Complete - 1 successfully verified harnesses, 0 failures, 1 total. \ No newline at end of file diff --git a/tests/expected/derive-bounded-arbitrary/struct.rs b/tests/expected/derive-bounded-arbitrary/struct.rs new file mode 100644 index 000000000000..311680dbe65b --- /dev/null +++ b/tests/expected/derive-bounded-arbitrary/struct.rs @@ -0,0 +1,26 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that derive BoundedArbitrary macro works on enums + +extern crate kani; +use kani::BoundedArbitrary; + +#[derive(BoundedArbitrary)] +#[allow(unused)] +struct MyVector { + #[bounded] + vector: Vec, + cap: usize, +} + +#[kani::proof] +#[kani::unwind(6)] +fn check_my_vec() { + let my_vec: MyVector = kani::bounded_any::<_, 4>(); + kani::cover!(my_vec.vector.len() == 0); + kani::cover!(my_vec.vector.len() == 1); + kani::cover!(my_vec.vector.len() == 2); + kani::cover!(my_vec.vector.len() == 3); + kani::cover!(my_vec.vector.len() == 4); +} From 0aa6c4124bbdd758b70c461111803ba2bc61cd1e Mon Sep 17 00:00:00 2001 From: clubby789 Date: Wed, 23 Apr 2025 05:49:27 +0100 Subject: [PATCH 116/161] Support `trait_upcasting` (#4001) This adds support for the recently stabilized `trait_upcasting` feature. Currently, when we cast between non-matching dyn traits, codegen incorrectly reuses the old vtable, leading to confusing issues (#3998). This reproduces the upcasting logic from upstream rustc to load the vptr when performing the coercion. Resolves #358 Resolves #3998 This was tested using the motivating example from the linked issue. I will add further tests to make sure this feature is robust. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: clubby789 --- .../codegen_cprover_gotoc/codegen/rvalue.rs | 62 ++++++++++++++++--- .../src/codegen_cprover_gotoc/codegen/typ.rs | 59 +++++++++++++++--- tests/kani/DynTrait/upcast.rs | 29 +++++++++ 3 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 tests/kani/DynTrait/upcast.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index a3aa20f05b1b..243262a875b5 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -26,7 +26,10 @@ use stable_mir::mir::mono::Instance; use stable_mir::mir::{ AggregateKind, BinOp, CastKind, NullOp, Operand, Place, PointerCoercion, Rvalue, UnOp, }; -use stable_mir::ty::{ClosureKind, IntTy, RigidTy, Size, Ty, TyConst, TyKind, UintTy, VariantIdx}; +use stable_mir::ty::{ + Binder, ClosureKind, ExistentialPredicate, IntTy, RigidTy, Size, Ty, TyConst, TyKind, UintTy, + VariantIdx, +}; use std::collections::BTreeMap; use tracing::{debug, trace, warn}; @@ -1528,9 +1531,26 @@ impl GotocCtx<'_> { VtblEntry::MetadataSize => Some(vt_size.clone()), VtblEntry::MetadataAlign => Some(vt_align.clone()), VtblEntry::Vacant => None, - // TODO: trait upcasting - // https://github.com/model-checking/kani/issues/358 - VtblEntry::TraitVPtr(_trait_ref) => None, + VtblEntry::TraitVPtr(trait_ref) => { + let projections = match dst_mir_type.kind() { + TyKind::RigidTy(RigidTy::Dynamic(predicates, ..)) => predicates + .iter() + .filter_map(|pred| match &pred.value { + ExistentialPredicate::Projection(proj) => { + Some(Binder::bind_with_vars( + proj.clone(), + pred.bound_vars.clone(), + )) + } + _ => None, + }) + .collect(), + _ => vec![], + }; + + let dyn_ref = ctx.trait_ref_to_dyn_trait(*trait_ref, projections); + Some(ctx.codegen_vtable(src_mir_type, dyn_ref, loc).address_of()) + } VtblEntry::Method(instance) => Some(ctx.codegen_vtable_method_field( rustc_internal::stable(instance), trait_type, @@ -1593,8 +1613,15 @@ impl GotocCtx<'_> { }; slice_fat_ptr(fat_ptr_type, dst_data_expr, dst_goto_len, &self.symbol_table) } - (TyKind::RigidTy(RigidTy::Dynamic(..)), TyKind::RigidTy(RigidTy::Dynamic(..))) => { - // Cast between fat pointers. Cast the data and the source + ( + ref src_ty @ TyKind::RigidTy(RigidTy::Dynamic(.., ref src_dyn_kind)), + ref dst_ty @ TyKind::RigidTy(RigidTy::Dynamic(.., ref dst_dyn_kind)), + ) => { + assert_eq!( + src_dyn_kind, dst_dyn_kind, + "casting from `{src_dyn_kind:?}` to `{dst_dyn_kind:?}` is not supported" + ); + // Cast between dyn pointers. Cast the data and the source let src_data = src_goto_expr.to_owned().member("data", &self.symbol_table); let dst_data = src_data.cast_to(dst_data_type); @@ -1602,9 +1629,28 @@ impl GotocCtx<'_> { let src_vtable = src_goto_expr.member("vtable", &self.symbol_table); let vtable_name = self.vtable_name_stable(metadata_dst_type); let vtable_ty = Type::struct_tag(vtable_name).to_pointer(); - let dst_vtable = src_vtable.cast_to(vtable_ty); - // Construct a fat pointer with the same (casted) fields and new type + // Note: Logic based on upstream rustc_codegen_ssa: + // https://github.com/rust-lang/rust/blob/8bf5a8d/compiler/rustc_codegen_ssa/src/base.rs#L171 + let dst_vtable = if src_ty.trait_principal() == dst_ty.trait_principal() + || dst_ty.trait_principal().is_none() + { + // If we're casting to the same dyn trait, we can reuse the old vtable + src_vtable.cast_to(vtable_ty) + } else if let Some(vptr_entry_idx) = self.tcx.supertrait_vtable_slot(( + rustc_internal::internal(self.tcx, metadata_src_type), + rustc_internal::internal(self.tcx, metadata_dst_type), + )) { + // Otherwise, if the principals differ and our vtable contains a pointer to the supertrait vtable, + // we can load that + src_vtable + .dereference() + .member(vptr_entry_idx.to_string(), &self.symbol_table) + .cast_to(vtable_ty) + } else { + // Fallback to original vtable + src_vtable.cast_to(vtable_ty) + }; dynamic_fat_ptr(fat_ptr_type, dst_data, dst_vtable, &self.symbol_table) } (_, TyKind::RigidTy(RigidTy::Dynamic(..))) => { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index 84d2c52afba4..9dd28a05f00a 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -10,20 +10,24 @@ use rustc_abi::{ }; use rustc_ast::ast::Mutability; use rustc_index::IndexVec; -use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::FmtPrinter; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ - self, AdtDef, Const, CoroutineArgs, CoroutineArgsExt, FloatTy, Instance, IntTy, PolyFnSig, Ty, - TyCtxt, TyKind, UintTy, VariantDef, VtblEntry, + self, AdtDef, Const, CoroutineArgs, CoroutineArgsExt, FloatTy, Instance, IntTy, PolyFnSig, + TraitRef, Ty, TyCtxt, TyKind, UintTy, VariantDef, VtblEntry, }; +use rustc_middle::ty::{ExistentialTraitRef, GenericArgsRef}; use rustc_middle::ty::{List, TypeFoldable}; use rustc_smir::rustc_internal; use rustc_span::def_id::DefId; use stable_mir::abi::{ArgAbi, FnAbi, PassMode}; use stable_mir::mir::Body; use stable_mir::mir::mono::Instance as InstanceStable; +use stable_mir::ty::{ + Binder, DynKind, ExistentialPredicate, ExistentialProjection, Region, RegionKind, RigidTy, + Ty as StableTy, +}; use tracing::{debug, trace, warn}; /// Map the unit type to an empty struct @@ -254,8 +258,7 @@ impl<'tcx> GotocCtx<'tcx> { .is_sized(*self.tcx.at(rustc_span::DUMMY_SP), ty::TypingEnv::fully_monomorphized()) } - /// Generates the type for a single field for a dynamic vtable. - /// In particular, these fields are function pointers. + /// Generates the type for a supertrait vtable pointer field for a vtable. fn trait_method_vtable_field_type( &mut self, instance: Instance<'tcx>, @@ -274,6 +277,44 @@ impl<'tcx> GotocCtx<'tcx> { DatatypeComponent::field(vtable_field_name, fn_ptr) } + pub fn trait_ref_to_dyn_trait( + &self, + trait_ref: TraitRef<'tcx>, + projections: Vec>, + ) -> StableTy { + let existential_trait_ref = + rustc_internal::stable(ExistentialTraitRef::erase_self_ty(self.tcx, trait_ref)); + let binder = Binder::dummy(ExistentialPredicate::Trait(existential_trait_ref)); + + let mut predictates = vec![binder]; + predictates.extend( + projections.into_iter().map(|proj| proj.map_bound(ExistentialPredicate::Projection)), + ); + let rigid = + RigidTy::Dynamic(predictates, Region { kind: RegionKind::ReErased }, DynKind::Dyn); + + stable_mir::ty::Ty::from_rigid_kind(rigid) + } + + /// Generates the type for a single field for a dynamic vtable. + /// In particular, these fields are function pointers. + fn trait_vptr_vtable_field_type( + &mut self, + idx: usize, + trait_ref: TraitRef<'tcx>, + projections: Vec>, + ) -> DatatypeComponent { + let dyn_trait = self.trait_ref_to_dyn_trait(trait_ref, projections); + let vtable_ty = + self.codegen_trait_vtable_type(rustc_internal::internal(self.tcx, dyn_trait)); + let vtable_ptr = vtable_ty.to_pointer(); + + // vtable field name, i.e., 3_vol (idx_method) + let vtable_field_name = self.vtable_field_name(idx); + + DatatypeComponent::field(vtable_field_name, vtable_ptr) + } + /// Generates a vtable that looks like this: /// struct io::error::vtable { /// void *drop_in_place; @@ -383,9 +424,11 @@ impl<'tcx> GotocCtx<'tcx> { VtblEntry::Method(instance) => { Some(self.trait_method_vtable_field_type(instance, idx)) } - // TODO: trait upcasting - // https://github.com/model-checking/kani/issues/358 - VtblEntry::TraitVPtr(..) => None, + VtblEntry::TraitVPtr(trait_ref) => Some(self.trait_vptr_vtable_field_type( + idx, + trait_ref, + binder.projection_bounds().map(rustc_internal::stable).collect(), + )), VtblEntry::MetadataDropInPlace | VtblEntry::MetadataSize | VtblEntry::MetadataAlign diff --git a/tests/kani/DynTrait/upcast.rs b/tests/kani/DynTrait/upcast.rs new file mode 100644 index 000000000000..68618d2cdc9a --- /dev/null +++ b/tests/kani/DynTrait/upcast.rs @@ -0,0 +1,29 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that upcasting to a supertrait works correctly. + +trait SubTrait: SuperTrait2 + SuperTrait1 {} + +impl SubTrait for T {} + +trait SuperTrait1 { + fn trigger(&self, _old: &()) {} +} + +trait SuperTrait2 { + #[allow(unused)] + fn func(&self) {} +} + +#[derive(Clone, Copy, Default)] +struct Struct; + +impl SuperTrait1 for Struct {} +impl SuperTrait2 for Struct {} + +#[kani::proof] +fn main() { + let val: &dyn SubTrait = &Struct; + (val as &dyn SuperTrait1).trigger(&()); +} From 2879207097a875f22768aec1e972c9b87b01b1b6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 23 Apr 2025 05:20:57 +0000 Subject: [PATCH 117/161] Automatic toolchain upgrade to nightly-2025-04-23 (#4040) Update Rust toolchain from nightly-2025-04-22 to nightly-2025-04-23 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 8135d86a6f81..712c3519aebc 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-04-22" +channel = "nightly-2025-04-23" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 5a8febbee746a9a9f2224bc7753f3011ad929da1 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Thu, 24 Apr 2025 04:52:22 -0400 Subject: [PATCH 118/161] Analyze unsafe code reachability (#4037) Continuation of #3546. From @celinval in #3546: > Add call graph analysis to scanner in order to find the distance between functions in a crate and unsafe functions. > > For that, we build the crate call graph and collect the unsafe functions. After that, do reverse BFS traversal from the unsafe functions and store the distance to other functions. The result is stored in a new csv file. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Celina G. Val --- .../tool-scanner/scanner-test.expected | 18 ++- .../tool-scanner/scanner-test.sh | 6 + tests/script-based-pre/tool-scanner/test.rs | 9 ++ tools/scanner/src/analysis.rs | 83 ++++++++++--- tools/scanner/src/call_graph.rs | 110 ++++++++++++++++++ tools/scanner/src/lib.rs | 9 +- 6 files changed, 215 insertions(+), 20 deletions(-) create mode 100644 tools/scanner/src/call_graph.rs diff --git a/tests/script-based-pre/tool-scanner/scanner-test.expected b/tests/script-based-pre/tool-scanner/scanner-test.expected index f55a883434ee..92751988b06d 100644 --- a/tests/script-based-pre/tool-scanner/scanner-test.expected +++ b/tests/script-based-pre/tool-scanner/scanner-test.expected @@ -1,6 +1,18 @@ 5 test_scan_fn_loops.csv -19 test_scan_functions.csv +20 test_scan_functions.csv 5 test_scan_input_tys.csv -16 test_scan_overall.csv +17 test_scan_overall.csv 3 test_scan_recursion.csv -5 test_scan_unsafe_ops.csv +9 test_scan_unsafe_distance.csv +6 test_scan_unsafe_ops.csv + +Unsafe Distance Results +call_external;0 +next_id;0 +external_function;0 +raw_to_ref;0 +with_for_loop;1 +check_outer_coercion;1 +with_iterator;2 +current_id;0 +generic;0 diff --git a/tests/script-based-pre/tool-scanner/scanner-test.sh b/tests/script-based-pre/tool-scanner/scanner-test.sh index 2cd5a33a3f8e..468bb61dcf0d 100755 --- a/tests/script-based-pre/tool-scanner/scanner-test.sh +++ b/tests/script-based-pre/tool-scanner/scanner-test.sh @@ -16,5 +16,11 @@ pushd $OUT_DIR cargo run -p scanner test.rs --crate-type lib wc -l *csv +# How to intepret these results: +# - If the function is "truly safe," i.e., there's no unsafe in its call graph, it will not show up in the output at all. +# - Otherwise, the count should match the rules described in scanner::call_graph::OverallStats::unsafe_distance. +echo "Unsafe Distance Results" +cat test_scan_unsafe_distance.csv + popd rm -rf ${OUT_DIR} diff --git a/tests/script-based-pre/tool-scanner/test.rs b/tests/script-based-pre/tool-scanner/test.rs index f6a141f2a708..be45a653d55e 100644 --- a/tests/script-based-pre/tool-scanner/test.rs +++ b/tests/script-based-pre/tool-scanner/test.rs @@ -102,3 +102,12 @@ pub fn start_recursion() { pub fn not_recursive() { let _ = ok(); } + +extern "C" { + fn external_function(); +} + +/// Ensure scanner finds unsafe calls to external functions. +pub fn call_external() { + unsafe { external_function() }; +} diff --git a/tools/scanner/src/analysis.rs b/tools/scanner/src/analysis.rs index 7c8b8d7468da..95b529a530ca 100644 --- a/tools/scanner/src/analysis.rs +++ b/tools/scanner/src/analysis.rs @@ -1,7 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -//! Provide different static analysis to be performed in the crate under compilation +//! Provide passes that perform intra-function analysis on the crate under compilation use crate::info; use csv::WriterBuilder; @@ -11,9 +11,10 @@ use serde::{Serialize, Serializer, ser::SerializeStruct}; use stable_mir::mir::mono::Instance; use stable_mir::mir::visit::{Location, PlaceContext, PlaceRef}; use stable_mir::mir::{ - BasicBlock, Body, MirVisitor, Mutability, ProjectionElem, Safety, Terminator, TerminatorKind, + BasicBlock, Body, CastKind, MirVisitor, Mutability, NonDivergingIntrinsic, ProjectionElem, + Rvalue, Safety, Statement, StatementKind, Terminator, TerminatorKind, }; -use stable_mir::ty::{AdtDef, AdtKind, FnDef, GenericArgs, MirConst, RigidTy, Ty, TyKind}; +use stable_mir::ty::{Abi, AdtDef, AdtKind, FnDef, GenericArgs, MirConst, RigidTy, Ty, TyKind}; use stable_mir::visitor::{Visitable, Visitor}; use stable_mir::{CrateDef, CrateItem}; use std::collections::{HashMap, HashSet}; @@ -23,7 +24,7 @@ use std::path::{Path, PathBuf}; #[derive(Clone, Debug)] pub struct OverallStats { /// The key and value of each counter. - counters: Vec<(&'static str, usize)>, + pub counters: Vec<(&'static str, usize)>, /// TODO: Group stats per function. fn_stats: HashMap, } @@ -49,6 +50,12 @@ impl FnStats { } } +impl Default for OverallStats { + fn default() -> Self { + Self::new() + } +} + impl OverallStats { pub fn new() -> OverallStats { let all_items = stable_mir::all_local_items(); @@ -232,24 +239,24 @@ impl OverallStats { macro_rules! fn_props { ($(#[$attr:meta])* - struct $name:ident { + $vis:vis struct $name:ident { $( $(#[$prop_attr:meta])* $prop:ident, )+ }) => { #[derive(Debug)] - struct $name { + $vis struct $name { fn_name: String, $($(#[$prop_attr])* $prop: usize,)+ } impl $name { - const fn num_props() -> usize { + $vis const fn num_props() -> usize { [$(stringify!($prop),)+].len() } - fn new(fn_name: String) -> Self { + $vis fn new(fn_name: String) -> Self { Self { fn_name, $($prop: 0,)+} } } @@ -369,7 +376,7 @@ impl Visitor for TypeVisitor<'_> { } } -fn dump_csv(mut out_path: PathBuf, data: &[T]) { +pub(crate) fn dump_csv(mut out_path: PathBuf, data: &[T]) { out_path.set_extension("csv"); info(format!("Write file: {out_path:?}")); let mut writer = WriterBuilder::new().delimiter(b';').from_path(&out_path).unwrap(); @@ -379,17 +386,23 @@ fn dump_csv(mut out_path: PathBuf, data: &[T]) { } fn_props! { - struct FnUnsafeOperations { + pub struct FnUnsafeOperations { inline_assembly, /// Dereference a raw pointer. /// This is also counted when we access a static variable since it gets translated to a raw pointer. unsafe_dereference, - /// Call an unsafe function or method. + /// Call an unsafe function or method including C-FFI. unsafe_call, /// Access or modify a mutable static variable. unsafe_static_access, /// Access fields of unions. unsafe_union_access, + /// Invoke external functions (this is a subset of `unsafe_call`. + extern_call, + /// Transmute operations. + transmute, + /// Cast raw pointer to reference. + unsafe_cast, } } @@ -419,9 +432,19 @@ impl MirVisitor for BodyVisitor<'_> { fn visit_terminator(&mut self, term: &Terminator, location: Location) { match &term.kind { TerminatorKind::Call { func, .. } => { - let fn_sig = func.ty(self.body.locals()).unwrap().kind().fn_sig().unwrap(); - if fn_sig.value.safety == Safety::Unsafe { + let TyKind::RigidTy(RigidTy::FnDef(fn_def, _)) = + func.ty(self.body.locals()).unwrap().kind() + else { + return self.super_terminator(term, location); + }; + let fn_sig = fn_def.fn_sig().skip_binder(); + if fn_sig.safety == Safety::Unsafe { self.props.unsafe_call += 1; + if !matches!(fn_sig.abi, Abi::Rust | Abi::RustCold | Abi::RustCall) + && !fn_def.has_body() + { + self.props.extern_call += 1; + } } } TerminatorKind::InlineAsm { .. } => self.props.inline_assembly += 1, @@ -430,6 +453,34 @@ impl MirVisitor for BodyVisitor<'_> { self.super_terminator(term, location) } + fn visit_rvalue(&mut self, rvalue: &Rvalue, location: Location) { + if let Rvalue::Cast(cast_kind, operand, ty) = rvalue { + match cast_kind { + CastKind::Transmute => { + self.props.transmute += 1; + } + _ => { + let operand_ty = operand.ty(self.body.locals()).unwrap(); + if ty.kind().is_ref() && operand_ty.kind().is_raw_ptr() { + self.props.unsafe_cast += 1; + } + } + } + }; + self.super_rvalue(rvalue, location); + } + + fn visit_statement(&mut self, stmt: &Statement, location: Location) { + if matches!( + &stmt.kind, + StatementKind::Intrinsic(NonDivergingIntrinsic::CopyNonOverlapping(_)) + ) { + // Treat this as invoking the copy intrinsic. + self.props.unsafe_call += 1; + } + self.super_statement(stmt, location) + } + fn visit_projection_elem( &mut self, place: PlaceRef, @@ -674,9 +725,9 @@ impl Recursion { } } -struct FnCallVisitor<'a> { - body: &'a Body, - fns: Vec, +pub struct FnCallVisitor<'a> { + pub body: &'a Body, + pub fns: Vec, } impl MirVisitor for FnCallVisitor<'_> { diff --git a/tools/scanner/src/call_graph.rs b/tools/scanner/src/call_graph.rs new file mode 100644 index 000000000000..a8fa3baa3b4f --- /dev/null +++ b/tools/scanner/src/call_graph.rs @@ -0,0 +1,110 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Provide passes that perform inter-function analysis on the crate under compilation. +//! +//! This module also includes a `CallGraph` structure to help the analysis. +//! For now, we build the CallGraph as part of the pass, but as we add more analysis, +//! the call-graph could be reused by different analysis. + +use crate::analysis::{FnCallVisitor, FnUnsafeOperations, OverallStats}; +use stable_mir::mir::{MirVisitor, Safety}; +use stable_mir::ty::{FnDef, RigidTy, Ty, TyKind}; +use stable_mir::{CrateDef, CrateDefType}; +use std::collections::hash_map::Entry; +use std::collections::{HashMap, VecDeque}; +use std::hash::{Hash, Hasher}; +use std::path::PathBuf; + +impl OverallStats { + /// Iterate over all functions defined in this crate and log any unsafe operation. + /// Distance indicates how many degrees of separation the function has to unsafe code, if any? + /// - `None` if this function is indeed safe. + /// - 0 if this function contains unsafe code (including invoking unsafe fns). + /// - 1 if this function calls a safe abstraction. + /// - 2+ if this function calls other functions that call safe abstractions. + pub fn unsafe_distance(&mut self, filename: PathBuf) { + let all_items = stable_mir::all_local_items(); + let mut queue = + all_items.into_iter().filter_map(|item| Node::try_new(item.ty())).collect::>(); + // Build call graph + let mut call_graph = CallGraph::default(); + while let Some(node) = queue.pop() { + if let Entry::Vacant(e) = call_graph.nodes.entry(node.def) { + e.insert(node); + let Some(body) = node.def.body() else { + continue; + }; + let mut visitor = FnCallVisitor { body: &body, fns: vec![] }; + visitor.visit_body(&body); + queue.extend(visitor.fns.iter().map(|def| Node::try_new(def.ty()).unwrap())); + for callee in &visitor.fns { + call_graph.rev_edges.entry(*callee).or_default().push(node.def) + } + call_graph.edges.insert(node.def, visitor.fns); + } + } + + // Calculate the distance between unsafe functions and functions with unsafe operation. + let mut queue = call_graph + .nodes + .values() + .filter_map(|node| node.has_unsafe.then_some((node.def, 0))) + .collect::>(); + let mut visited: HashMap = HashMap::from_iter(queue.iter().cloned()); + while let Some(current) = queue.pop_front() { + for caller in call_graph.rev_edges.entry(current.0).or_default() { + if !visited.contains_key(caller) { + let distance = current.1 + 1; + visited.insert(*caller, distance); + queue.push_back((*caller, distance)) + } + } + } + let krate = stable_mir::local_crate(); + let transitive_unsafe = visited + .into_iter() + .filter_map(|(def, distance)| (def.krate() == krate).then_some((def.name(), distance))) + .collect::>(); + self.counters.push(("transitive_unsafe", transitive_unsafe.len())); + crate::analysis::dump_csv(filename, &transitive_unsafe); + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +struct Node { + def: FnDef, + is_unsafe: bool, + has_unsafe: bool, +} + +impl Node { + fn try_new(ty: Ty) -> Option { + let kind = ty.kind(); + let TyKind::RigidTy(RigidTy::FnDef(def, _)) = kind else { + return None; + }; + let has_unsafe = if let Some(body) = def.body() { + let unsafe_ops = FnUnsafeOperations::new(def.name()).collect(&body); + unsafe_ops.has_unsafe() + } else { + true + }; + let fn_sig = kind.fn_sig().unwrap(); + let is_unsafe = fn_sig.skip_binder().safety == Safety::Unsafe; + Some(Node { def, is_unsafe, has_unsafe }) + } +} + +impl Hash for Node { + fn hash(&self, state: &mut H) { + self.def.hash(state) + } +} + +#[derive(Default, Debug)] +struct CallGraph { + nodes: HashMap, + edges: HashMap>, + rev_edges: HashMap>, +} diff --git a/tools/scanner/src/lib.rs b/tools/scanner/src/lib.rs index fe2f30acb435..effcceb27dec 100644 --- a/tools/scanner/src/lib.rs +++ b/tools/scanner/src/lib.rs @@ -10,7 +10,8 @@ #![feature(rustc_private)] -mod analysis; +pub mod analysis; +pub mod call_graph; extern crate rustc_driver; extern crate rustc_interface; @@ -65,6 +66,8 @@ pub enum Analysis { FnLoops, /// Collect information about recursion via direct calls. Recursion, + /// Collect information about transitive usage of unsafe. + UnsafeDistance, } fn info(msg: String) { @@ -75,6 +78,9 @@ fn info(msg: String) { /// This function invoke the required analyses in the given order. fn analyze_crate(tcx: TyCtxt, analyses: &[Analysis]) -> ControlFlow<()> { + if stable_mir::local_crate().name == "build_script_build" { + return ControlFlow::Continue(()); + } let object_file = tcx.output_filenames(()).path(OutputType::Object); let base_path = object_file.as_path().to_path_buf(); // Use name for now to make it more friendly. Change to base_path.file_stem() to avoid conflict. @@ -96,6 +102,7 @@ fn analyze_crate(tcx: TyCtxt, analyses: &[Analysis]) -> ControlFlow<()> { Analysis::UnsafeOps => crate_stats.unsafe_operations(out_path), Analysis::FnLoops => crate_stats.loops(out_path), Analysis::Recursion => crate_stats.recursion(out_path), + Analysis::UnsafeDistance => crate_stats.unsafe_distance(out_path), } } crate_stats.store_csv(base_path, &file_stem); From d6853436382d876e574fb8a3fdef5b798a6e7d0d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 24 Apr 2025 12:12:55 +0200 Subject: [PATCH 119/161] Automatic toolchain upgrade to nightly-2025-04-24 (#4042) Update Rust toolchain from nightly-2025-04-23 to nightly-2025-04-24 without any other source changes. --------- Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> Co-authored-by: Michael Tautschnig --- .../codegen_cprover_gotoc/codegen/contract.rs | 2 +- .../codegen/intrinsic.rs | 13 ++- .../src/codegen_cprover_gotoc/codegen/typ.rs | 10 +- kani-compiler/src/kani_middle/attributes.rs | 23 +++-- .../src/kani_middle/codegen_units.rs | 8 +- kani-compiler/src/kani_middle/intrinsics.rs | 8 +- .../kani_middle/points_to/points_to_graph.rs | 8 +- kani-compiler/src/kani_middle/reachability.rs | 16 +-- kani-compiler/src/kani_middle/resolve.rs | 2 +- .../kani_middle/resolve/type_resolution.rs | 11 +-- .../src/kani_middle/stubbing/annotations.rs | 24 ++--- .../delayed_ub/initial_target_visitor.rs | 8 +- .../check_uninit/ptr_uninit/uninit_visitor.rs | 67 +++++++------ .../kani_middle/transform/loop_contracts.rs | 22 ++--- .../kani_middle/transform/rustc_intrinsics.rs | 48 +++++---- .../src/kani_middle/transform/stubs.rs | 97 +++++++++---------- kani-driver/src/args/mod.rs | 45 ++++----- kani-driver/src/args_toml.rs | 26 ++--- kani-driver/src/assess/scan.rs | 10 +- kani-driver/src/autoharness/mod.rs | 11 +-- kani-driver/src/cbmc_property_renderer.rs | 2 +- .../src/concrete_playback/test_generator.rs | 2 +- kani-driver/src/harness_runner.rs | 2 +- kani-driver/src/list/output.rs | 4 +- rust-toolchain.toml | 2 +- tools/scanner/src/analysis.rs | 4 +- tools/scanner/src/lib.rs | 2 +- 27 files changed, 232 insertions(+), 245 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs index d3d4481bf258..b0583056b88b 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs @@ -160,7 +160,7 @@ impl GotocCtx<'_> { }; for ty in &modifies_tys { - assert!(ty.kind().is_any_ptr(), "Expected pointer, but found {}", ty); + assert!(ty.kind().is_any_ptr(), "Expected pointer, but found {ty}"); } let assigns: Vec<_> = modifies_tys diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 579e2b083f05..5f56c69a9041 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -1253,10 +1253,10 @@ impl GotocCtx<'_> { let size = sized_size.plus(unsized_size); // Packed types ignore the alignment of their fields. - if let TyKind::RigidTy(RigidTy::Adt(def, _)) = ty.kind() { - if rustc_internal::internal(self.tcx, def).repr().packed() { - unsized_align = sized_align.clone(); - } + if let TyKind::RigidTy(RigidTy::Adt(def, _)) = ty.kind() + && rustc_internal::internal(self.tcx, def).repr().packed() + { + unsized_align = sized_align.clone(); } // The alignment should be the maximum of the alignments for the @@ -1882,12 +1882,11 @@ impl GotocCtx<'_> { assert!(ty.kind().is_float()); let TyKind::RigidTy(integral_ty) = res_ty.kind() else { panic!( - "Expected intrinsic `{}` type to be `RigidTy`, but found: `{:?}`", - intrinsic, res_ty + "Expected intrinsic `{intrinsic}` type to be `RigidTy`, but found: `{res_ty:?}`" ); }; let TyKind::RigidTy(RigidTy::Float(float_type)) = ty.kind() else { - panic!("Expected intrinsic `{}` type to be `Float`, but found: `{:?}`", intrinsic, ty); + panic!("Expected intrinsic `{intrinsic}` type to be `Float`, but found: `{ty:?}`"); }; let mm = self.symbol_table.machine_model(); let in_range = utils::codegen_in_range_expr(&expr, float_type, integral_ty, mm); diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index 9dd28a05f00a..cd8feb9e7b6c 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -1548,11 +1548,11 @@ impl<'tcx> GotocCtx<'tcx> { // components as parameters with a special naming convention // so that we can "retuple" them in the function prelude. // See: compiler/rustc_codegen_llvm/src/gotoc/mod.rs:codegen_function_prelude - if let Some(spread) = body.spread_arg() { - if lc >= spread { - let (name, _) = self.codegen_spread_arg_name(&lc); - ident = name; - } + if let Some(spread) = body.spread_arg() + && lc >= spread + { + let (name, _) = self.codegen_spread_arg_name(&lc); + ident = name; } Some( self.codegen_ty_stable(ty) diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index 53f8ea408618..4dfb8cd64bee 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -510,14 +510,14 @@ impl<'tcx> KaniAttributes<'tcx> { /// Check that the function specified in the `proof_for_contract` attribute /// is reachable and emit an error if it isn't pub fn check_proof_for_contract(&self, reachable_functions: &HashSet) { - if let Some((symbol, function, span)) = self.interpret_for_contract_attribute() { - if !reachable_functions.contains(&function) { - let err_msg = format!( - "The function specified in the `proof_for_contract` attribute, `{symbol}`, was not found.\ + if let Some((symbol, function, span)) = self.interpret_for_contract_attribute() + && !reachable_functions.contains(&function) + { + let err_msg = format!( + "The function specified in the `proof_for_contract` attribute, `{symbol}`, was not found.\ \nMake sure the function is reachable from the harness." - ); - self.tcx.dcx().span_err(span, err_msg); - } + ); + self.tcx.dcx().span_err(span, err_msg); } } @@ -609,11 +609,11 @@ impl<'tcx> KaniAttributes<'tcx> { if seen.contains(&name) { dcx.struct_span_warn( span, - format!("Multiple occurrences of `stub_verified({})`.", name), + format!("Multiple occurrences of `stub_verified({name})`."), ) .with_span_note( self.tcx.def_span(def_id), - format!("Use a single `stub_verified({})` annotation.", name), + format!("Use a single `stub_verified({name})` annotation."), ) .emit(); } else { @@ -623,8 +623,7 @@ impl<'tcx> KaniAttributes<'tcx> { dcx.struct_span_err( span, format!( - "Target function in `stub_verified({})` has no contract.", - name, + "Target function in `stub_verified({name})` has no contract.", ), ) .with_span_note( @@ -1112,7 +1111,7 @@ pub(crate) fn fn_marker(def: T) -> Option { let marker = def.tool_attrs(&fn_marker).pop()?; let attribute = syn_attr_stable(&marker); let meta_name = attribute.meta.require_name_value().unwrap_or_else(|_| { - panic!("Expected name value attribute for `kanitool::fn_marker`, but found: `{:?}`", marker) + panic!("Expected name value attribute for `kanitool::fn_marker`, but found: `{marker:?}`") }); let Expr::Lit(ExprLit { lit: Lit::Str(lit_str), .. }) = &meta_name.value else { panic!( diff --git a/kani-compiler/src/kani_middle/codegen_units.rs b/kani-compiler/src/kani_middle/codegen_units.rs index 3faa1a03d77c..336c45ab55d6 100644 --- a/kani-compiler/src/kani_middle/codegen_units.rs +++ b/kani-compiler/src/kani_middle/codegen_units.rs @@ -234,10 +234,10 @@ fn extract_contracts( ) -> BTreeSet { let def = harness.def; let mut result = BTreeSet::new(); - if let HarnessKind::ProofForContract { target_fn } = &metadata.attributes.kind { - if let Ok(check_def) = expect_resolve_fn(tcx, def, target_fn, "proof_for_contract") { - result.insert(ContractUsage::Check(check_def.def_id().to_index())); - } + if let HarnessKind::ProofForContract { target_fn } = &metadata.attributes.kind + && let Ok(check_def) = expect_resolve_fn(tcx, def, target_fn, "proof_for_contract") + { + result.insert(ContractUsage::Check(check_def.def_id().to_index())); } for stub in &metadata.attributes.verified_stubs { diff --git a/kani-compiler/src/kani_middle/intrinsics.rs b/kani-compiler/src/kani_middle/intrinsics.rs index a53abef90e88..7013feaf34d9 100644 --- a/kani-compiler/src/kani_middle/intrinsics.rs +++ b/kani-compiler/src/kani_middle/intrinsics.rs @@ -108,10 +108,10 @@ fn resolve_rust_intrinsic<'tcx>( tcx: TyCtxt<'tcx>, func_ty: Ty<'tcx>, ) -> Option<(IntrinsicDef, GenericArgsRef<'tcx>)> { - if let ty::FnDef(def_id, args) = *func_ty.kind() { - if let Some(symbol) = tcx.intrinsic(def_id) { - return Some((symbol, args)); - } + if let ty::FnDef(def_id, args) = *func_ty.kind() + && let Some(symbol) = tcx.intrinsic(def_id) + { + return Some((symbol, args)); } None } diff --git a/kani-compiler/src/kani_middle/points_to/points_to_graph.rs b/kani-compiler/src/kani_middle/points_to/points_to_graph.rs index 6e473a84eecc..f27c53fcc3b0 100644 --- a/kani-compiler/src/kani_middle/points_to/points_to_graph.rs +++ b/kani-compiler/src/kani_middle/points_to/points_to_graph.rs @@ -187,7 +187,7 @@ impl<'tcx> PointsToGraph<'tcx> { /// Dump the graph into a file using the graphviz format for later visualization. pub fn dump(&self, file_path: &str) { let mut nodes: Vec = - self.nodes.keys().map(|from| format!("\t\"{:?}\"", from)).collect(); + self.nodes.keys().map(|from| format!("\t\"{from:?}\"")).collect(); nodes.sort(); let nodes_str = nodes.join("\n"); @@ -195,9 +195,9 @@ impl<'tcx> PointsToGraph<'tcx> { .nodes .iter() .flat_map(|(from, to)| { - let from = format!("\"{:?}\"", from); + let from = format!("\"{from:?}\""); to.successors.iter().map(move |to| { - let to = format!("\"{:?}\"", to); + let to = format!("\"{to:?}\""); format!("\t{} -> {}", from.clone(), to) }) }) @@ -205,7 +205,7 @@ impl<'tcx> PointsToGraph<'tcx> { edges.sort(); let edges_str = edges.join("\n"); - std::fs::write(file_path, format!("digraph {{\n{}\n{}\n}}", nodes_str, edges_str)).unwrap(); + std::fs::write(file_path, format!("digraph {{\n{nodes_str}\n{edges_str}\n}}")).unwrap(); } /// Find a transitive closure of the graph starting from a set of given locations; this also diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 534af39c94e4..5ccd611cbeab 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -121,14 +121,14 @@ where // Filter regular items. for item in crate_items { // Only collect monomorphic items. - if let Ok(instance) = Instance::try_from(item) { - if predicate(tcx, instance) { - let body = transformer.body(tcx, instance); - let mut collector = - MonoItemsFnCollector { tcx, body: &body, collected: FxHashSet::default() }; - collector.visit_body(&body); - roots.extend(collector.collected.into_iter()); - } + if let Ok(instance) = Instance::try_from(item) + && predicate(tcx, instance) + { + let body = transformer.body(tcx, instance); + let mut collector = + MonoItemsFnCollector { tcx, body: &body, collected: FxHashSet::default() }; + collector.visit_body(&body); + roots.extend(collector.collected.into_iter()); } } roots.into_iter().map(|root| root.item).collect() diff --git a/kani-compiler/src/kani_middle/resolve.rs b/kani-compiler/src/kani_middle/resolve.rs index 6dada3ce6aa2..30d951e22e65 100644 --- a/kani-compiler/src/kani_middle/resolve.rs +++ b/kani-compiler/src/kani_middle/resolve.rs @@ -716,5 +716,5 @@ fn is_item_name_with_generic_args( ) -> bool { let item_path = tcx.def_path_str(item); let all_but_base_type = item_path.find("::").map_or("", |idx| &item_path[idx..]); - all_but_base_type == format!("{}::{}", generic_args, name) + all_but_base_type == format!("{generic_args}::{name}") } diff --git a/kani-compiler/src/kani_middle/resolve/type_resolution.rs b/kani-compiler/src/kani_middle/resolve/type_resolution.rs index ea6ac94ee317..50183634637d 100644 --- a/kani-compiler/src/kani_middle/resolve/type_resolution.rs +++ b/kani-compiler/src/kani_middle/resolve/type_resolution.rs @@ -191,12 +191,11 @@ pub(super) fn is_type_primitive(typ: &syn::Type) -> bool { /// Parse the length of the array. /// We currently only support a constant length. fn parse_len(len: &Expr) -> Result { - if let Expr::Lit(ExprLit { lit: Lit::Int(lit), .. }) = len { - if matches!(lit.suffix(), "" | "usize") - && let Ok(val) = usize::from_str(lit.base10_digits()) - { - return Ok(val); - } + if let Expr::Lit(ExprLit { lit: Lit::Int(lit), .. }) = len + && matches!(lit.suffix(), "" | "usize") + && let Ok(val) = usize::from_str(lit.base10_digits()) + { + return Ok(val); } Err(format!("Expected a `usize` constant, but found `{}`", len.to_token_stream())) } diff --git a/kani-compiler/src/kani_middle/stubbing/annotations.rs b/kani-compiler/src/kani_middle/stubbing/annotations.rs index 9fc7be491d85..e75cd04aa53f 100644 --- a/kani-compiler/src/kani_middle/stubbing/annotations.rs +++ b/kani-compiler/src/kani_middle/stubbing/annotations.rs @@ -45,18 +45,18 @@ pub fn update_stub_mapping( ) { if let Some((orig_id, stub_id)) = stub_def_ids(tcx, harness, stub) { let other_opt = stub_pairs.insert(orig_id, stub_id); - if let Some(other) = other_opt { - if other != stub_id { - tcx.dcx().span_err( - tcx.def_span(harness), - format!( - "duplicate stub mapping: {} mapped to {} and {}", - tcx.def_path_str(orig_id), - tcx.def_path_str(stub_id), - tcx.def_path_str(other) - ), - ); - } + if let Some(other) = other_opt + && other != stub_id + { + tcx.dcx().span_err( + tcx.def_span(harness), + format!( + "duplicate stub mapping: {} mapped to {} and {}", + tcx.def_path_str(orig_id), + tcx.def_path_str(stub_id), + tcx.def_path_str(other) + ), + ); } } } diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/initial_target_visitor.rs b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/initial_target_visitor.rs index dfd16594146f..7fa3315577cb 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/initial_target_visitor.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/delayed_ub/initial_target_visitor.rs @@ -79,10 +79,10 @@ impl MirVisitor for InitialTargetVisitor { RigidTy::RawPtr(ty, _) | RigidTy::Ref(_, ty, _) => Some(ty), _ => None, }; - if let (Some(from_ty), Some(to_ty)) = (from_ty, to_ty) { - if !tys_layout_equal_to_size(from_ty, to_ty) { - self.push_operand(operand); - } + if let (Some(from_ty), Some(to_ty)) = (from_ty, to_ty) + && !tys_layout_equal_to_size(from_ty, to_ty) + { + self.push_operand(operand); } } _ => {} diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs b/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs index c286b36c5bd5..1bd4566ab96a 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/ptr_uninit/uninit_visitor.rs @@ -136,15 +136,14 @@ impl MirVisitor for CheckUninitVisitor { for projection_elem in place_without_deref.projection.iter() { // If the projection is Deref and the current type is raw pointer, check // if it points to initialized memory. - if *projection_elem == ProjectionElem::Deref { - if let TyKind::RigidTy(RigidTy::RawPtr(..)) = + if *projection_elem == ProjectionElem::Deref + && let TyKind::RigidTy(RigidTy::RawPtr(..)) = place_to_add_projections.ty(&self.locals).unwrap().kind() - { - self.push_target(MemoryInitOp::Check { - operand: Operand::Copy(place_to_add_projections.clone()), - }); - }; - } + { + self.push_target(MemoryInitOp::Check { + operand: Operand::Copy(place_to_add_projections.clone()), + }); + }; place_to_add_projections.projection.push(projection_elem.clone()); } if place_without_deref.ty(&self.locals).unwrap().kind().is_raw_ptr() { @@ -156,14 +155,14 @@ impl MirVisitor for CheckUninitVisitor { } } // Check whether Rvalue creates a new initialized pointer previously not captured inside shadow memory. - if place.ty(&self.locals).unwrap().kind().is_raw_ptr() { - if let Rvalue::AddressOf(..) = rvalue { - self.push_target(MemoryInitOp::Set { - operand: Operand::Copy(place.clone()), - value: true, - position: InsertPosition::After, - }); - } + if place.ty(&self.locals).unwrap().kind().is_raw_ptr() + && let Rvalue::AddressOf(..) = rvalue + { + self.push_target(MemoryInitOp::Set { + operand: Operand::Copy(place.clone()), + value: true, + position: InsertPosition::After, + }); } // TODO: add support for ADTs which could have unions as subfields. Currently, @@ -496,20 +495,20 @@ impl MirVisitor for CheckUninitVisitor { } fn visit_operand(&mut self, operand: &Operand, location: Location) { - if let Operand::Constant(constant) = operand { - if let ConstantKind::Allocated(allocation) = constant.const_.kind() { - for (_, prov) in &allocation.provenance.ptrs { - if let GlobalAlloc::Static(_) = GlobalAlloc::from(prov.0) { - if constant.ty().kind().is_raw_ptr() { - // If a static is a raw pointer, need to mark it as initialized. - self.push_target(MemoryInitOp::Set { - operand: Operand::Constant(constant.clone()), - value: true, - position: InsertPosition::Before, - }); - } - }; - } + if let Operand::Constant(constant) = operand + && let ConstantKind::Allocated(allocation) = constant.const_.kind() + { + for (_, prov) in &allocation.provenance.ptrs { + if let GlobalAlloc::Static(_) = GlobalAlloc::from(prov.0) + && constant.ty().kind().is_raw_ptr() + { + // If a static is a raw pointer, need to mark it as initialized. + self.push_target(MemoryInitOp::Set { + operand: Operand::Constant(constant.clone()), + value: true, + position: InsertPosition::Before, + }); + }; } } self.super_operand(operand, location); @@ -519,12 +518,12 @@ impl MirVisitor for CheckUninitVisitor { if let Rvalue::Cast(cast_kind, operand, ty) = rvalue { match cast_kind { CastKind::PointerCoercion(PointerCoercion::Unsize) => { - if let TyKind::RigidTy(RigidTy::RawPtr(pointee_ty, _)) = ty.kind() { - if pointee_ty.kind().is_trait() { - self.push_target(MemoryInitOp::Unsupported { + if let TyKind::RigidTy(RigidTy::RawPtr(pointee_ty, _)) = ty.kind() + && pointee_ty.kind().is_trait() + { + self.push_target(MemoryInitOp::Unsupported { reason: "Kani does not support reasoning about memory initialization of unsized pointers.".to_string(), }); - } } } CastKind::Transmute => { diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index ca01b7071abf..45484f41091d 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -237,18 +237,16 @@ impl LoopContractPass { let mut contain_loop_contracts = false; // Redirect loop latches to the new latches. - if let TerminatorKind::Goto { target: terminator_target } = &terminator.kind { - if self.new_loop_latches.contains_key(terminator_target) { - new_body.replace_terminator( - &SourceInstruction::Terminator { bb: bb_idx }, - Terminator { - kind: TerminatorKind::Goto { - target: self.new_loop_latches[terminator_target], - }, - span: terminator.span, - }, - ); - } + if let TerminatorKind::Goto { target: terminator_target } = &terminator.kind + && self.new_loop_latches.contains_key(terminator_target) + { + new_body.replace_terminator( + &SourceInstruction::Terminator { bb: bb_idx }, + Terminator { + kind: TerminatorKind::Goto { target: self.new_loop_latches[terminator_target] }, + span: terminator.span, + }, + ); } // Transform loop heads with loop contracts. diff --git a/kani-compiler/src/kani_middle/transform/rustc_intrinsics.rs b/kani-compiler/src/kani_middle/transform/rustc_intrinsics.rs index 43f8e5b7013f..b14f21a9b24b 100644 --- a/kani-compiler/src/kani_middle/transform/rustc_intrinsics.rs +++ b/kani-compiler/src/kani_middle/transform/rustc_intrinsics.rs @@ -156,34 +156,30 @@ impl MutMirVisitor for ReplaceIntrinsicCallVisitor<'_> { /// Note that we only need to replace function calls since intrinsics must always be called /// directly. I.e., no need to handle function pointers. fn visit_terminator(&mut self, term: &mut Terminator) { - if let TerminatorKind::Call { func, .. } = &mut term.kind { - if let TyKind::RigidTy(RigidTy::FnDef(def, args)) = + if let TerminatorKind::Call { func, .. } = &mut term.kind + && let TyKind::RigidTy(RigidTy::FnDef(def, args)) = func.ty(&self.locals).unwrap().kind() - { - if def.is_intrinsic() { - let instance = Instance::resolve(def, &args).unwrap(); - let intrinsic = Intrinsic::from_instance(&instance); - debug!(?intrinsic, "handle_terminator"); - let model = match intrinsic { - Intrinsic::SizeOfVal => self.models[&KaniModel::SizeOfVal], - Intrinsic::MinAlignOfVal => self.models[&KaniModel::AlignOfVal], - Intrinsic::PtrOffsetFrom => self.models[&KaniModel::PtrOffsetFrom], - Intrinsic::PtrOffsetFromUnsigned => { - self.models[&KaniModel::PtrOffsetFromUnsigned] - } - // The rest is handled in codegen. - _ => { - return self.super_terminator(term); - } - }; - let new_instance = Instance::resolve(model, &args).unwrap(); - let literal = MirConst::try_new_zero_sized(new_instance.ty()).unwrap(); - let span = term.span; - let new_func = ConstOperand { span, user_ty: None, const_: literal }; - *func = Operand::Constant(new_func); - self.changed = true; + && def.is_intrinsic() + { + let instance = Instance::resolve(def, &args).unwrap(); + let intrinsic = Intrinsic::from_instance(&instance); + debug!(?intrinsic, "handle_terminator"); + let model = match intrinsic { + Intrinsic::SizeOfVal => self.models[&KaniModel::SizeOfVal], + Intrinsic::MinAlignOfVal => self.models[&KaniModel::AlignOfVal], + Intrinsic::PtrOffsetFrom => self.models[&KaniModel::PtrOffsetFrom], + Intrinsic::PtrOffsetFromUnsigned => self.models[&KaniModel::PtrOffsetFromUnsigned], + // The rest is handled in codegen. + _ => { + return self.super_terminator(term); } - } + }; + let new_instance = Instance::resolve(model, &args).unwrap(); + let literal = MirConst::try_new_zero_sized(new_instance.ty()).unwrap(); + let span = term.span; + let new_func = ConstOperand { span, user_ty: None, const_: literal }; + *func = Operand::Constant(new_func); + self.changed = true; } self.super_terminator(term); } diff --git a/kani-compiler/src/kani_middle/transform/stubs.rs b/kani-compiler/src/kani_middle/transform/stubs.rs index 3d72cff0d56d..1c96be291594 100644 --- a/kani-compiler/src/kani_middle/transform/stubs.rs +++ b/kani-compiler/src/kani_middle/transform/stubs.rs @@ -46,14 +46,13 @@ impl TransformPass for FnStubPass { fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { trace!(function=?instance.name(), "transform"); let ty = instance.ty(); - if let TyKind::RigidTy(RigidTy::FnDef(fn_def, args)) = ty.kind() { - if let Some(replace) = self.stubs.get(&fn_def) { - let new_instance = Instance::resolve(*replace, &args).unwrap(); - debug!(from=?instance.name(), to=?new_instance.name(), "FnStubPass::transform"); - if let Some(body) = FnStubValidator::validate(tcx, (fn_def, *replace), new_instance) - { - return (true, body); - } + if let TyKind::RigidTy(RigidTy::FnDef(fn_def, args)) = ty.kind() + && let Some(replace) = self.stubs.get(&fn_def) + { + let new_instance = Instance::resolve(*replace, &args).unwrap(); + debug!(from=?instance.name(), to=?new_instance.name(), "FnStubPass::transform"); + if let Some(body) = FnStubValidator::validate(tcx, (fn_def, *replace), new_instance) { + return (true, body); } } (false, body) @@ -152,28 +151,28 @@ impl FnStubValidator<'_, '_> { impl MirVisitor for FnStubValidator<'_, '_> { fn visit_operand(&mut self, op: &Operand, loc: Location) { let op_ty = op.ty(self.locals).unwrap(); - if let TyKind::RigidTy(RigidTy::FnDef(def, args)) = op_ty.kind() { - if Instance::resolve(def, &args).is_err() { - self.is_valid = false; - let callee = def.name(); - let receiver_ty = args.0[0].expect_ty(); - let sep = callee.rfind("::").unwrap(); - let trait_ = &callee[..sep]; - self.tcx.dcx().span_err( - rustc_internal::internal(self.tcx, loc.span()), - format!( - "`{}` doesn't implement \ + if let TyKind::RigidTy(RigidTy::FnDef(def, args)) = op_ty.kind() + && Instance::resolve(def, &args).is_err() + { + self.is_valid = false; + let callee = def.name(); + let receiver_ty = args.0[0].expect_ty(); + let sep = callee.rfind("::").unwrap(); + let trait_ = &callee[..sep]; + self.tcx.dcx().span_err( + rustc_internal::internal(self.tcx, loc.span()), + format!( + "`{}` doesn't implement \ `{}`. The function `{}` \ cannot be stubbed by `{}` due to \ generic bounds not being met. Callee: {}", - receiver_ty, - trait_, - self.stub.0.name(), - self.stub.1.name(), - callee, - ), - ); - } + receiver_ty, + trait_, + self.stub.0.name(), + self.stub.1.name(), + callee, + ), + ); } } } @@ -187,36 +186,34 @@ struct ExternFnStubVisitor<'a> { impl MutMirVisitor for ExternFnStubVisitor<'_> { fn visit_terminator(&mut self, term: &mut Terminator) { // Replace direct calls - if let TerminatorKind::Call { func, .. } = &mut term.kind { - if let TyKind::RigidTy(RigidTy::FnDef(def, args)) = + if let TerminatorKind::Call { func, .. } = &mut term.kind + && let TyKind::RigidTy(RigidTy::FnDef(def, args)) = func.ty(&self.locals).unwrap().kind() - { - if let Some(new_def) = self.stubs.get(&def) { - let instance = Instance::resolve(*new_def, &args).unwrap(); - let literal = MirConst::try_new_zero_sized(instance.ty()).unwrap(); - let span = term.span; - let new_func = ConstOperand { span, user_ty: None, const_: literal }; - *func = Operand::Constant(new_func); - self.changed = true; - } - } + && let Some(new_def) = self.stubs.get(&def) + { + let instance = Instance::resolve(*new_def, &args).unwrap(); + let literal = MirConst::try_new_zero_sized(instance.ty()).unwrap(); + let span = term.span; + let new_func = ConstOperand { span, user_ty: None, const_: literal }; + *func = Operand::Constant(new_func); + self.changed = true; } self.super_terminator(term); } fn visit_operand(&mut self, operand: &mut Operand) { let func_ty = operand.ty(&self.locals).unwrap(); - if let TyKind::RigidTy(RigidTy::FnDef(orig_def, args)) = func_ty.kind() { - if let Some(new_def) = self.stubs.get(&orig_def) { - let Operand::Constant(ConstOperand { span, .. }) = operand else { - unreachable!(); - }; - let instance = Instance::resolve_for_fn_ptr(*new_def, &args).unwrap(); - let literal = MirConst::try_new_zero_sized(instance.ty()).unwrap(); - let new_func = ConstOperand { span: *span, user_ty: None, const_: literal }; - *operand = Operand::Constant(new_func); - self.changed = true; - } + if let TyKind::RigidTy(RigidTy::FnDef(orig_def, args)) = func_ty.kind() + && let Some(new_def) = self.stubs.get(&orig_def) + { + let Operand::Constant(ConstOperand { span, .. }) = operand else { + unreachable!(); + }; + let instance = Instance::resolve_for_fn_ptr(*new_def, &args).unwrap(); + let literal = MirConst::try_new_zero_sized(instance.ty()).unwrap(); + let new_func = ConstOperand { span: *span, user_ty: None, const_: literal }; + *operand = Operand::Constant(new_func); + self.changed = true; } } } diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index c2e4acc96310..5b19236a4334 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -485,7 +485,7 @@ fn check_no_cargo_opt(is_set: bool, name: &str) -> Result<(), Error> { if is_set { Err(Error::raw( ErrorKind::UnknownArgument, - format!("argument `{}` cannot be used with standalone Kani.", name), + format!("argument `{name}` cannot be used with standalone Kani."), )) } else { Ok(()) @@ -516,16 +516,16 @@ impl ValidateArgs for StandaloneArgs { check_no_cargo_opt(!self.verify_opts.cargo.exclude.is_empty(), "--exclude")?; check_no_cargo_opt(self.verify_opts.cargo.workspace, "--workspace")?; check_no_cargo_opt(self.verify_opts.cargo.manifest_path.is_some(), "--manifest-path")?; - if let Some(input) = &self.input { - if !input.is_file() { - return Err(Error::raw( - ErrorKind::InvalidValue, - format!( - "Invalid argument: Input invalid. `{}` is not a regular file.", - input.display() - ), - )); - } + if let Some(input) = &self.input + && !input.is_file() + { + return Err(Error::raw( + ErrorKind::InvalidValue, + format!( + "Invalid argument: Input invalid. `{}` is not a regular file.", + input.display() + ), + )); } Ok(()) } @@ -634,16 +634,17 @@ impl ValidateArgs for VerificationArgs { "Conflicting options: --jobs requires `--output-format=terse`", )); } - if let Some(out_dir) = &self.target_dir { - if out_dir.exists() && !out_dir.is_dir() { - return Err(Error::raw( - ErrorKind::InvalidValue, - format!( - "Invalid argument: `--target-dir` argument `{}` is not a directory", - out_dir.display() - ), - )); - } + if let Some(out_dir) = &self.target_dir + && out_dir.exists() + && !out_dir.is_dir() + { + return Err(Error::raw( + ErrorKind::InvalidValue, + format!( + "Invalid argument: `--target-dir` argument `{}` is not a directory", + out_dir.display() + ), + )); } self.common_args.check_unstable( @@ -1016,7 +1017,7 @@ mod tests { args: &str, unstable: UnstableFeature, ) -> Result { - let args = format!("kani -Z {} file.rs {args}", unstable); + let args = format!("kani -Z {unstable} file.rs {args}"); let parse_res = StandaloneArgs::try_parse_from(args.split(' '))?; parse_res.verify_opts.validate()?; Ok(parse_res) diff --git a/kani-driver/src/args_toml.rs b/kani-driver/src/args_toml.rs index 2fff01c4f5f9..cff00e6368f6 100644 --- a/kani-driver/src/args_toml.rs +++ b/kani-driver/src/args_toml.rs @@ -105,21 +105,21 @@ fn toml_to_args(tomldata: &str) -> Result<(Vec, Vec)> { for table in tables { if let Some(table) = get_table(&config, table) { - if let Some(entry) = table.get("flags") { - if let Some(val) = entry.as_table() { - map.extend(val.iter().map(|(x, y)| (x.to_owned(), y.to_owned()))); - } + if let Some(entry) = table.get("flags") + && let Some(val) = entry.as_table() + { + map.extend(val.iter().map(|(x, y)| (x.to_owned(), y.to_owned()))); } - if let Some(entry) = table.get("unstable") { - if let Some(val) = entry.as_table() { - args.append( - &mut val - .iter() - .filter_map(|(k, v)| unstable_entry(k, v).transpose()) - .collect::>>()?, - ); - } + if let Some(entry) = table.get("unstable") + && let Some(val) = entry.as_table() + { + args.append( + &mut val + .iter() + .filter_map(|(k, v)| unstable_entry(k, v).transpose()) + .collect::>>()?, + ); } } } diff --git a/kani-driver/src/assess/scan.rs b/kani-driver/src/assess/scan.rs index 63a778b294b4..4af4838094f1 100644 --- a/kani-driver/src/assess/scan.rs +++ b/kani-driver/src/assess/scan.rs @@ -72,11 +72,11 @@ pub(crate) fn assess_scan_main(session: KaniSession, args: &ScanArgs) -> Result< for workspace in &project_metadata { let workspace_root = workspace.workspace_root.as_std_path(); for package in workspace.workspace_packages() { - if let Some(filter) = &package_filter { - if !filter.contains(&package.name) { - println!("Skipping filtered-out package {}", package.name); - continue; - } + if let Some(filter) = &package_filter + && !filter.contains(&package.name) + { + println!("Skipping filtered-out package {}", package.name); + continue; } // This is a hack. Some repos contains workspaces with "examples" (not actually cargo examples, but // full packages as workspace members) that are named after other crates. diff --git a/kani-driver/src/autoharness/mod.rs b/kani-driver/src/autoharness/mod.rs index 552c38a8128e..9c79601c2e89 100644 --- a/kani-driver/src/autoharness/mod.rs +++ b/kani-driver/src/autoharness/mod.rs @@ -102,7 +102,7 @@ fn print_autoharness_metadata(metadata: Vec) { format!( "{reason} {}", args.iter() - .map(|(name, typ)| format!("{}: {}", name, typ)) + .map(|(name, typ)| format!("{name}: {typ}")) .collect::>() .join(", ") ), @@ -169,10 +169,9 @@ impl KaniSession { // This catches cases like include="foo::bar" exclude="bar" or include="foo" exclude="foo" if include_pattern.contains(exclude_pattern) { warning(&format!( - "Include pattern '{}' contains exclude pattern '{}'. \ + "Include pattern '{include_pattern}' contains exclude pattern '{exclude_pattern}'. \ This combination will never match any functions since all functions matching \ - the include pattern will also match the exclude pattern, and the exclude pattern takes precedence.", - include_pattern, exclude_pattern + the include pattern will also match the exclude pattern, and the exclude pattern takes precedence." )); } } @@ -180,10 +179,10 @@ impl KaniSession { let mut args = vec![]; for pattern in included { - args.push(format!("--autoharness-include-pattern {}", pattern)); + args.push(format!("--autoharness-include-pattern {pattern}")); } for pattern in excluded { - args.push(format!("--autoharness-exclude-pattern {}", pattern)); + args.push(format!("--autoharness-exclude-pattern {pattern}")); } self.autoharness_compiler_flags = Some(args); } diff --git a/kani-driver/src/cbmc_property_renderer.rs b/kani-driver/src/cbmc_property_renderer.rs index 3245e9d0ccb2..f3e6791cde8f 100644 --- a/kani-driver/src/cbmc_property_renderer.rs +++ b/kani-driver/src/cbmc_property_renderer.rs @@ -441,7 +441,7 @@ pub fn format_coverage( let verification_output = format_result(&non_coverage_checks, status, should_panic, failed_properties, show_checks); let cov_results_intro = "Source-based code coverage results:"; - let result = format!("{}\n{}\n\n{}", verification_output, cov_results_intro, cov_results); + let result = format!("{verification_output}\n{cov_results_intro}\n\n{cov_results}"); result } diff --git a/kani-driver/src/concrete_playback/test_generator.rs b/kani-driver/src/concrete_playback/test_generator.rs index 2cf741c23f82..9393eec1999c 100644 --- a/kani-driver/src/concrete_playback/test_generator.rs +++ b/kani-driver/src/concrete_playback/test_generator.rs @@ -124,7 +124,7 @@ impl KaniSession { line_range: Some((unit_test_start_line, unit_test_end_line)), }]; self.run_rustfmt(&file_line_ranges, Some(&path)) - .unwrap_or_else(|err| println!("WARNING: {}", err)); + .unwrap_or_else(|err| println!("WARNING: {err}")); } Ok(()) diff --git a/kani-driver/src/harness_runner.rs b/kani-driver/src/harness_runner.rs index a35eee8513d4..6399d30499e2 100644 --- a/kani-driver/src/harness_runner.rs +++ b/kani-driver/src/harness_runner.rs @@ -180,7 +180,7 @@ impl KaniSession { file_output = format!("Thread {thread_index}:\n{file_output}"); } - if let Err(e) = writeln!(file, "{}", file_output) { + if let Err(e) = writeln!(file, "{file_output}") { eprintln!( "Failed to write to file {}: {}", file_name.into_os_string().into_string().unwrap(), diff --git a/kani-driver/src/list/output.rs b/kani-driver/src/list/output.rs index e91f71a6c25f..1db9af6c2c19 100644 --- a/kani-driver/src/list/output.rs +++ b/kani-driver/src/list/output.rs @@ -82,8 +82,8 @@ fn construct_output( /// Print results to the terminal. fn pretty(list_metadata: BTreeSet) -> Result<()> { let (contract_output, standard_output) = construct_output(list_metadata, pretty_constructor)?; - println!("{}", contract_output); - println!("{}", standard_output); + println!("{contract_output}"); + println!("{standard_output}"); Ok(()) } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 712c3519aebc..17bb020afb0e 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-04-23" +channel = "nightly-2025-04-24" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tools/scanner/src/analysis.rs b/tools/scanner/src/analysis.rs index 95b529a530ca..238698537646 100644 --- a/tools/scanner/src/analysis.rs +++ b/tools/scanner/src/analysis.rs @@ -68,12 +68,12 @@ impl OverallStats { } pub fn store_csv(&self, base_path: PathBuf, file_stem: &str) { - let filename = format!("{}_overall", file_stem); + let filename = format!("{file_stem}_overall"); let mut out_path = base_path.parent().map_or(PathBuf::default(), Path::to_path_buf); out_path.set_file_name(filename); dump_csv(out_path, &self.counters); - let filename = format!("{}_functions", file_stem); + let filename = format!("{file_stem}_functions"); let mut out_path = base_path.parent().map_or(PathBuf::default(), Path::to_path_buf); out_path.set_file_name(filename); dump_csv(out_path, &self.fn_stats.values().collect::>()); diff --git a/tools/scanner/src/lib.rs b/tools/scanner/src/lib.rs index effcceb27dec..2ddc3dac674f 100644 --- a/tools/scanner/src/lib.rs +++ b/tools/scanner/src/lib.rs @@ -72,7 +72,7 @@ pub enum Analysis { fn info(msg: String) { if VERBOSE.load(Ordering::Relaxed) { - eprintln!("[INFO] {}", msg); + eprintln!("[INFO] {msg}"); } } From 60aa1dd2ecb6161fab81320e596e9aae04c35dd4 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Thu, 24 Apr 2025 21:34:30 +0200 Subject: [PATCH 120/161] Scanner: log crate-level visibility of functions (#4041) This will help determine whether a failed property appears in a public or private function. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../tool-scanner/scanner-test.expected | 2 +- tools/scanner/src/analysis.rs | 27 +++++++++++++++++++ tools/scanner/src/lib.rs | 3 +++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/tests/script-based-pre/tool-scanner/scanner-test.expected b/tests/script-based-pre/tool-scanner/scanner-test.expected index 92751988b06d..67bd14fc0e46 100644 --- a/tests/script-based-pre/tool-scanner/scanner-test.expected +++ b/tests/script-based-pre/tool-scanner/scanner-test.expected @@ -1,7 +1,7 @@ 5 test_scan_fn_loops.csv 20 test_scan_functions.csv 5 test_scan_input_tys.csv -17 test_scan_overall.csv +19 test_scan_overall.csv 3 test_scan_recursion.csv 9 test_scan_unsafe_distance.csv 6 test_scan_unsafe_ops.csv diff --git a/tools/scanner/src/analysis.rs b/tools/scanner/src/analysis.rs index 238698537646..08bcc7c04b04 100644 --- a/tools/scanner/src/analysis.rs +++ b/tools/scanner/src/analysis.rs @@ -7,6 +7,8 @@ use crate::info; use csv::WriterBuilder; use graph_cycles::Cycles; use petgraph::graph::Graph; +use rustc_middle::ty::TyCtxt; +use rustc_smir::rustc_internal; use serde::{Serialize, Serializer, ser::SerializeStruct}; use stable_mir::mir::mono::Instance; use stable_mir::mir::visit::{Location, PlaceContext, PlaceRef}; @@ -36,6 +38,7 @@ struct FnStats { has_unsafe_ops: Option, has_unsupported_input: Option, has_loop_or_iterator: Option, + is_public: Option, } impl FnStats { @@ -46,6 +49,7 @@ impl FnStats { has_unsafe_ops: None, has_unsupported_input: None, has_loop_or_iterator: None, + is_public: None, } } } @@ -235,6 +239,29 @@ impl OverallStats { .collect::>(), ); } + + /// Iterate over all functions defined in this crate and log public vs private + pub fn public_fns(&mut self, tcx: &TyCtxt) { + let all_items = stable_mir::all_local_items(); + let (public_fns, private_fns) = all_items + .into_iter() + .filter_map(|item| { + let kind = item.ty().kind(); + if !kind.is_fn() { + return None; + }; + let int_def_id = rustc_internal::internal(*tcx, item.def_id()); + let is_public = tcx.visibility(int_def_id).is_public() + || tcx.visibility(int_def_id).is_visible_locally(); + self.fn_stats.get_mut(&item).unwrap().is_public = Some(is_public); + Some((item, is_public)) + }) + .partition::, _>(|(_, is_public)| *is_public); + self.counters.extend_from_slice(&[ + ("public_fns", public_fns.len()), + ("private_fns", private_fns.len()), + ]); + } } macro_rules! fn_props { diff --git a/tools/scanner/src/lib.rs b/tools/scanner/src/lib.rs index 2ddc3dac674f..fba8bf08e520 100644 --- a/tools/scanner/src/lib.rs +++ b/tools/scanner/src/lib.rs @@ -68,6 +68,8 @@ pub enum Analysis { Recursion, /// Collect information about transitive usage of unsafe. UnsafeDistance, + /// Collect information about function visibility. + PublicFns, } fn info(msg: String) { @@ -103,6 +105,7 @@ fn analyze_crate(tcx: TyCtxt, analyses: &[Analysis]) -> ControlFlow<()> { Analysis::FnLoops => crate_stats.loops(out_path), Analysis::Recursion => crate_stats.recursion(out_path), Analysis::UnsafeDistance => crate_stats.unsafe_distance(out_path), + Analysis::PublicFns => crate_stats.public_fns(&tcx), } } crate_stats.store_csv(base_path, &file_stem); From 72097650b368ac3cb17859e0d0abdfdace2046b9 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Thu, 24 Apr 2025 15:52:43 -0400 Subject: [PATCH 121/161] Autoharness: exit code 1 upon harness failure (#4043) Exit Kani with a failure code if any automatic harnesses fail. This replicates the behavior of just running manual harnesses. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- kani-driver/src/autoharness/mod.rs | 7 +++++-- kani-driver/src/harness_runner.rs | 10 ++++++---- .../cargo_autoharness_contracts/config.yml | 1 + .../cargo_autoharness_contracts/contracts.sh | 4 ---- .../cargo_autoharness_dependencies/dependencies.sh | 6 +----- .../cargo_autoharness_harnesses_fail/config.yml | 1 + .../cargo_autoharness_harnesses_fail/harnesses_fail.sh | 6 +----- .../cargo_autoharness_termination_timeout/config.yml | 1 + .../cargo_autoharness_termination_unwind/config.yml | 1 + .../cargo_autoharness_type_invariant/config.yml | 1 + .../cargo_autoharness_type_invariant/type-invariant.sh | 4 ---- 11 files changed, 18 insertions(+), 24 deletions(-) diff --git a/kani-driver/src/autoharness/mod.rs b/kani-driver/src/autoharness/mod.rs index 9c79601c2e89..f4bcd221ec08 100644 --- a/kani-driver/src/autoharness/mod.rs +++ b/kani-driver/src/autoharness/mod.rs @@ -200,7 +200,10 @@ impl KaniSession { } /// Prints the results from running the `autoharness` subcommand. - pub fn print_autoharness_summary(&self, mut automatic: Vec<&HarnessResult<'_>>) -> Result<()> { + pub fn print_autoharness_summary( + &self, + mut automatic: Vec<&HarnessResult<'_>>, + ) -> Result { automatic.sort_by(|a, b| a.harness.pretty_name.cmp(&b.harness.pretty_name)); let (successes, failures): (Vec<_>, Vec<_>) = automatic.into_iter().partition(|r| r.result.status == VerificationStatus::Success); @@ -258,6 +261,6 @@ impl KaniSession { println!("No functions were eligible for automatic verification."); } - Ok(()) + Ok(failing) } } diff --git a/kani-driver/src/harness_runner.rs b/kani-driver/src/harness_runner.rs index 6399d30499e2..db47c7c8d40a 100644 --- a/kani-driver/src/harness_runner.rs +++ b/kani-driver/src/harness_runner.rs @@ -298,11 +298,13 @@ impl KaniSession { self.show_coverage_summary()?; } - if self.autoharness_compiler_flags.is_some() { - self.print_autoharness_summary(automatic)?; - } + let autoharness_failing = if self.autoharness_compiler_flags.is_some() { + self.print_autoharness_summary(automatic)? + } else { + 0 + }; - if failing > 0 && self.autoharness_compiler_flags.is_none() { + if failing + autoharness_failing > 0 { // Failure exit code without additional error message drop(self); std::process::exit(1); diff --git a/tests/script-based-pre/cargo_autoharness_contracts/config.yml b/tests/script-based-pre/cargo_autoharness_contracts/config.yml index 38bf8ee6c38c..8b42bc3cbd18 100644 --- a/tests/script-based-pre/cargo_autoharness_contracts/config.yml +++ b/tests/script-based-pre/cargo_autoharness_contracts/config.yml @@ -2,3 +2,4 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT script: contracts.sh expected: contracts.expected +exit_code: 1 \ No newline at end of file diff --git a/tests/script-based-pre/cargo_autoharness_contracts/contracts.sh b/tests/script-based-pre/cargo_autoharness_contracts/contracts.sh index 34e177be9134..db4a99f8be09 100755 --- a/tests/script-based-pre/cargo_autoharness_contracts/contracts.sh +++ b/tests/script-based-pre/cargo_autoharness_contracts/contracts.sh @@ -3,7 +3,3 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT cargo kani autoharness -Z autoharness -# We expect verification to fail, so the above command will produce an exit status of 1 -# However, we don't want the test to fail because of that exit status; we only want it to fail if the expected file doesn't match -# So, exit with a status code of 0 explicitly. -exit 0; diff --git a/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.sh b/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.sh index 34e177be9134..0e0f76e871ea 100755 --- a/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.sh +++ b/tests/script-based-pre/cargo_autoharness_dependencies/dependencies.sh @@ -2,8 +2,4 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -cargo kani autoharness -Z autoharness -# We expect verification to fail, so the above command will produce an exit status of 1 -# However, we don't want the test to fail because of that exit status; we only want it to fail if the expected file doesn't match -# So, exit with a status code of 0 explicitly. -exit 0; +cargo kani autoharness -Z autoharness \ No newline at end of file diff --git a/tests/script-based-pre/cargo_autoharness_harnesses_fail/config.yml b/tests/script-based-pre/cargo_autoharness_harnesses_fail/config.yml index f5b3400d03bf..a165769407e4 100644 --- a/tests/script-based-pre/cargo_autoharness_harnesses_fail/config.yml +++ b/tests/script-based-pre/cargo_autoharness_harnesses_fail/config.yml @@ -2,3 +2,4 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT script: harnesses_fail.sh expected: harnesses_fail.expected +exit_code: 1 \ No newline at end of file diff --git a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.sh b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.sh index 34e177be9134..0e0f76e871ea 100755 --- a/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.sh +++ b/tests/script-based-pre/cargo_autoharness_harnesses_fail/harnesses_fail.sh @@ -2,8 +2,4 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT -cargo kani autoharness -Z autoharness -# We expect verification to fail, so the above command will produce an exit status of 1 -# However, we don't want the test to fail because of that exit status; we only want it to fail if the expected file doesn't match -# So, exit with a status code of 0 explicitly. -exit 0; +cargo kani autoharness -Z autoharness \ No newline at end of file diff --git a/tests/script-based-pre/cargo_autoharness_termination_timeout/config.yml b/tests/script-based-pre/cargo_autoharness_termination_timeout/config.yml index b6fab7133fd6..6098e8a6cdf3 100644 --- a/tests/script-based-pre/cargo_autoharness_termination_timeout/config.yml +++ b/tests/script-based-pre/cargo_autoharness_termination_timeout/config.yml @@ -2,3 +2,4 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT script: termination_timeout.sh expected: termination_timeout.expected +exit_code: 1 \ No newline at end of file diff --git a/tests/script-based-pre/cargo_autoharness_termination_unwind/config.yml b/tests/script-based-pre/cargo_autoharness_termination_unwind/config.yml index 148335b104cc..9a1a7065a88f 100644 --- a/tests/script-based-pre/cargo_autoharness_termination_unwind/config.yml +++ b/tests/script-based-pre/cargo_autoharness_termination_unwind/config.yml @@ -2,3 +2,4 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT script: termination_unwind.sh expected: termination_unwind.expected +exit_code: 1 \ No newline at end of file diff --git a/tests/script-based-pre/cargo_autoharness_type_invariant/config.yml b/tests/script-based-pre/cargo_autoharness_type_invariant/config.yml index 5762f901d15e..7366397e2ad0 100644 --- a/tests/script-based-pre/cargo_autoharness_type_invariant/config.yml +++ b/tests/script-based-pre/cargo_autoharness_type_invariant/config.yml @@ -2,3 +2,4 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT script: type-invariant.sh expected: type-invariant.expected +exit_code: 1 \ No newline at end of file diff --git a/tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.sh b/tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.sh index 34e177be9134..db4a99f8be09 100755 --- a/tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.sh +++ b/tests/script-based-pre/cargo_autoharness_type_invariant/type-invariant.sh @@ -3,7 +3,3 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT cargo kani autoharness -Z autoharness -# We expect verification to fail, so the above command will produce an exit status of 1 -# However, we don't want the test to fail because of that exit status; we only want it to fail if the expected file doesn't match -# So, exit with a status code of 0 explicitly. -exit 0; From 570469fc946807a7fa7677ee55fc36db5d662df9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 09:42:58 +0200 Subject: [PATCH 122/161] Automatic cargo update to 2025-04-28 (#4047) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 53 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c5b22f2f13fb..1c3da78ba475 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "version_check", "zerocopy", @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.19" +version = "1.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" dependencies = [ "shlex", ] @@ -639,9 +639,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -794,9 +794,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ec30f7142be6fe14e1b021f50b85db8df2d4324ea6e91ec3e5dcde092021d0" +checksum = "5a064218214dc6a10fbae5ec5fa888d80c45d611aba169222fc272072bf7aef6" dependencies = [ "jiff-static", "log", @@ -807,9 +807,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "526b834d727fd59d37b076b0c3236d9adde1b1729a4361e20b2026f738cc1dbe" +checksum = "199b7932d97e325aff3a7030e141eafe7f2c6268e1d1b24859b753a627f45254" dependencies = [ "proc-macro2", "quote", @@ -1335,9 +1335,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88" +checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" dependencies = [ "cc", ] @@ -1634,9 +1634,9 @@ checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "stacker" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601f9201feb9b09c00266478bf459952b9ef9a6b94edb2f21eba14ab681a60a9" +checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" dependencies = [ "cc", "cfg-if", @@ -1695,9 +1695,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -1838,9 +1838,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "900f6c86a685850b1bc9f6223b20125115ee3f31e01207d81655bbcc0aea9231" dependencies = [ "serde", "serde_spanned", @@ -1850,26 +1850,33 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "10558ed0bd2a1562e630926a2d1f0b98c827da99fabd3fe20920a59642504485" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", + "toml_write", "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28391a4201ba7eb1984cfeb6862c0b3ea2cfe23332298967c749dddc0d6cd976" + [[package]] name = "tracing" version = "0.1.41" @@ -2208,9 +2215,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" dependencies = [ "memchr", ] From 3e5ee7f94035bc01dc08847c7ed39eafe29db949 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 18:03:06 +0000 Subject: [PATCH 123/161] Bump tests/perf/s2n-quic from `f42521d` to `29e5e15` (#4048) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `f42521d` to `29e5e15`.
Commits
  • 29e5e15 fix(s2n-quic-transport) Ensures packet number about to be skipped is not the ...
  • 0c3ac8b chore: Remove wireshark ppa repository (#2622)
  • 8f7696e fix(s2n-quic-transport): handle pto overflows in connection migration (#2620)
  • 34f5e4b fix(s2n-quic-transport): Fixes s2n-quic TLS state incongruity when s2n-tls re...
  • 9ed7d73 events: Adds event for missing packet space when processing a packet (#2615)
  • See full diff in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index f42521d87640..29e5e153a8ca 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit f42521d8764046e9b8bb10551367e4b5d8b98716 +Subproject commit 29e5e153a8ca33c98e2142c5c9b22ef9fed15c72 From f915000a17f392766defe8a6d23da2dc64a9e1d0 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 29 Apr 2025 09:34:39 +0200 Subject: [PATCH 124/161] Overflow operators can also be used with vectors (#4049) We already supported the same for plus and minus, but need to equally do so for overflow-plus, overflow-minus. Resolves: #2631 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- cprover_bindings/src/goto_program/expr.rs | 5 +++-- ...{simd_float_ops_fixme.rs => simd_float_ops.rs} | 15 ++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) rename tests/kani/SIMD/{simd_float_ops_fixme.rs => simd_float_ops.rs} (64%) diff --git a/cprover_bindings/src/goto_program/expr.rs b/cprover_bindings/src/goto_program/expr.rs index 6f140b67ec84..5d0890e99d2b 100644 --- a/cprover_bindings/src/goto_program/expr.rs +++ b/cprover_bindings/src/goto_program/expr.rs @@ -1017,11 +1017,12 @@ impl Expr { IeeeFloatEqual | IeeeFloatNotequal => lhs.typ == rhs.typ && lhs.typ.is_floating_point(), // Overflow flags OverflowMinus | OverflowResultMinus => { - (lhs.typ == rhs.typ && (lhs.typ.is_pointer() || lhs.typ.is_numeric())) + (lhs.typ == rhs.typ + && (lhs.typ.is_pointer() || lhs.typ.is_numeric() || lhs.typ.is_vector())) || (lhs.typ.is_pointer() && rhs.typ.is_integer()) } OverflowMult | OverflowPlus | OverflowResultMult | OverflowResultPlus => { - (lhs.typ == rhs.typ && lhs.typ.is_integer()) + (lhs.typ == rhs.typ && (lhs.typ.is_numeric() || lhs.typ.is_vector())) || (lhs.typ.is_pointer() && rhs.typ.is_integer()) } ROk => lhs.typ.is_pointer() && rhs.typ.is_c_size_t(), diff --git a/tests/kani/SIMD/simd_float_ops_fixme.rs b/tests/kani/SIMD/simd_float_ops.rs similarity index 64% rename from tests/kani/SIMD/simd_float_ops_fixme.rs rename to tests/kani/SIMD/simd_float_ops.rs index 6b53d95d554a..dbb6d77260b6 100644 --- a/tests/kani/SIMD/simd_float_ops_fixme.rs +++ b/tests/kani/SIMD/simd_float_ops.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Ensure we can handle SIMD defined in the standard library -//! FIXME: #![allow(non_camel_case_types)] #![feature(repr_simd, core_intrinsics, portable_simd)] use std::intrinsics::simd::simd_add; @@ -10,7 +9,7 @@ use std::simd::f32x4; #[repr(simd)] #[derive(Clone, PartialEq, kani::Arbitrary)] -pub struct f32x2(f32, f32); +pub struct f32x2([f32; 2]); impl f32x2 { fn as_array(&self) -> &[f32; 2] { @@ -20,16 +19,22 @@ impl f32x2 { #[kani::proof] fn check_sum() { - let a = f32x2(0.0, 0.0); + let a = f32x2([0.0, 0.0]); let b = kani::any::(); - let sum = unsafe { simd_add(a.clone(), b) }; - assert_eq!(sum.as_array(), a.as_array()); + kani::assume(b.as_array()[0].is_normal()); + kani::assume(b.as_array()[1].is_normal()); + let sum = unsafe { simd_add(a.clone(), b.clone()) }; + assert_eq!(sum.as_array(), b.as_array()); } #[kani::proof] fn check_sum_portable() { let a = f32x4::splat(0.0); let b = f32x4::from_array(kani::any()); + kani::assume(b.as_array()[0].is_normal()); + kani::assume(b.as_array()[1].is_normal()); + kani::assume(b.as_array()[2].is_normal()); + kani::assume(b.as_array()[3].is_normal()); // Cannot compare them directly: https://github.com/model-checking/kani/issues/2632 assert_eq!((a + b).as_array(), b.as_array()); } From cc367b34beeda1f3622c6f2daa05838c9ab0cab1 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 29 Apr 2025 05:10:09 -0500 Subject: [PATCH 125/161] Update CBMC dependency to 6.6.0 (#4050) Kani now uses CBMC's latest release 6.6.0. Resolves #4019 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. Co-authored-by: Michael Tautschnig --- kani-dependencies | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kani-dependencies b/kani-dependencies index f620835102de..b3667d086efa 100644 --- a/kani-dependencies +++ b/kani-dependencies @@ -1,5 +1,5 @@ CBMC_MAJOR="6" -CBMC_MINOR="5" -CBMC_VERSION="6.5.0" +CBMC_MINOR="6" +CBMC_VERSION="6.6.0" KISSAT_VERSION="4.0.1" From 0a3d32ba6ab933f96c209960c5b9304cff8b69e2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 05:42:31 -0700 Subject: [PATCH 126/161] Automatic cargo update to 2025-05-05 (#4053) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c3da78ba475..0ecd069dbf21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.20" +version = "1.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" +checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" dependencies = [ "shlex", ] @@ -693,9 +693,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "foldhash", ] @@ -753,7 +753,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] @@ -794,9 +794,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a064218214dc6a10fbae5ec5fa888d80c45d611aba169222fc272072bf7aef6" +checksum = "d07d8d955d798e7a4d6f9c58cd1f1916e790b42b092758a9ef6e16fef9f1b3fd" dependencies = [ "jiff-static", "log", @@ -807,9 +807,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "199b7932d97e325aff3a7030e141eafe7f2c6268e1d1b24859b753a627f45254" +checksum = "f244cfe006d98d26f859c7abd1318d85327e1882dc9cef80f62daeeb0adcf300" dependencies = [ "proc-macro2", "quote", @@ -1379,9 +1379,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ "bitflags", ] @@ -1460,9 +1460,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags", "errno", @@ -1664,7 +1664,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23de088478b31c349c9ba67816fa55d9355232d63c3afea8bf513e31f0f1d2c0" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.3", "serde", ] @@ -1719,7 +1719,7 @@ dependencies = [ "fastrand", "getrandom 0.3.2", "once_cell", - "rustix 1.0.5", + "rustix 1.0.7", "windows-sys 0.59.0", ] @@ -1838,9 +1838,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900f6c86a685850b1bc9f6223b20125115ee3f31e01207d81655bbcc0aea9231" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", @@ -1859,9 +1859,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.25" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10558ed0bd2a1562e630926a2d1f0b98c827da99fabd3fe20920a59642504485" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "serde", @@ -1873,9 +1873,9 @@ dependencies = [ [[package]] name = "toml_write" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28391a4201ba7eb1984cfeb6862c0b3ea2cfe23332298967c749dddc0d6cd976" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" [[package]] name = "tracing" @@ -2096,7 +2096,7 @@ checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" dependencies = [ "either", "env_home", - "rustix 1.0.5", + "rustix 1.0.7", "winsafe", ] @@ -2215,9 +2215,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.7" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" +checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" dependencies = [ "memchr", ] From e229b691c9904c77637620070267b3939b89737a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 13:32:29 -0400 Subject: [PATCH 127/161] Bump tests/perf/s2n-quic from `29e5e15` to `6aa9975` (#4054) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `29e5e15` to `6aa9975`.
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 29e5e153a8ca..6aa9975c999b 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 29e5e153a8ca33c98e2142c5c9b22ef9fed15c72 +Subproject commit 6aa9975c999b25b282bfafc3791393c8c2a8f25d From 08d99d387c7666fcfb10c03dddacff231a8972a8 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Wed, 7 May 2025 12:48:01 -0400 Subject: [PATCH 128/161] Contracts/Stubs for multiple inherent impls: fix checking the generic args path for equality (#4051) #3829 fixed an issue where we couldn't verify contracts or stubs for types with multiple inherent impls. However, the logic to check for path equality incorrectly assumed that there would only be one element in the path before the generic argument, when in fact the path can be arbitrarily long if there are modules involved. Change the logic to just look at the last two elements, since we can expect those will be the generic argument and the method name. Before this PR, the new test would fail with this error: ``` error: Failed to resolve checking function NegativeNumber::::unchecked_mul because the generic arguments :: are invalid. The available implementations are: num::negative::NegativeNumber::::unchecked_mul num::negative::NegativeNumber::::unchecked_mul --> /Users/cmzech/kani/tests/kani/FunctionContracts/multiple_inherent_impls.rs:57:5 | 57 | #[kani::proof_for_contract(NegativeNumber::::unchecked_mul)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the attribute macro `kani::proof_for_contract` (in Nightly builds, run with -Z macro-backtrace for more info) ``` because we'd try to compare `::negative::NegativeNumber::::unchecked_mul` to `::::unchecked_mul` and find they weren't equal. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- kani-compiler/src/kani_middle/resolve.rs | 54 ++++++++++++++++++- .../multiple_inherent_impls.rs | 34 ++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/kani-compiler/src/kani_middle/resolve.rs b/kani-compiler/src/kani_middle/resolve.rs index 30d951e22e65..9b46eb2e7d09 100644 --- a/kani-compiler/src/kani_middle/resolve.rs +++ b/kani-compiler/src/kani_middle/resolve.rs @@ -708,6 +708,8 @@ fn is_item_name(tcx: TyCtxt, item: DefId, name: &str) -> bool { last == name } +/// Use this when we don't just care about the item name matching (c.f. is_item_name), +/// but also if the generic arguments are the same, e.g. ::unchecked_add. fn is_item_name_with_generic_args( tcx: TyCtxt, item: DefId, @@ -715,6 +717,54 @@ fn is_item_name_with_generic_args( name: &str, ) -> bool { let item_path = tcx.def_path_str(item); - let all_but_base_type = item_path.find("::").map_or("", |idx| &item_path[idx..]); - all_but_base_type == format!("{generic_args}::{name}") + last_two_items_of_path_match(&item_path, generic_args, name) +} + +// This is just a helper function for is_item_name_with_generic_args. +// It's in a separate function so we can unit-test it without a mock TyCtxt or DefIds. +fn last_two_items_of_path_match(item_path: &str, generic_args: &str, name: &str) -> bool { + let parts: Vec<&str> = item_path.split("::").collect(); + + if parts.len() < 2 { + return false; + } + + let actual_last_two = + format!("{}{}{}{}", "::", parts[parts.len() - 2], "::", parts[parts.len() - 1]); + + let last_two = format!("{}{}{}", generic_args, "::", name); + + // The last two components of the item_path should be the same as ::{generic_args}::{name} + last_two == actual_last_two +} + +#[cfg(test)] +mod tests { + mod simple_last_two_items_of_path_match { + use crate::kani_middle::resolve::last_two_items_of_path_match; + + #[test] + fn length_one_item_prefix() { + let generic_args = "::"; + let name = "unchecked_add"; + let item_path = format!("NonZero{}::{}", generic_args, name); + assert!(last_two_items_of_path_match(&item_path, generic_args, name)) + } + + #[test] + fn length_three_item_prefix() { + let generic_args = "::"; + let name = "unchecked_add"; + let item_path = format!("core::num::NonZero{}::{}", generic_args, name); + assert!(last_two_items_of_path_match(&item_path, generic_args, name)) + } + + #[test] + fn wrong_generic_arg() { + let generic_args = "::"; + let name = "unchecked_add"; + let item_path = format!("core::num::NonZero{}::{}", "::", name); + assert!(!last_two_items_of_path_match(&item_path, generic_args, name)) + } + } } diff --git a/tests/kani/FunctionContracts/multiple_inherent_impls.rs b/tests/kani/FunctionContracts/multiple_inherent_impls.rs index cdd25337cd36..76ee51d99590 100644 --- a/tests/kani/FunctionContracts/multiple_inherent_impls.rs +++ b/tests/kani/FunctionContracts/multiple_inherent_impls.rs @@ -26,3 +26,37 @@ fn verify_unchecked_mul_ambiguous_path() { let x: NonZero = NonZero(-1); x.unchecked_mul(-2); } + +// Test that the resolution still works if the function in question is nested inside multiple modules, +// i.e. the absolute path to the function can be arbitrarily long. +// As long as the generic arguments and function name match, resolution should succeed. +// This mimics the actual structure of NonZero relative to its harnesses in the standard library. +pub mod num { + pub mod negative { + pub struct NegativeNumber(pub T); + + impl NegativeNumber { + #[kani::requires(self.0.checked_mul(x).is_some())] + pub fn unchecked_mul(self, x: i32) -> i32 { + self.0 * x + } + } + + impl NegativeNumber { + #[kani::requires(self.0.checked_mul(x).is_some())] + pub fn unchecked_mul(self, x: i16) -> i16 { + self.0 * x + } + } + } +} + +mod verify { + use crate::num::negative::*; + + #[kani::proof_for_contract(NegativeNumber::::unchecked_mul)] + fn verify_unchecked_mul_ambiguous_path() { + let x: NegativeNumber = NegativeNumber(-1); + x.unchecked_mul(-2); + } +} From f47b27a968f57097516bf8a369a8fb4d39b234af Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Wed, 7 May 2025 13:46:47 -0700 Subject: [PATCH 129/161] Remove bool typedef (#4058) This is a proposed alternative fix to #4052 where the new GCC version (v15) switched from C17 to C23 by default and complains about errors in https://github.com/model-checking/kani/blob/main/library/kani/kani_lib.c. The first proposed fix in #4055 pinned the version to C17. This PR instead removes the typedef that causes an error with C23. Resolves #4052 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- library/kani/kani_lib.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/library/kani/kani_lib.c b/library/kani/kani_lib.c index eca17a3abb0e..02a50708a7ff 100644 --- a/library/kani/kani_lib.c +++ b/library/kani/kani_lib.c @@ -10,8 +10,6 @@ void *memcpy(void *dst, const void *src, size_t n); void *calloc(size_t nmemb, size_t size); void *malloc(size_t size); -typedef __CPROVER_bool bool; - /// Mapping unit to `void` works for functions with no return type but not for /// variables with type unit. We treat both uniformly by declaring an empty /// struct type: `struct Unit {}` and a global variable `struct Unit VoidUnit` @@ -22,13 +20,13 @@ extern struct Unit VoidUnit; // `assert` then `assume` #define __KANI_assert(cond, msg) \ do { \ - bool __KANI_temp = (cond); \ + __CPROVER_bool __KANI_temp = (cond); \ __CPROVER_assert(__KANI_temp, msg); \ __CPROVER_assume(__KANI_temp); \ } while (0) // Check that the input is either a power of 2, or 0. Algorithm from Hackers Delight. -bool __KANI_is_nonzero_power_of_two(size_t i) { return (i != 0) && (i & (i - 1)) == 0; } +__CPROVER_bool __KANI_is_nonzero_power_of_two(size_t i) { return (i != 0) && (i & (i - 1)) == 0; } // This is a C implementation of the __rust_alloc function. // https://stdrs.dev/nightly/x86_64-unknown-linux-gnu/alloc/alloc/fn.__rust_alloc.html From a15883b25d362979e63575cc051e0fdc5a835baa Mon Sep 17 00:00:00 2001 From: thanhnguyen-aws Date: Wed, 7 May 2025 15:27:41 -0700 Subject: [PATCH 130/161] Bump Kani version 0.62.0 (#4056) Description of changes: Bumps version of Kani crates to 0.62.0. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- cprover_bindings/Cargo.toml | 2 +- kani-compiler/Cargo.toml | 2 +- kani-driver/Cargo.toml | 2 +- kani_metadata/Cargo.toml | 2 +- library/kani/Cargo.toml | 2 +- library/kani_core/Cargo.toml | 2 +- library/kani_macros/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- tools/build-kani/Cargo.toml | 2 +- 12 files changed, 50 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e4bb46e4633..b8b95786ec9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,36 @@ This file contains notable changes (e.g. breaking changes, major changes, etc.) This file was introduced starting Kani 0.23.0, so it only contains changes from version 0.23.0 onwards. +## [0.62.0] + +### What's Changed +* Disable llbc feature by default by @zhassan-aws in https://github.com/model-checking/kani/pull/3980 +* Add an option to skip codegen by @zhassan-aws in https://github.com/model-checking/kani/pull/4002 +* Add support for loop-contract historic values by @thanhnguyen-aws in https://github.com/model-checking/kani/pull/3951 +* Clarify Rust intrinsic assumption error message by @carolynzech in https://github.com/model-checking/kani/pull/4015 +* Autoharness: enable function-contracts and loop-contracts features by default by @carolynzech in https://github.com/model-checking/kani/pull/4016 +* Autoharness: Harness Generation Improvements by @carolynzech in https://github.com/model-checking/kani/pull/4017 +* Add support for Loop-loops by @thanhnguyen-aws in https://github.com/model-checking/kani/pull/4011 +* Clarify installation instructions by @zhassan-aws in https://github.com/model-checking/kani/pull/4023 +* Fix the bug of while loop invariant contains no local variables by @thanhnguyen-aws in https://github.com/model-checking/kani/pull/4022 +* List Subcommand: include crate name by @carolynzech in https://github.com/model-checking/kani/pull/4024 +* Autoharness: Update Filtering Options by @carolynzech in https://github.com/model-checking/kani/pull/4025 +* Introduce BoundedArbitrary trait and macro for bounded proofs by @sgpthomas in https://github.com/model-checking/kani/pull/4000 +* Support `trait_upcasting` by @clubby789 in https://github.com/model-checking/kani/pull/4001 +* Analyze unsafe code reachability by @carolynzech in https://github.com/model-checking/kani/pull/4037 +* Scanner: log crate-level visibility of functions by @tautschnig in https://github.com/model-checking/kani/pull/4041 +* Autoharness: exit code 1 upon harness failure by @carolynzech in https://github.com/model-checking/kani/pull/4043 +* Overflow operators can also be used with vectors by @tautschnig in https://github.com/model-checking/kani/pull/4049 +* Remove bool typedef by @zhassan-aws in https://github.com/model-checking/kani/pull/4058 +* Update CBMC dependency to 6.6.0 by @qinheping in https://github.com/model-checking/kani/pull/4050 +* Automatic toolchain upgrade to nightly-2025-04-24 by @zhassan-aws in https://github.com/model-checking/kani/pull/4042 + +## New Contributors +* @sgpthomas made their first contribution in https://github.com/model-checking/kani/pull/4000 +* @clubby789 made their first contribution in https://github.com/model-checking/kani/pull/4001 + +**Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.61.0...kani-0.62.0 + ## [0.61.0] ### What's Changed diff --git a/Cargo.lock b/Cargo.lock index 0ecd069dbf21..15732a85da9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -176,7 +176,7 @@ dependencies = [ [[package]] name = "build-kani" -version = "0.61.0" +version = "0.62.0" dependencies = [ "anyhow", "cargo_metadata", @@ -381,7 +381,7 @@ dependencies = [ [[package]] name = "cprover_bindings" -version = "0.61.0" +version = "0.62.0" dependencies = [ "lazy_static", "linear-map", @@ -824,7 +824,7 @@ checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5" [[package]] name = "kani" -version = "0.61.0" +version = "0.62.0" dependencies = [ "kani_core", "kani_macros", @@ -832,7 +832,7 @@ dependencies = [ [[package]] name = "kani-compiler" -version = "0.61.0" +version = "0.62.0" dependencies = [ "charon", "clap", @@ -868,7 +868,7 @@ dependencies = [ [[package]] name = "kani-driver" -version = "0.61.0" +version = "0.62.0" dependencies = [ "anyhow", "cargo_metadata", @@ -897,7 +897,7 @@ dependencies = [ [[package]] name = "kani-verifier" -version = "0.61.0" +version = "0.62.0" dependencies = [ "anyhow", "home", @@ -906,14 +906,14 @@ dependencies = [ [[package]] name = "kani_core" -version = "0.61.0" +version = "0.62.0" dependencies = [ "kani_macros", ] [[package]] name = "kani_macros" -version = "0.61.0" +version = "0.62.0" dependencies = [ "proc-macro-error2", "proc-macro2", @@ -923,7 +923,7 @@ dependencies = [ [[package]] name = "kani_metadata" -version = "0.61.0" +version = "0.62.0" dependencies = [ "clap", "cprover_bindings", @@ -1647,7 +1647,7 @@ dependencies = [ [[package]] name = "std" -version = "0.61.0" +version = "0.62.0" dependencies = [ "kani", ] diff --git a/Cargo.toml b/Cargo.toml index 93b0b727f030..455ee300bfef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-verifier" -version = "0.61.0" +version = "0.62.0" edition = "2021" description = "A bit-precise model checker for Rust." readme = "README.md" diff --git a/cprover_bindings/Cargo.toml b/cprover_bindings/Cargo.toml index 3ff968d46e8a..641d7862d776 100644 --- a/cprover_bindings/Cargo.toml +++ b/cprover_bindings/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "cprover_bindings" -version = "0.61.0" +version = "0.62.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index e79b167dafb2..8a77ade4b9e2 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-compiler" -version = "0.61.0" +version = "0.62.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index b0d2c3055125..a4a030335c74 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-driver" -version = "0.61.0" +version = "0.62.0" edition = "2021" description = "Build a project with Kani and run all proof harnesses" license = "MIT OR Apache-2.0" diff --git a/kani_metadata/Cargo.toml b/kani_metadata/Cargo.toml index 148276d2c561..73eee459900d 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_metadata" -version = "0.61.0" +version = "0.62.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index 93ba8d876639..71fc4275a606 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.61.0" +version = "0.62.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani_core/Cargo.toml b/library/kani_core/Cargo.toml index cea8ae669af7..bd824f0e1c97 100644 --- a/library/kani_core/Cargo.toml +++ b/library/kani_core/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_core" -version = "0.61.0" +version = "0.62.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani_macros/Cargo.toml b/library/kani_macros/Cargo.toml index 2204721dd116..ccb88d4fb098 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_macros" -version = "0.61.0" +version = "0.62.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 392812deaffa..724a4abca104 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -5,7 +5,7 @@ # Note: this package is intentionally named std to make sure the names of # standard library symbols are preserved name = "std" -version = "0.61.0" +version = "0.62.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/tools/build-kani/Cargo.toml b/tools/build-kani/Cargo.toml index 87e29cef410f..9c8decbab8ea 100644 --- a/tools/build-kani/Cargo.toml +++ b/tools/build-kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "build-kani" -version = "0.61.0" +version = "0.62.0" edition = "2021" description = "Builds Kani, Sysroot and release bundle." license = "MIT OR Apache-2.0" From a0d7d80a074fe73f86d64d5dff7164b3faa6740e Mon Sep 17 00:00:00 2001 From: thanhnguyen-aws Date: Thu, 8 May 2025 15:18:10 -0700 Subject: [PATCH 131/161] Toolchain upgrade to nightly-2025-05-04 (#4059) This PR upgrade Rust toolchain to nightly-2025-05-04. Thanks to @remi-delmas-3000 the suggestion, we can enable target-features. For this PR, I enabled sse and neon. Resolves #3059 #3878 #4044 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../codegen_cprover_gotoc/codegen/statement.rs | 1 + .../codegen_cprover_gotoc/compiler_interface.rs | 16 +++++++++++++++- .../src/kani_middle/transform/internal_mir.rs | 5 +++++ rust-toolchain.toml | 2 +- .../function-contract/history/block.expected | 2 +- .../history/side_effect.expected | 2 +- .../interior-mutability/api/unsafecell.expected | 2 +- .../whole-struct/refcell.expected | 2 +- .../whole-struct/unsafecell.expected | 2 +- .../expected/ptr_to_ref_cast/alignment/expected | 2 +- tools/scanner/src/lib.rs | 2 +- 11 files changed, 29 insertions(+), 9 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index eabc861b8fb7..19ad12b307b3 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -283,6 +283,7 @@ impl GotocCtx<'_> { | AssertMessage::DivisionByZero { .. } | AssertMessage::RemainderByZero { .. } | AssertMessage::ResumedAfterReturn { .. } + | AssertMessage::ResumedAfterDrop { .. } | AssertMessage::ResumedAfterPanic { .. } => { (msg.description().unwrap(), PropertyClass::Assertion) } diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index 23bfe06ba32a..8deb69714f05 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -29,7 +29,7 @@ use rustc_codegen_ssa::back::archive::{ }; use rustc_codegen_ssa::back::link::link_binary; use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_codegen_ssa::{CodegenResults, CrateInfo}; +use rustc_codegen_ssa::{CodegenResults, CrateInfo, TargetConfig}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::DEFAULT_LOCALE_RESOURCE; use rustc_hir::def_id::{DefId as InternalDefId, LOCAL_CRATE}; @@ -41,6 +41,7 @@ use rustc_session::Session; use rustc_session::config::{CrateType, OutputFilenames, OutputType}; use rustc_session::output::out_filename; use rustc_smir::rustc_internal; +use rustc_span::symbol::Symbol; use rustc_target::spec::PanicStrategy; use stable_mir::CrateDef; use stable_mir::mir::mono::{Instance, MonoItem}; @@ -249,6 +250,19 @@ impl CodegenBackend for GotocCodegenBackend { DEFAULT_LOCALE_RESOURCE } + fn target_config(&self, _sess: &Session) -> TargetConfig { + TargetConfig { + target_features: vec![], + unstable_target_features: vec![Symbol::intern("sse"), Symbol::intern("neon")], + // `true` is used as a default so backends need to acknowledge when they do not + // support the float types, rather than accidentally quietly skipping all tests. + has_reliable_f16: true, + has_reliable_f16_math: true, + has_reliable_f128: true, + has_reliable_f128_math: true, + } + } + fn codegen_crate( &self, tcx: TyCtxt, diff --git a/kani-compiler/src/kani_middle/transform/internal_mir.rs b/kani-compiler/src/kani_middle/transform/internal_mir.rs index 090c74311fa4..fba62152f38e 100644 --- a/kani-compiler/src/kani_middle/transform/internal_mir.rs +++ b/kani-compiler/src/kani_middle/transform/internal_mir.rs @@ -549,6 +549,9 @@ impl RustcInternalMir for AssertMessage { AssertMessage::NullPointerDereference => { rustc_middle::mir::AssertMessage::NullPointerDereference } + AssertMessage::ResumedAfterDrop(coroutine_kind) => { + rustc_middle::mir::AssertMessage::ResumedAfterDrop(coroutine_kind.internal_mir(tcx)) + } } } } @@ -579,6 +582,8 @@ impl RustcInternalMir for TerminatorKind { target: rustc_middle::mir::BasicBlock::from_usize(*target), unwind: unwind.internal_mir(tcx), replace: false, + drop: None, + async_fut: None, } } TerminatorKind::Call { func, args, destination, target, unwind } => { diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 17bb020afb0e..cc30eee35b35 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-04-24" +channel = "nightly-2025-05-04" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/expected/function-contract/history/block.expected b/tests/expected/function-contract/history/block.expected index 83e958f52d77..b54752699237 100644 --- a/tests/expected/function-contract/history/block.expected +++ b/tests/expected/function-contract/history/block.expected @@ -1,5 +1,5 @@ assertion\ - Status: SUCCESS\ - - Description: "|result| old({ let x = &ptr; let y = **x; y + 1 }) == *ptr"\ + - Description: "|result| old({let x = &ptr; let y = **x; y + 1}) == *ptr"\ VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/history/side_effect.expected b/tests/expected/function-contract/history/side_effect.expected index 386399c67941..0a2aa2a7273c 100644 --- a/tests/expected/function-contract/history/side_effect.expected +++ b/tests/expected/function-contract/history/side_effect.expected @@ -1,2 +1,2 @@ -Failed Checks: |result| old({ *ptr+=1; *ptr }) == _val +Failed Checks: |result| old({*ptr+=1; *ptr}) == _val VERIFICATION:- FAILED diff --git a/tests/expected/function-contract/interior-mutability/api/unsafecell.expected b/tests/expected/function-contract/interior-mutability/api/unsafecell.expected index 1646a8a78e7f..3639d3fd5deb 100644 --- a/tests/expected/function-contract/interior-mutability/api/unsafecell.expected +++ b/tests/expected/function-contract/interior-mutability/api/unsafecell.expected @@ -1,6 +1,6 @@ assertion\ - Status: SUCCESS\ -- Description: "|_| unsafe{ *im.x.get() } < 101"\ +- Description: "|_| unsafe{*im.x.get()} < 101"\ in function modify VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/interior-mutability/whole-struct/refcell.expected b/tests/expected/function-contract/interior-mutability/whole-struct/refcell.expected index 225c290a171e..978d86752068 100644 --- a/tests/expected/function-contract/interior-mutability/whole-struct/refcell.expected +++ b/tests/expected/function-contract/interior-mutability/whole-struct/refcell.expected @@ -1,6 +1,6 @@ assertion\ - Status: SUCCESS\ -- Description: "|_| unsafe{ *im.x.as_ptr() } < 101"\ +- Description: "|_| unsafe{*im.x.as_ptr()} < 101"\ in function modify VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/interior-mutability/whole-struct/unsafecell.expected b/tests/expected/function-contract/interior-mutability/whole-struct/unsafecell.expected index 1646a8a78e7f..3639d3fd5deb 100644 --- a/tests/expected/function-contract/interior-mutability/whole-struct/unsafecell.expected +++ b/tests/expected/function-contract/interior-mutability/whole-struct/unsafecell.expected @@ -1,6 +1,6 @@ assertion\ - Status: SUCCESS\ -- Description: "|_| unsafe{ *im.x.get() } < 101"\ +- Description: "|_| unsafe{*im.x.get()} < 101"\ in function modify VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/ptr_to_ref_cast/alignment/expected b/tests/expected/ptr_to_ref_cast/alignment/expected index c7d8f5109e21..2e4d4c3be02a 100644 --- a/tests/expected/ptr_to_ref_cast/alignment/expected +++ b/tests/expected/ptr_to_ref_cast/alignment/expected @@ -1,4 +1,4 @@ check_misaligned_ptr_cast_fail.safety_check\ Status: FAILURE\ -Description: "misaligned pointer to reference cast: address must be a multiple of its type's alignment"\ +Description: "misaligned pointer dereference: address must be a multiple of its type's alignment"\ in function check_misaligned_ptr_cast_fail diff --git a/tools/scanner/src/lib.rs b/tools/scanner/src/lib.rs index fba8bf08e520..83ab1207ecfa 100644 --- a/tools/scanner/src/lib.rs +++ b/tools/scanner/src/lib.rs @@ -43,7 +43,7 @@ pub fn run_all(rustc_args: Vec, verbose: bool) -> ExitCode { /// Executes a compilation and run the analysis that were requested. pub fn run_analyses(rustc_args: Vec, analyses: &[Analysis], verbose: bool) -> ExitCode { VERBOSE.store(verbose, Ordering::Relaxed); - let result = run_with_tcx!(rustc_args, |tcx| analyze_crate(tcx, analyses)); + let result = run_with_tcx!(&rustc_args, |tcx| analyze_crate(tcx, analyses)); if result.is_ok() || matches!(result, Err(CompilerError::Skipped)) { ExitCode::SUCCESS } else { From e9798c76d5b168290fce869e37e6b8c389d8c4da Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 22:20:22 -0700 Subject: [PATCH 132/161] Automatic toolchain upgrade to nightly-2025-05-05 (#4060) Update Rust toolchain from nightly-2025-05-04 to nightly-2025-05-05 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index cc30eee35b35..3fc7c8acbd36 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-05-04" +channel = "nightly-2025-05-05" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 8d5370c3682b5845e8fa2f0ff035be2cc784001b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 15:52:53 +0000 Subject: [PATCH 133/161] Automatic toolchain upgrade to nightly-2025-05-06 (#4061) Update Rust toolchain from nightly-2025-05-05 to nightly-2025-05-06 without any other source changes. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 3fc7c8acbd36..0fb26468fadc 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-05-05" +channel = "nightly-2025-05-06" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 5b42a48ef672ce402e6fdb1e32248d7dc8af5015 Mon Sep 17 00:00:00 2001 From: thanhnguyen-aws Date: Fri, 9 May 2025 11:39:46 -0700 Subject: [PATCH 134/161] Enable target features: x87 and sse2 (#4062) This PR add two target features: x87 and sse2 to target the warning from Rust compiler. Resolves https://github.com/model-checking/kani/issues/3878 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../src/codegen_cprover_gotoc/compiler_interface.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index 8deb69714f05..3c316797693f 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -253,7 +253,12 @@ impl CodegenBackend for GotocCodegenBackend { fn target_config(&self, _sess: &Session) -> TargetConfig { TargetConfig { target_features: vec![], - unstable_target_features: vec![Symbol::intern("sse"), Symbol::intern("neon")], + unstable_target_features: vec![ + Symbol::intern("sse"), + Symbol::intern("neon"), + Symbol::intern("x87"), + Symbol::intern("sse2"), + ], // `true` is used as a default so backends need to acknowledge when they do not // support the float types, rather than accidentally quietly skipping all tests. has_reliable_f16: true, From 670d1d1492d51ea2f2aac246bd1965debff75b0e Mon Sep 17 00:00:00 2001 From: thanhnguyen-aws Date: Fri, 9 May 2025 11:45:09 -0700 Subject: [PATCH 135/161] Fix the bug: Loop contracts are not composable with function contracts (#3979) This PR fixes the bug of loop contracts are not composable with function contracts. Previously, using loop-contract inside a function with contract may result in unwinding the loop instead of contracting it. Resolves #3910 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Carolyn Zech --- .../reference/experimental/loop-contracts.md | 27 ++++++- .../kani_middle/transform/loop_contracts.rs | 72 ++++++++++--------- ...contract_proof_function_with_loop.expected | 13 ++++ .../contract_proof_function_with_loop.rs | 25 +++++++ .../function_call_with_loop.expected | 11 +++ .../loop-contract/function_call_with_loop.rs | 25 +++++++ .../function_with_loop_no_assertion.expected | 8 +++ .../function_with_loop_no_assertion.rs | 25 +++++++ 8 files changed, 172 insertions(+), 34 deletions(-) create mode 100644 tests/expected/loop-contract/contract_proof_function_with_loop.expected create mode 100644 tests/expected/loop-contract/contract_proof_function_with_loop.rs create mode 100644 tests/expected/loop-contract/function_call_with_loop.expected create mode 100644 tests/expected/loop-contract/function_call_with_loop.rs create mode 100644 tests/expected/loop-contract/function_with_loop_no_assertion.expected create mode 100644 tests/expected/loop-contract/function_with_loop_no_assertion.rs diff --git a/docs/src/reference/experimental/loop-contracts.md b/docs/src/reference/experimental/loop-contracts.md index 3cf5ecd429cc..2a07f5c2c1aa 100644 --- a/docs/src/reference/experimental/loop-contracts.md +++ b/docs/src/reference/experimental/loop-contracts.md @@ -139,13 +139,36 @@ In proof path 1, we prove properties inside the loop and at last check that the In proof path 2, we prove properties after leaving the loop. As we leave the loop only when the loop guard is violated, the post condition of the loop can be expressed as `!guard && inv`, which is `x <= 1 && x >= 1` in the example. The postcondition implies `x == 1`—the property we want to prove at the end of `simple_loop_with_loop_contracts`. +## Loop contracts inside functions with contracts +Kani supports using loop contracts together with function contracts, as demonstrated in the following example: +``` Rust +#![feature(proc_macro_hygiene)] +#![feature(stmt_expr_attributes)] + +#[kani::requires(i>=2)] +#[kani::ensures(|ret| *ret == 2)] +pub fn has_loop(mut i: u16) -> u16 { + #[kani::loop_invariant(i>=2)] + while i > 2 { + i = i - 1 + } + i +} + +#[kani::proof_for_contract(has_loop)] +fn contract_proof() { + let i: u16 = kani::any(); + let j = has_loop(i); +} +``` +When loop contracts and function contracts are both enabled (by flags `-Z loop-contracts -Z function-contracts`), +Kani automatically contracts (instead of unwinds) all loops in the functions that we want to prove contracts for. ## Limitations Loop contracts comes with the following limitations. -1. Only `while` loops are supported. The other three kinds of loops are not supported: [`loop` loops](https://doc.rust-lang.org/reference/expressions/loop-expr.html#infinite-loops) - , [`while let` loops](https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-pattern-loops), and [`for` loops](https://doc.rust-lang.org/reference/expressions/loop-expr.html#iterator-loops). +1. `while` loops and `loop` loops are supported. The other kinds of loops are not supported: [`while let` loops](https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-pattern-loops), and [`for` loops](https://doc.rust-lang.org/reference/expressions/loop-expr.html#iterator-loops). 2. Kani infers *loop modifies* with alias analysis. Loop modifies are those variables we assume to be arbitrary in the inductive hypothesis, and should cover all memory locations that are written to during the execution of the loops. A proof will fail if the inferred loop modifies misses some targets written in the loops. We observed this happens when some fields of structs are modified by some other functions called in the loops. diff --git a/kani-compiler/src/kani_middle/transform/loop_contracts.rs b/kani-compiler/src/kani_middle/transform/loop_contracts.rs index 45484f41091d..c3fdbcfc06d5 100644 --- a/kani-compiler/src/kani_middle/transform/loop_contracts.rs +++ b/kani-compiler/src/kani_middle/transform/loop_contracts.rs @@ -103,39 +103,10 @@ impl TransformPass for LoopContractPass { let run = Instance::resolve(self.run_contract_fn.unwrap(), args).unwrap(); (true, run.body().unwrap()) } else { - let mut new_body = MutableBody::from(body); - let mut contain_loop_contracts: bool = false; - - // Visit basic blocks in control flow order (BFS). - let mut visited: HashSet = HashSet::new(); - let mut queue: VecDeque = VecDeque::new(); - // Visit blocks in loops only when there is no blocks in queue. - let mut loop_queue: VecDeque = VecDeque::new(); - queue.push_back(0); - - while let Some(bb_idx) = queue.pop_front().or_else(|| loop_queue.pop_front()) { - visited.insert(bb_idx); - - let terminator = new_body.blocks()[bb_idx].terminator.clone(); - - let is_loop_head = self.transform_bb(tcx, &mut new_body, bb_idx); - contain_loop_contracts |= is_loop_head; - - // Add successors of the current basic blocks to - // the visiting queue. - for to_visit in terminator.successors() { - if !visited.contains(&to_visit) { - if is_loop_head { - loop_queue.push_back(to_visit); - } else { - queue.push_back(to_visit) - }; - } - } - } - (contain_loop_contracts, new_body.into()) + self.transform_body_with_loop(tcx, body) } } + RigidTy::Closure(_, _) => self.transform_body_with_loop(tcx, body), _ => { /* static variables case */ (false, body) @@ -194,6 +165,43 @@ impl LoopContractPass { )) } + /// This function transform the function body as described in fn transform. + /// It is the core of fn transform, and is separated just to avoid code repetition. + fn transform_body_with_loop(&mut self, tcx: TyCtxt, body: Body) -> (bool, Body) { + let mut new_body = MutableBody::from(body); + let mut contain_loop_contracts: bool = false; + + // Visit basic blocks in control flow order (BFS). + let mut visited: HashSet = HashSet::new(); + let mut queue: VecDeque = VecDeque::new(); + // Visit blocks in loops only when there is no blocks in queue. + let mut loop_queue: VecDeque = VecDeque::new(); + queue.push_back(0); + + while let Some(bb_idx) = queue.pop_front().or_else(|| loop_queue.pop_front()) { + visited.insert(bb_idx); + + let terminator = new_body.blocks()[bb_idx].terminator.clone(); + + let is_loop_head = self.transform_bb(tcx, &mut new_body, bb_idx); + contain_loop_contracts |= is_loop_head; + + // Add successors of the current basic blocks to + // the visiting queue. + for to_visit in terminator.successors() { + if !visited.contains(&to_visit) { + if is_loop_head { + loop_queue.push_back(to_visit); + } else { + queue.push_back(to_visit) + }; + } + } + } + + (contain_loop_contracts, new_body.into()) + } + /// Transform loops with contracts from /// ```ignore /// bb_idx: { @@ -316,7 +324,7 @@ impl LoopContractPass { for stmt in &new_body.blocks()[bb_idx].statements { if let StatementKind::Assign(place, rvalue) = &stmt.kind { match rvalue { - Rvalue::Ref(_,_,rplace) => { + Rvalue::Ref(_,_,rplace) | Rvalue::CopyForDeref(rplace) => { if supported_vars.contains(&rplace.local) { supported_vars.push(place.local); } } diff --git a/tests/expected/loop-contract/contract_proof_function_with_loop.expected b/tests/expected/loop-contract/contract_proof_function_with_loop.expected new file mode 100644 index 000000000000..0109678c22fd --- /dev/null +++ b/tests/expected/loop-contract/contract_proof_function_with_loop.expected @@ -0,0 +1,13 @@ +has_loop::{closure#2}::{closure#1}.loop_invariant_step.1\ + - Status: SUCCESS\ + - Description: "Check invariant after step for loop has_loop::{closure#2}::{closure#1}.0"\ +in function has_loop::{closure#2}::{closure#1} + + + +has_loop::{closure#2}::{closure#1}.precondition_instance.1\ + - Status: SUCCESS\ + - Description: "free argument must be NULL or valid pointer"\ +in function has_loop::{closure#2}::{closure#1} + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/loop-contract/contract_proof_function_with_loop.rs b/tests/expected/loop-contract/contract_proof_function_with_loop.rs new file mode 100644 index 000000000000..446214979c69 --- /dev/null +++ b/tests/expected/loop-contract/contract_proof_function_with_loop.rs @@ -0,0 +1,25 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: -Z loop-contracts -Z function-contracts + +//! Test that we can prove a function contract and a loop contract in tandem. + +#![feature(proc_macro_hygiene)] +#![feature(stmt_expr_attributes)] + +#[kani::requires(i>=2)] +#[kani::ensures(|ret| *ret == 2)] +pub fn has_loop(mut i: u16) -> u16 { + #[kani::loop_invariant(i>=2)] + while i > 2 { + i = i - 1 + } + i +} + +#[kani::proof_for_contract(has_loop)] +fn contract_proof() { + let i: u16 = kani::any(); + let j = has_loop(i); +} diff --git a/tests/expected/loop-contract/function_call_with_loop.expected b/tests/expected/loop-contract/function_call_with_loop.expected new file mode 100644 index 000000000000..d16789b1d209 --- /dev/null +++ b/tests/expected/loop-contract/function_call_with_loop.expected @@ -0,0 +1,11 @@ +has_loop::{closure#3}::{closure#0}.loop_invariant_base.1\ + - Status: SUCCESS\ + - Description: "Check invariant before entry for loop has_loop::{closure#3}::{closure#0}.0"\ +in function has_loop::{closure#3}::{closure#0} + +has_loop::{closure#3}::{closure#0}.precondition_instance.5\ + - Status: SUCCESS\ + - Description: "free called for new[] object"\ +in function has_loop::{closure#3}::{closure#0} + +Failed Checks: i>=2 diff --git a/tests/expected/loop-contract/function_call_with_loop.rs b/tests/expected/loop-contract/function_call_with_loop.rs new file mode 100644 index 000000000000..cb2453496187 --- /dev/null +++ b/tests/expected/loop-contract/function_call_with_loop.rs @@ -0,0 +1,25 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: -Z loop-contracts -Z function-contracts + +//! Calling a function that contains loops and test that using a #[kani::proof] harness fails because the function's precondition gets asserted. + +#![feature(proc_macro_hygiene)] +#![feature(stmt_expr_attributes)] + +#[kani::requires(i>=2)] +#[kani::ensures(|ret| *ret == 2)] +pub fn has_loop(mut i: u16) -> u16 { + #[kani::loop_invariant(i>=2)] + while i > 2 { + i = i - 1 + } + i +} + +#[kani::proof] +fn call_has_loop() { + let i: u16 = kani::any(); + let j = has_loop(i); +} diff --git a/tests/expected/loop-contract/function_with_loop_no_assertion.expected b/tests/expected/loop-contract/function_with_loop_no_assertion.expected new file mode 100644 index 000000000000..afd863b2937b --- /dev/null +++ b/tests/expected/loop-contract/function_with_loop_no_assertion.expected @@ -0,0 +1,8 @@ +has_loop.loop_invariant_base.1\ + - Status: SUCCESS\ + - Description: "Check invariant before entry for loop has_loop.0"\ +in function has_loop + + + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/loop-contract/function_with_loop_no_assertion.rs b/tests/expected/loop-contract/function_with_loop_no_assertion.rs new file mode 100644 index 000000000000..5609796d56be --- /dev/null +++ b/tests/expected/loop-contract/function_with_loop_no_assertion.rs @@ -0,0 +1,25 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// kani-flags: -Z loop-contracts -Z function-contracts --no-assert-contracts + +//Call a function with loop without checking the contract. + +#![feature(proc_macro_hygiene)] +#![feature(stmt_expr_attributes)] + +#[kani::requires(i>=2)] +#[kani::ensures(|ret| *ret == 2)] +pub fn has_loop(mut i: u16) -> u16 { + #[kani::loop_invariant(i>=2)] + while i > 2 { + i = i - 1 + } + i +} + +#[kani::proof] +fn contract_proof() { + let i: u16 = kani::any(); + let j = has_loop(i); +} From 91680b69c30f80f2be5a20e3a20026794c3f9f33 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 11 May 2025 23:15:46 -0700 Subject: [PATCH 136/161] Automatic cargo update to 2025-05-12 (#4066) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 75 +++++++++++++++++++++++------------------------------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15732a85da9e..378eb56d5477 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,12 +19,12 @@ checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.2.16", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -135,9 +135,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.21" +version = "1.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" +checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" dependencies = [ "shlex", ] @@ -276,9 +276,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -286,9 +286,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -639,20 +639,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -794,9 +783,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07d8d955d798e7a4d6f9c58cd1f1916e790b42b092758a9ef6e16fef9f1b3fd" +checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806" dependencies = [ "jiff-static", "log", @@ -807,9 +796,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f244cfe006d98d26f859c7abd1318d85327e1882dc9cef80f62daeeb0adcf300" +checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48" dependencies = [ "proc-macro2", "quote", @@ -1185,9 +1174,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "os_info" -version = "3.10.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a604e53c24761286860eba4e2c8b23a0161526476b1de520139d69cdb85a6b5" +checksum = "41fc863e2ca13dc2d5c34fb22ea4a588248ac14db929616ba65c45f21744b1e9" dependencies = [ "log", "windows-sys 0.52.0", @@ -1712,12 +1701,12 @@ checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom", "once_cell", "rustix 1.0.7", "windows-sys 0.59.0", @@ -1823,9 +1812,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.2" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", @@ -1978,9 +1967,9 @@ dependencies = [ [[package]] name = "tree-sitter" -version = "0.25.3" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ac5ea5e7f2f1700842ec071401010b9c59bf735295f6e9fa079c3dc035b167" +checksum = "69aff09fea9a41fb061ae6b206cb87cac1b8db07df31be3ba271fbc26760f213" dependencies = [ "cc", "regex", @@ -2215,9 +2204,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -2239,18 +2228,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", From 1e9e8db0073c53e0bcd60920f18ebada89c5056d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 10:23:53 -0700 Subject: [PATCH 137/161] Bump tests/perf/s2n-quic from `6aa9975` to `5f323b7` (#4068) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `6aa9975` to `5f323b7`.
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 6aa9975c999b..5f323b771557 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 6aa9975c999b25b282bfafc3791393c8c2a8f25d +Subproject commit 5f323b77155766d3878755e5de48972df4b47344 From 1ff80547bac48124ae8cfb6a6bdfbec67defb68d Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Mon, 12 May 2025 13:45:11 -0400 Subject: [PATCH 138/161] Fix stabilization instructions in RFC intro (#4067) The introduction to our RFC book instructs people to remove the unstable flag in one go when they're stabilizing features, which contradicts the instructions from the unstable APIs RFC to do a two-phase deletion. Update the intro to be consistent with the two-phase deletion approach. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- rfc/src/intro.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rfc/src/intro.md b/rfc/src/intro.md index c10b1486dfba..8278dd682edd 100644 --- a/rfc/src/intro.md +++ b/rfc/src/intro.md @@ -61,8 +61,10 @@ This is the overall workflow for the RFC process: 4. Add regression tests to cover all expected behaviors and unit tests whenever possible. 5. Stabilization. 1. Propose to stabilize the feature when feature is well tested and UX has received positive feedback. - 2. Create a new PR that removes the `-Z ` guard and that marks the RFC status as "STABLE". + 2. Create a new PR that makes the option a no-op with a deprecation warning. + 3. *Only after the PR from #2 is included in a release*, create another PR that actually removes the option and marks the RFC status as "STABLE". 1. Make sure the RFC reflects the final implementation and user experience. + 2. See [#3561](https://github.com/model-checking/kani/issues/3561) for an example of such a two-phase deletion, where we first deprecate the option in one release, then remove it in the next. See also [0006-unstable-api](rfcs/0006-unstable-api.md). 3. In some cases, we might decide not to incorporate a feature (E.g.: performance degradation, bad user experience, better alternative). In those cases, please update the RFC status to "CANCELLED as per " and remove the code From db238e6f257c4c33f8e60ae55aa5a3ca35a4df43 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Tue, 13 May 2025 11:55:29 -0500 Subject: [PATCH 139/161] Add support for quantifiers (#3993) This PR add support for quantifiers. Especially, we inline function calls in quantified expressions so that the result statement-expression can be accepted by the CBMC backend. RFC: [RFC 0010-quantifiers](https://github.com/model-checking/kani/blob/main/rfc/src/rfcs/0010-quantifiers.md). Resolves https://github.com/model-checking/kani/issues/2546 and https://github.com/model-checking/kani/issues/836. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Signed-off-by: Felipe R. Monteiro Co-authored-by: Felipe R. Monteiro Co-authored-by: Michael Tautschnig --- cprover_bindings/src/goto_program/expr.rs | 18 + cprover_bindings/src/goto_program/symbol.rs | 4 + .../src/goto_program/symbol_table.rs | 32 +- cprover_bindings/src/irep/to_irep.rs | 24 + docs/src/SUMMARY.md | 1 + .../src/reference/experimental/quantifiers.md | 56 +++ .../codegen_cprover_gotoc/codegen/function.rs | 23 +- .../compiler_interface.rs | 2 + .../codegen_cprover_gotoc/context/goto_ctx.rs | 459 +++++++++++++++++- .../codegen_cprover_gotoc/overrides/hooks.rs | 135 +++++- .../src/kani_middle/kani_functions.rs | 4 + library/kani_core/src/lib.rs | 46 ++ rfc/src/rfcs/0010-quantifiers.md | 66 ++- .../assert_with_exists_fail.expected | 4 + .../quantifiers/assert_with_exists_fail.rs | 9 + .../assert_with_exists_pass.expected | 4 + .../quantifiers/assert_with_exists_pass.rs | 9 + .../assert_with_forall_fail.expected | 5 + .../quantifiers/assert_with_forall_fail.rs | 9 + .../assert_with_forall_pass.expected | 4 + .../quantifiers/assert_with_forall_pass.rs | 9 + .../assume_with_exists_fail.expected | 4 + .../quantifiers/assume_with_exists_fail.rs | 9 + .../quantifiers/contracts_fail.expected | 11 + tests/expected/quantifiers/contracts_fail.rs | 24 + tests/kani/Quantifiers/array.rs | 33 ++ tests/kani/Quantifiers/contracts.rs | 43 ++ tests/kani/Quantifiers/even.rs | 9 + tests/kani/Quantifiers/from_raw_part_fixme.rs | 45 ++ tests/kani/Quantifiers/no_array.rs | 48 ++ 30 files changed, 1121 insertions(+), 28 deletions(-) create mode 100644 docs/src/reference/experimental/quantifiers.md create mode 100644 tests/expected/quantifiers/assert_with_exists_fail.expected create mode 100644 tests/expected/quantifiers/assert_with_exists_fail.rs create mode 100644 tests/expected/quantifiers/assert_with_exists_pass.expected create mode 100644 tests/expected/quantifiers/assert_with_exists_pass.rs create mode 100644 tests/expected/quantifiers/assert_with_forall_fail.expected create mode 100644 tests/expected/quantifiers/assert_with_forall_fail.rs create mode 100644 tests/expected/quantifiers/assert_with_forall_pass.expected create mode 100644 tests/expected/quantifiers/assert_with_forall_pass.rs create mode 100644 tests/expected/quantifiers/assume_with_exists_fail.expected create mode 100644 tests/expected/quantifiers/assume_with_exists_fail.rs create mode 100644 tests/expected/quantifiers/contracts_fail.expected create mode 100644 tests/expected/quantifiers/contracts_fail.rs create mode 100644 tests/kani/Quantifiers/array.rs create mode 100644 tests/kani/Quantifiers/contracts.rs create mode 100644 tests/kani/Quantifiers/even.rs create mode 100644 tests/kani/Quantifiers/from_raw_part_fixme.rs create mode 100644 tests/kani/Quantifiers/no_array.rs diff --git a/cprover_bindings/src/goto_program/expr.rs b/cprover_bindings/src/goto_program/expr.rs index 5d0890e99d2b..469df299ee5e 100644 --- a/cprover_bindings/src/goto_program/expr.rs +++ b/cprover_bindings/src/goto_program/expr.rs @@ -177,6 +177,14 @@ pub enum ExprValue { Vector { elems: Vec, }, + Forall { + variable: Expr, // symbol + domain: Expr, // where + }, + Exists { + variable: Expr, // symbol + domain: Expr, // where + }, } /// Binary operators. The names are the same as in the Irep representation. @@ -972,6 +980,16 @@ impl Expr { let typ = typ.aggr_tag().unwrap(); expr!(Union { value, field }, typ) } + + pub fn forall_expr(typ: Type, variable: Expr, domain: Expr) -> Expr { + assert!(variable.is_symbol()); + expr!(Forall { variable, domain }, typ) + } + + pub fn exists_expr(typ: Type, variable: Expr, domain: Expr) -> Expr { + assert!(variable.is_symbol()); + expr!(Exists { variable, domain }, typ) + } } /// Constructors for Binary Operations diff --git a/cprover_bindings/src/goto_program/symbol.rs b/cprover_bindings/src/goto_program/symbol.rs index 5c86dd04909a..bb40ed9ed9d9 100644 --- a/cprover_bindings/src/goto_program/symbol.rs +++ b/cprover_bindings/src/goto_program/symbol.rs @@ -172,6 +172,10 @@ impl Symbol { } } + pub fn update(&mut self, value: SymbolValues) { + self.value = value; + } + /// Add this contract to the symbol (symbol must be a function) or fold the /// conditions into an existing contract. pub fn attach_contract(&mut self, contract: FunctionContract) { diff --git a/cprover_bindings/src/goto_program/symbol_table.rs b/cprover_bindings/src/goto_program/symbol_table.rs index 97567670dee0..b66eb581e3cb 100644 --- a/cprover_bindings/src/goto_program/symbol_table.rs +++ b/cprover_bindings/src/goto_program/symbol_table.rs @@ -10,13 +10,18 @@ use std::collections::BTreeMap; #[derive(Clone, Debug)] pub struct SymbolTable { symbol_table: BTreeMap, + parameters_map: BTreeMap>, machine_model: MachineModel, } /// Constructors impl SymbolTable { pub fn new(machine_model: MachineModel) -> SymbolTable { - let mut symtab = SymbolTable { machine_model, symbol_table: BTreeMap::new() }; + let mut symtab = SymbolTable { + machine_model, + symbol_table: BTreeMap::new(), + parameters_map: BTreeMap::new(), + }; env::machine_model_symbols(symtab.machine_model()) .into_iter() .for_each(|s| symtab.insert(s)); @@ -54,6 +59,19 @@ impl SymbolTable { self.symbol_table.insert(symbol.name, symbol); } + /// Inserts a parameter into the parameters map for a given function symbol. + /// If the function does not exist in the parameters map, it initializes it with an empty vector. + pub fn insert_parameter, P: Into>( + &mut self, + function_name: T, + parameter: P, + ) { + let function_name = function_name.into(); + let parameter = parameter.into(); + + self.parameters_map.entry(function_name).or_default().push(parameter); + } + /// Validates the previous value of the symbol using the validator function, then replaces it. /// Useful to replace declarations with the actual definition. pub fn replace) -> bool>( @@ -102,6 +120,10 @@ impl SymbolTable { self.symbol_table.iter() } + pub fn iter_mut(&mut self) -> std::collections::btree_map::IterMut<'_, InternedString, Symbol> { + self.symbol_table.iter_mut() + } + pub fn lookup>(&self, name: T) -> Option<&Symbol> { let name = name.into(); self.symbol_table.get(&name) @@ -112,6 +134,14 @@ impl SymbolTable { self.symbol_table.get_mut(&name) } + pub fn lookup_parameters>( + &self, + name: T, + ) -> Option<&Vec> { + let name = name.into(); + self.parameters_map.get(&name) + } + pub fn machine_model(&self) -> &MachineModel { &self.machine_model } diff --git a/cprover_bindings/src/irep/to_irep.rs b/cprover_bindings/src/irep/to_irep.rs index b9cc6978ea85..90c8189ba1b4 100644 --- a/cprover_bindings/src/irep/to_irep.rs +++ b/cprover_bindings/src/irep/to_irep.rs @@ -377,6 +377,30 @@ impl ToIrep for ExprValue { sub: elems.iter().map(|x| x.to_irep(mm)).collect(), named_sub: linear_map![], }, + ExprValue::Forall { variable, domain } => Irep { + id: IrepId::Forall, + sub: vec![ + Irep { + id: IrepId::Tuple, + sub: vec![variable.to_irep(mm)], + named_sub: linear_map![], + }, + domain.to_irep(mm), + ], + named_sub: linear_map![], + }, + ExprValue::Exists { variable, domain } => Irep { + id: IrepId::Exists, + sub: vec![ + Irep { + id: IrepId::Tuple, + sub: vec![variable.to_irep(mm)], + named_sub: linear_map![], + }, + domain.to_irep(mm), + ], + named_sub: linear_map![], + }, } } } diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 0b618c577021..9d7553a668d7 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -25,6 +25,7 @@ - [Contracts](./reference/experimental/contracts.md) - [Loop Contracts](./reference/experimental/loop-contracts.md) - [Concrete Playback](./reference/experimental/concrete-playback.md) + - [Quantifiers](./reference/experimental/quantifiers.md) - [Application](./application.md) - [Comparison with other tools](./tool-comparison.md) - [Where to start on real code](./tutorial-real-code.md) diff --git a/docs/src/reference/experimental/quantifiers.md b/docs/src/reference/experimental/quantifiers.md new file mode 100644 index 000000000000..c62a70f2c7b3 --- /dev/null +++ b/docs/src/reference/experimental/quantifiers.md @@ -0,0 +1,56 @@ +# Quantifiers in Kani + +Quantifiers are a powerful feature in formal verification that allow you to express properties over a range of values. Kani provides experimental support for quantifiers, enabling users to write concise and expressive specifications for their programs. + +## Supported Quantifiers + +Kani currently supports the following quantifiers: + +1. **Universal Quantifier**: + - Ensures that a property holds for all values in a given range. + - Syntax: `kani::forall!(|variable in range| condition)` + - Example: + +```rust +#[kani::proof] +fn test_forall() { + let v = vec![10; 10]; + kani::assert(kani::forall!(|i in 0..10| v[i] == 10)); +} +``` + +2. **Existential Quantifier**: + - Ensures that there exists at least one value in a given range for which a property holds. + - Syntax: `kani::exists!(|variable in range| condition)` + - Example: + +```rust +#[kani::proof] +fn test_exists() { + let v = vec![1, 2, 3, 4, 5]; + kani::assert(kani::exists!(|i in 0..v.len()| v[i] == 3)); +} +``` + +### Limitations + +#### Array Indexing + +The performance of quantifiers can be affected by the depth of call stacks in the quantified expressions. If the call stack is too deep, Kani may not be able to evaluate the quantifier effectively, leading to potential timeouts or running out of memory. Actually, array indexing in Rust leads to a deep call stack, which can cause issues with quantifiers. To mitigate this, consider using *unsafe* pointer dereferencing instead of array indexing when working with quantifiers. For example: + +```rust + +#[kani::proof] +fn vec_assert_forall_harness() { + let v = vec![10 as u8; 128]; + let ptr = v.as_ptr(); + unsafe { + kani::assert(kani::forall!(|i in (0,128)| *ptr.wrapping_byte_offset(i as isize) == 10), ""); + } +} +``` + +#### Types of Quantified Variables + +We now assume that all quantified variables are of type `usize`. This means that the range specified in the quantifier must be compatible with `usize`. + We plan to support other types in the future, but for now, ensure that your quantifiers use `usize` ranges. diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs index dd6909483694..de17c4b33879 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs @@ -6,6 +6,7 @@ use crate::codegen_cprover_gotoc::GotocCtx; use crate::codegen_cprover_gotoc::codegen::block::reverse_postorder; use cbmc::InternString; +use cbmc::InternedString; use cbmc::goto_program::{Expr, Stmt, Symbol}; use stable_mir::CrateDef; use stable_mir::mir::mono::Instance; @@ -20,7 +21,7 @@ impl GotocCtx<'_> { /// - Index 0 represents the return value. /// - Indices [1, N] represent the function parameters where N is the number of parameters. /// - Indices that are greater than N represent local variables. - fn codegen_declare_variables(&mut self, body: &Body) { + fn codegen_declare_variables(&mut self, body: &Body, function_name: InternedString) { let ldecls = body.local_decls(); let num_args = body.arg_locals().len(); for (lc, ldata) in ldecls { @@ -35,13 +36,23 @@ impl GotocCtx<'_> { let loc = self.codegen_span_stable(ldata.span); // Indices [1, N] represent the function parameters where N is the number of parameters. // Except that ZST fields are not included as parameters. - let sym = - Symbol::variable(name, base_name, var_type, self.codegen_span_stable(ldata.span)) - .with_is_hidden(!self.is_user_variable(&lc)) - .with_is_parameter((lc > 0 && lc <= num_args) && !self.is_zst_stable(ldata.ty)); + let sym = Symbol::variable( + name.clone(), + base_name, + var_type, + self.codegen_span_stable(ldata.span), + ) + .with_is_hidden(!self.is_user_variable(&lc)) + .with_is_parameter((lc > 0 && lc <= num_args) && !self.is_zst_stable(ldata.ty)); let sym_e = sym.to_expr(); self.symbol_table.insert(sym); + // Store the parameter symbols of the function, which will be used for function + // inlining. + if lc > 0 && lc <= num_args { + self.symbol_table.insert_parameter(function_name, name); + } + // Index 0 represents the return value, which does not need to be // declared in the first block if lc < 1 || lc > body.arg_locals().len() { @@ -64,7 +75,7 @@ impl GotocCtx<'_> { self.set_current_fn(instance, &body); self.print_instance(instance, &body); self.codegen_function_prelude(&body); - self.codegen_declare_variables(&body); + self.codegen_declare_variables(&body, name.clone().into()); // Get the order from internal body for now. reverse_postorder(&body).for_each(|bb| self.codegen_block(bb, &body.blocks[bb])); diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index 3c316797693f..9080d1a16c00 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -199,6 +199,8 @@ impl GotocCodegenBackend { None }; + gcx.handle_quantifiers(); + // No output should be generated if user selected no_codegen. if !tcx.sess.opts.unstable_opts.no_codegen && tcx.sess.opts.output_types.should_codegen() { let pretty = self.queries.lock().unwrap().args().output_pretty_json; diff --git a/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs b/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs index 79410bd3373e..e3073f87ba6b 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs @@ -22,7 +22,8 @@ use crate::codegen_cprover_gotoc::utils::full_crate_name; use crate::kani_middle::transform::BodyTransformation; use crate::kani_queries::QueryDb; use cbmc::goto_program::{ - DatatypeComponent, Expr, Location, Stmt, Symbol, SymbolTable, SymbolValues, Type, + CIntType, DatatypeComponent, Expr, ExprValue, Location, Stmt, StmtBody, SwitchCase, Symbol, + SymbolTable, SymbolValues, Type, }; use cbmc::utils::aggr_tag; use cbmc::{InternedString, MachineModel}; @@ -40,6 +41,7 @@ use rustc_target::callconv::FnAbi; use stable_mir::mir::Body; use stable_mir::mir::mono::Instance; use stable_mir::ty::Allocation; +use std::collections::{BTreeMap, HashSet}; use std::fmt::Debug; pub struct GotocCtx<'tcx> { @@ -304,6 +306,461 @@ impl<'tcx> GotocCtx<'tcx> { } } +/// Quantifiers Related +impl GotocCtx<'_> { + /// Find all quantifier expressions and recursively inline functions in the quantifier bodies. + /// We inline all the function calls in quantifier expressions because CBMC accept only + /// statement expressiont without function calls in quantifier expressions: + /// see https://github.com/diffblue/cbmc/pull/8605 for detail. + pub fn handle_quantifiers(&mut self) { + // Store the found quantifiers and the inlined results. + let mut to_modify: BTreeMap = BTreeMap::new(); + for (key, symbol) in self.symbol_table.iter() { + if let SymbolValues::Stmt(stmt) = &symbol.value { + let new_stmt_val = SymbolValues::Stmt(self.handle_quantifiers_in_stmt(stmt)); + to_modify.insert(*key, new_stmt_val); + } + } + + // Update the found quantifiers with the inlined results. + for (key, symbol_value) in to_modify { + self.symbol_table.lookup_mut(key).unwrap().update(symbol_value); + } + } + + /// Find all quantifier expressions in `stmt` and recursively inline functions. + fn handle_quantifiers_in_stmt(&self, stmt: &Stmt) -> Stmt { + match &stmt.body() { + // According to the hook handling for quantifiers, quantifier expressions must be of form + // lhs = typecast(qex, c_bool) + // where qex is either a forall-expression or an exists-expression. + StmtBody::Assign { lhs, rhs } => { + let new_rhs = match &rhs.value() { + ExprValue::Typecast(quantified_expr) => match &quantified_expr.value() { + ExprValue::Forall { variable, domain } => { + // We store the function symbols we have inlined to avoid recursion. + let mut visited_func_symbols: HashSet = HashSet::new(); + // We count the number of function that we have inlined, and use the count to + // make inlined labeled unique. + let mut suffix_count: u16 = 0; + + let end_stmt = Stmt::code_expression( + self.inline_function_calls_in_expr( + domain, + &mut visited_func_symbols, + &mut suffix_count, + ) + .unwrap(), + *domain.location(), + ); + + // Make the result a statement expression. + let res = Expr::forall_expr( + Type::Bool, + variable.clone(), + Expr::statement_expression( + vec![Stmt::skip(*domain.location()), end_stmt], + Type::Bool, + *domain.location(), + ), + ); + res.cast_to(Type::CInteger(CIntType::Bool)) + } + ExprValue::Exists { variable, domain } => { + // We store the function symbols we have inlined to avoid recursion. + let mut visited_func_symbols: HashSet = HashSet::new(); + // We count the number of function that we have inlined, and use the count to + // make inlined labeled unique. + let mut suffix_count = 0; + + let end_stmt = Stmt::code_expression( + self.inline_function_calls_in_expr( + domain, + &mut visited_func_symbols, + &mut suffix_count, + ) + .unwrap(), + *domain.location(), + ); + + // Make the result a statement expression. + let res = Expr::exists_expr( + Type::Bool, + variable.clone(), + Expr::statement_expression( + vec![Stmt::skip(*domain.location()), end_stmt], + Type::Bool, + *domain.location(), + ), + ); + res.cast_to(Type::CInteger(CIntType::Bool)) + } + _ => rhs.clone(), + }, + _ => rhs.clone(), + }; + Stmt::assign(lhs.clone(), new_rhs, *stmt.location()) + } + // Recursively find quantifier expressions. + StmtBody::Block(stmts) => Stmt::block( + stmts.iter().map(|stmt| self.handle_quantifiers_in_stmt(stmt)).collect(), + *stmt.location(), + ), + StmtBody::Label { label, body } => { + self.handle_quantifiers_in_stmt(body).with_label(*label) + } + _ => stmt.clone(), + } + } + + /// Count and return the number of return statements in `stmt`. + fn count_return_stmts(stmt: &Stmt) -> usize { + match stmt.body() { + StmtBody::Return(_) => 1, + StmtBody::Block(stmts) => stmts.iter().map(Self::count_return_stmts).sum(), + StmtBody::Label { label: _, body } => Self::count_return_stmts(body), + _ => 0, + } + } + + /// Rewrite return statements in `stmt` with a goto statement to `end_label`. + /// It also stores the return symbol in `return_symbol`. + /// When inlining the function body of some function foo + /// fn foo(params) { + /// body; + /// return res; // ** rewrite this statement + /// } + /// with the statement expression + /// { + /// DECL params + /// ASSIGN params = args + /// inline(body) + /// GOTO end_label // ** to this statement + /// end_label: + /// EXPRESSION res + /// }, + /// this function rewrites all return statements + /// into a goto statement to `end_label` + fn rewrite_return_stmt_with_goto( + stmt: &Stmt, + return_symbol: &mut Option, + end_label: &InternedString, + ) -> Stmt { + match stmt.body() { + StmtBody::Return(Some(expr)) => { + if let ExprValue::Symbol { ref identifier } = expr.value() { + *return_symbol = Some(Expr::symbol_expression(*identifier, expr.typ().clone())); + Stmt::goto(*end_label, *stmt.location()) + } else { + panic!("Expected symbol expression in return statement"); + } + } + StmtBody::Block(stmts) => Stmt::block( + stmts + .iter() + .map(|s| Self::rewrite_return_stmt_with_goto(s, return_symbol, end_label)) + .collect(), + *stmt.location(), + ), + StmtBody::Label { label, body } => { + Self::rewrite_return_stmt_with_goto(body, return_symbol, end_label) + .with_label(*label) + } + _ => stmt.clone(), + } + } + + /// Append a given suffix to all labels and goto destinations in `stmt`. + fn append_suffix_to_stmt(stmt: &Stmt, suffix: &str) -> Stmt { + match stmt.body() { + StmtBody::Label { label, body } => { + let new_label = format!("{label}{suffix}"); + Self::append_suffix_to_stmt(body, suffix).with_label(new_label) + } + StmtBody::Goto { dest, .. } => { + let new_target = format!("{dest}{suffix}"); + Stmt::goto(new_target, *stmt.location()) + } + StmtBody::Block(stmts) => Stmt::block( + stmts.iter().map(|s| Self::append_suffix_to_stmt(s, suffix)).collect(), + *stmt.location(), + ), + StmtBody::Ifthenelse { i, t, e } => Stmt::if_then_else( + i.clone(), + Self::append_suffix_to_stmt(t, suffix), + e.clone().map(|s| Self::append_suffix_to_stmt(&s, suffix)), + *stmt.location(), + ), + StmtBody::Switch { control, cases, default } => { + // Append the suffix to each case + let new_cases: Vec<_> = cases + .iter() + .map(|case| { + let new_body = Self::append_suffix_to_stmt(case.body(), suffix); + SwitchCase::new(case.case().clone(), new_body) + }) + .collect(); + + // Append the suffix to the default case, if it exists + let new_default = + default.as_ref().map(|stmt| Self::append_suffix_to_stmt(stmt, suffix)); + + // Construct the new switch statement + Stmt::switch(control.clone(), new_cases, new_default, *stmt.location()) + } + StmtBody::While { .. } | StmtBody::For { .. } => { + unimplemented!() + } + _ => stmt.clone(), + } + } + + /// Recursively inline all function calls in `expr`. + /// `visited_func_symbols` contain all function symbols in the stack. + /// `suffix_count` is used to make inlined labels unique. + fn inline_function_calls_in_expr( + &self, + expr: &Expr, + visited_func_symbols: &mut HashSet, + suffix_count: &mut u16, + ) -> Option { + match &expr.value() { + // For function call expression, we find the function symbol and function body from the + // symbol table for inlining. + ExprValue::FunctionCall { function, arguments } => { + if let ExprValue::Symbol { identifier } = &function.value() { + // Check if the function symbol exists in the symbol table + if let Some(function_body) = + self.symbol_table.lookup(*identifier).and_then(|sym| match &sym.value { + SymbolValues::Stmt(stmt) => Some(stmt), + _ => None, + }) + { + // For function calls to foo(args) where the definition of foo is + // fn foo(params) { + // body; + // return res; + // } + // The inlining result will be a statement expression + // { + // DECL params + // ASSIGN params = args + // inline(body) + // GOTO end_label + // end_label: + // EXPRESSION res + // } + // where res is the end expression of the statement expression. + + // Keep suffix unique in difference inlining. + *suffix_count += 1; + + // Use call stacks to avoid recursion. + assert!( + !visited_func_symbols.contains(identifier), + "Detected recursions in the usage of quantifiers." + ); + visited_func_symbols.insert(*identifier); + + let inlined_body: &Stmt = function_body; + let mut stmts_of_inlined_body: Vec = + inlined_body.get_stmts().unwrap().clone(); + + // Substitute parameters with arguments in the function body. + if let Some(parameters) = self.symbol_table.lookup_parameters(*identifier) { + // Create decl statements of parameters. + let mut param_decls: Vec = parameters + .iter() + .zip(arguments.iter()) + .map(|(param, arg)| { + Stmt::decl( + Expr::symbol_expression(*param, arg.typ().clone()), + None, + *arg.location(), + ) + }) + .collect(); + + // Create assignment statements from arguments to parameters. + let mut param_assigs: Vec = parameters + .iter() + .zip(arguments.iter()) + .map(|(param, arg)| { + Stmt::assign( + Expr::symbol_expression(*param, arg.typ().clone()), + arg.clone(), + *arg.location(), + ) + }) + .collect(); + + // Prepend the assignments to stmts_of_inlined_body + param_decls.append(&mut param_assigs); + param_decls.append(&mut stmts_of_inlined_body); + stmts_of_inlined_body = param_decls; + } + + let count_return: usize = stmts_of_inlined_body + .clone() + .iter() + .map(|stmt: &Stmt| Self::count_return_stmts(stmt)) + .sum(); + // The function is a void function, we safely ignore it. + if count_return == 0 { + return None; + } + // For simplicity, we currently only handle cases with one return statement. + assert_eq!(count_return, 1); + + // Make labels in the inlined body unique. + let suffix = format!("_{suffix_count}"); + stmts_of_inlined_body = stmts_of_inlined_body + .iter() + .map(|stmt| Self::append_suffix_to_stmt(stmt, &suffix)) + .collect(); + + // Replace all return stmts with symbol expressions. + let end_label: InternedString = + format!("KANI_quantifier_end{suffix}").into(); + let mut end_stmt = None; + stmts_of_inlined_body = stmts_of_inlined_body + .iter() + .map(|stmt| { + Self::rewrite_return_stmt_with_goto(stmt, &mut end_stmt, &end_label) + }) + .collect(); + stmts_of_inlined_body + .push(Stmt::skip(*expr.location()).with_label(end_label)); + stmts_of_inlined_body + .push(Stmt::code_expression(end_stmt.unwrap(), *expr.location())); + + // Recursively inline function calls in the function body. + let res = self.inline_function_calls_in_expr( + &Expr::statement_expression( + stmts_of_inlined_body, + expr.typ().clone(), + *expr.location(), + ), + visited_func_symbols, + suffix_count, + ); + + visited_func_symbols.remove(identifier); + + return res; + } else { + unreachable!() + } + } + } + // Recursively inline function calls in ops. + ExprValue::BinOp { op, lhs, rhs } => { + return Some( + self.inline_function_calls_in_expr(lhs, visited_func_symbols, suffix_count) + .unwrap() + .binop( + *op, + self.inline_function_calls_in_expr( + rhs, + visited_func_symbols, + suffix_count, + ) + .unwrap(), + ), + ); + } + ExprValue::StatementExpression { statements, location: _ } => { + let inlined_stmts: Vec = statements + .iter() + .filter_map(|stmt| { + self.inline_function_calls_in_stmt(stmt, visited_func_symbols, suffix_count) + }) + .collect(); + return Some(Expr::statement_expression( + inlined_stmts, + expr.typ().clone(), + *expr.location(), + )); + } + _ => {} + } + Some(expr.clone()) + } + + /// Recursively inline all function calls in `stmt`. + /// `visited_func_symbols` contain all function symbols in the stack. + /// `suffix_count` is used to make inlined labels unique. + fn inline_function_calls_in_stmt( + &self, + stmt: &Stmt, + visited_func_symbols: &mut HashSet, + suffix_count: &mut u16, + ) -> Option { + match stmt.body() { + StmtBody::Expression(expr) => self + .inline_function_calls_in_expr(expr, visited_func_symbols, suffix_count) + .map(|inlined_expr| Stmt::code_expression(inlined_expr, *expr.location())), + StmtBody::Assign { lhs, rhs } => self + .inline_function_calls_in_expr(rhs, visited_func_symbols, suffix_count) + .map(|inlined_rhs| Stmt::assign(lhs.clone(), inlined_rhs, *stmt.location())), + StmtBody::Block(stmts) => { + let inlined_block = stmts + .iter() + .filter_map(|s| { + self.inline_function_calls_in_stmt(s, visited_func_symbols, suffix_count) + }) + .collect(); + Some(Stmt::block(inlined_block, *stmt.location())) + } + StmtBody::Label { label, body } => { + match self.inline_function_calls_in_stmt(body, visited_func_symbols, suffix_count) { + None => Some(Stmt::skip(*stmt.location()).with_label(*label)), + Some(inlined_body) => Some(inlined_body.with_label(*label)), + } + } + StmtBody::Switch { control, cases, default } => { + // Inline function calls in the discriminant expression + let inlined_control = self + .inline_function_calls_in_expr(control, visited_func_symbols, suffix_count) + .unwrap_or_else(|| control.clone()); + + // Inline function calls in each case + let inlined_cases: Vec<_> = cases + .iter() + .map(|sc| { + let inlined_stmt = self + .inline_function_calls_in_stmt( + sc.body(), + visited_func_symbols, + suffix_count, + ) + .unwrap_or_else(|| sc.body().clone()); + SwitchCase::new(sc.case().clone(), inlined_stmt) + }) + .collect(); + + // Inline function calls in the default case, if it exists + let inlined_default = default.as_ref().map(|stmt| { + self.inline_function_calls_in_stmt(stmt, visited_func_symbols, suffix_count) + .unwrap_or_else(|| stmt.clone()) + }); + + // Construct the new switch statement + Some(Stmt::switch( + inlined_control, + inlined_cases, + inlined_default, + *stmt.location(), + )) + } + StmtBody::While { .. } | StmtBody::For { .. } => { + unimplemented!() + } + _ => Some(stmt.clone()), + } + } +} + /// Mutators impl GotocCtx<'_> { pub fn set_current_fn(&mut self, instance: Instance, body: &Body) { diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index e54a1fc4dd9e..cf614116a0c2 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -14,11 +14,13 @@ use crate::kani_middle::attributes; use crate::kani_middle::kani_functions::{KaniFunction, KaniHook}; use crate::unwrap_or_return_codegen_unimplemented_stmt; use cbmc::goto_program::CIntType; -use cbmc::goto_program::{BuiltinFn, Expr, Stmt, Type}; +use cbmc::goto_program::Symbol as GotoSymbol; +use cbmc::goto_program::{BuiltinFn, Expr, Location, Stmt, Type}; use rustc_middle::ty::TyCtxt; use rustc_smir::rustc_internal; use stable_mir::mir::mono::Instance; use stable_mir::mir::{BasicBlockIdx, Place}; +use stable_mir::ty::ClosureKind; use stable_mir::ty::RigidTy; use stable_mir::{CrateDef, ty::Span}; use std::collections::HashMap; @@ -761,10 +763,141 @@ impl GotocHook for LoopInvariantRegister { } } +struct Forall; +struct Exists; + +#[derive(Debug, Clone, Copy)] +enum QuantifierKind { + ForAll, + Exists, +} + +impl GotocHook for Forall { + fn hook_applies(&self, _tcx: TyCtxt, _instance: Instance) -> bool { + unreachable!("{UNEXPECTED_CALL}") + } + + fn handle( + &self, + gcx: &mut GotocCtx, + instance: Instance, + fargs: Vec, + assign_to: &Place, + target: Option, + span: Span, + ) -> Stmt { + handle_quantifier(gcx, instance, fargs, assign_to, target, span, QuantifierKind::ForAll) + } +} + +impl GotocHook for Exists { + fn hook_applies(&self, _tcx: TyCtxt, _instance: Instance) -> bool { + unreachable!("{UNEXPECTED_CALL}") + } + + fn handle( + &self, + gcx: &mut GotocCtx, + instance: Instance, + fargs: Vec, + assign_to: &Place, + target: Option, + span: Span, + ) -> Stmt { + handle_quantifier(gcx, instance, fargs, assign_to, target, span, QuantifierKind::Exists) + } +} + +fn handle_quantifier( + gcx: &mut GotocCtx, + instance: Instance, + fargs: Vec, + assign_to: &Place, + target: Option, + span: Span, + quantifier_kind: QuantifierKind, +) -> Stmt { + let loc = gcx.codegen_span_stable(span); + let target = target.unwrap(); + let lower_bound = &fargs[0]; + let upper_bound = &fargs[1]; + let closure_call_expr = find_closure_call_expr(&instance, gcx, loc) + .unwrap_or_else(|| unreachable!("Failed to find closure call expression")); + + let predicate = Expr::address_of(fargs[2].clone()); + + // Quantified variable. + let base_name = "kani_quantified_var".to_string(); + let mut counter = 0; + let mut unique_name = format!("{base_name}_{counter}"); + // Ensure the name is not already in the symbol table + while gcx.symbol_table.lookup(&unique_name).is_some() { + counter += 1; + unique_name = format!("{base_name}_{counter}"); + } + let new_variable_expr = { + let new_symbol = + GotoSymbol::variable(unique_name.clone(), unique_name, lower_bound.typ().clone(), loc); + gcx.symbol_table.insert(new_symbol.clone()); + new_symbol.to_expr() + }; + + let lower_bound_comparison = lower_bound.clone().le(new_variable_expr.clone()); + let upper_bound_comparison = new_variable_expr.clone().lt(upper_bound.clone()); + let range = lower_bound_comparison.and(upper_bound_comparison); + + let quantifier_expr = match quantifier_kind { + QuantifierKind::ForAll => { + let domain = range + .clone() + .implies(closure_call_expr.call(vec![predicate.clone(), new_variable_expr.clone()])) + .and(range.not().implies(Expr::bool_true())); + Expr::forall_expr(Type::Bool, new_variable_expr, domain) + } + QuantifierKind::Exists => { + let domain = range + .clone() + .and(closure_call_expr.call(vec![predicate.clone(), new_variable_expr.clone()])) + .and(range.not().implies(Expr::bool_false())); + Expr::exists_expr(Type::Bool, new_variable_expr, domain) + } + }; + + Stmt::block( + vec![ + unwrap_or_return_codegen_unimplemented_stmt!( + gcx, + gcx.codegen_place_stable(assign_to, loc) + ) + .goto_expr + .assign(quantifier_expr.cast_to(Type::CInteger(CIntType::Bool)), loc), + Stmt::goto(bb_label(target), loc), + ], + loc, + ) +} + +fn find_closure_call_expr(instance: &Instance, gcx: &mut GotocCtx, loc: Location) -> Option { + for arg in instance.args().0.iter() { + let arg_ty = arg.ty()?; + let kind = arg_ty.kind(); + let arg_kind = kind.rigid()?; + + if let RigidTy::Closure(def_id, args) = arg_kind { + let instance_closure = + Instance::resolve_closure(*def_id, args, ClosureKind::Fn).ok()?; + return Some(gcx.codegen_func_expr(instance_closure, loc)); + } + } + None +} + pub fn fn_hooks() -> GotocHooks { let kani_lib_hooks = [ (KaniHook::Assert, Rc::new(Assert) as Rc), (KaniHook::Assume, Rc::new(Assume)), + (KaniHook::Exists, Rc::new(Exists)), + (KaniHook::Forall, Rc::new(Forall)), (KaniHook::Panic, Rc::new(Panic)), (KaniHook::Check, Rc::new(Check)), (KaniHook::Cover, Rc::new(Cover)), diff --git a/kani-compiler/src/kani_middle/kani_functions.rs b/kani-compiler/src/kani_middle/kani_functions.rs index 85936041cc38..f1d064ed08eb 100644 --- a/kani-compiler/src/kani_middle/kani_functions.rs +++ b/kani-compiler/src/kani_middle/kani_functions.rs @@ -133,6 +133,10 @@ pub enum KaniHook { Check, #[strum(serialize = "CoverHook")] Cover, + #[strum(serialize = "ExistsHook")] + Exists, + #[strum(serialize = "ForallHook")] + Forall, // TODO: this is temporarily implemented as a hook, but should be implemented as an intrinsic #[strum(serialize = "FloatToIntInRangeHook")] FloatToIntInRange, diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index 36c76c1bab56..b0c27436227f 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -207,6 +207,34 @@ macro_rules! kani_intrinsics { assert!(cond, "{}", msg); } + #[macro_export] + macro_rules! forall { + (|$i:ident in ($lower_bound:expr, $upper_bound:expr)| $predicate:expr) => {{ + let lower_bound: usize = $lower_bound; + let upper_bound: usize = $upper_bound; + let predicate = |$i| $predicate; + kani::internal::kani_forall(lower_bound, upper_bound, predicate) + }}; + (|$i:ident | $predicate:expr) => {{ + let predicate = |$i| $predicate; + kani::internal::kani_forall(usize::MIN, usize::MAX, predicate) + }}; + } + + #[macro_export] + macro_rules! exists { + (|$i:ident in ($lower_bound:expr, $upper_bound:expr)| $predicate:expr) => {{ + let lower_bound: usize = $lower_bound; + let upper_bound: usize = $upper_bound; + let predicate = |$i| $predicate; + kani::internal::kani_exists(lower_bound, upper_bound, predicate) + }}; + (|$i:ident | $predicate:expr) => {{ + let predicate = |$i| $predicate; + kani::internal::kani_exists(usize::MIN, usize::MAX, predicate) + }}; + } + /// Creates a cover property with the specified condition and message. /// /// # Example: @@ -611,6 +639,24 @@ macro_rules! kani_intrinsics { pub(crate) const fn check(cond: bool, msg: &'static str) { assert!(cond, "{}", msg); } + + #[inline(never)] + #[kanitool::fn_marker = "ForallHook"] + pub fn kani_forall(lower_bound: T, upper_bound: T, predicate: F) -> bool + where + F: Fn(T) -> bool, + { + predicate(lower_bound) + } + + #[inline(never)] + #[kanitool::fn_marker = "ExistsHook"] + pub fn kani_exists(lower_bound: T, upper_bound: T, predicate: F) -> bool + where + F: Fn(T) -> bool, + { + predicate(lower_bound) + } } }; } diff --git a/rfc/src/rfcs/0010-quantifiers.md b/rfc/src/rfcs/0010-quantifiers.md index 07a5f7548974..9127834a4887 100644 --- a/rfc/src/rfcs/0010-quantifiers.md +++ b/rfc/src/rfcs/0010-quantifiers.md @@ -1,7 +1,7 @@ - **Feature Name:** Quantifiers - **Feature Request Issue:** [#2546](https://github.com/model-checking/kani/issues/2546) and [#836](https://github.com/model-checking/kani/issues/836) - **RFC PR:** [#](https://github.com/model-checking/kani/pull/) -- **Status:** Unstable +- **Status:** Under Review - **Version:** 1.0 ------------------- @@ -20,15 +20,15 @@ There are two primary quantifiers: the existential quantifier (∃) and the univ Rather than exhaustively listing all elements in a domain, quantifiers enable users to make statements about the entire domain at once. This compact representation is crucial when dealing with large or unbounded inputs. Quantifiers also facilitate abstraction and generalization of properties. Instead of specifying properties for specific instances, quantified properties can capture general patterns and behaviors that hold across different objects in a domain. Additionally, by replacing loops in the specification with quantifiers, Kani can encode the properties more efficiently within the specified bounds, making the verification process more manageable and computationally feasible. -This new feature doesn't introduce any breaking changes to users. It will only allow them to write properites using the existential (∃) and universal (∀) quantifiers. +This new feature doesn't introduce any breaking changes to users. It will only allow them to write properties using the existential (∃) and universal (∀) quantifiers. ## User Experience -We propose a syntax inspired by ["Pattern Types"](https://github.com/rust-lang/rust/pull/120131). The syntax of existential (i.e., `kani::exists`) and universal (i.e., `kani::forall`) quantifiers are: +The syntax of existential (i.e., `kani::exists`) and universal (i.e., `kani::forall`) quantifiers are: ```rust -kani::exists(|: [is ] | ) -kani::forall(|: [is ] | ) +kani::exists(|: [in ()] | ) +kani::forall(|: [in ()] | ) ``` If `` is not provided, we assume `` can range over all possible values of the given `` (i.e., syntactic sugar for full range `|: as .. |`). CBMC's SAT backend only supports bounded quantification under **constant** lower and upper bounds (for more details, see the [documentation for quantifiers in CBMC](https://diffblue.github.io/cbmc/contracts-quantifiers.html)). The SMT backend, on the other hand, supports arbitrary Boolean expressions. In any case, `` should not have side effects, as the purpose of quantifiers is to assert a condition over a domain of objects without altering the state. @@ -36,7 +36,6 @@ If `` is not provided, we assume `` can range over all possible Consider the following example adapted from the documentation for the [from_raw_parts](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.from_raw_parts) function: ```rust -use std::ptr; use std::mem; #[kani::proof] @@ -67,7 +66,6 @@ fn main() { Given the `v` vector has non-deterministic values, there are potential arithmetic overflows that might happen in the for loop. So we need to constrain all values of the array. We may also want to check all values of `rebuilt` after the operation. Without quantifiers, we might be tempted to use loops as follows: ```rust -use std::ptr; use std::mem; #[kani::proof] @@ -102,17 +100,23 @@ fn main() { } ``` -This, however, might unnecessary increase the complexity of the verication process. We can achieve the same effect using quantifiers as shown below. +This, however, might unnecessary increase the complexity of the verification process. We can achieve the same effect using quantifiers as shown below. ```rust -use std::ptr; use std::mem; #[kani::proof] fn main() { - let original_v = vec![kani::any::(); 3]; + let original_v = vec![kani::any::(); 3]; let v = original_v.clone(); - kani::assume(kani::forall(|i: usize is ..v.len() | v[i] < 5)); + let v_len = v.len(); + let v_ptr = v.as_ptr(); + let original_v_ptr = original_v.as_ptr(); + unsafe { + kani::assume( + kani::forall!(|i in (0,v_len) | *v_ptr.wrapping_byte_offset(4*i as isize) < 5), + ); + } // Prevent running `v`'s destructor so we are in complete control // of the allocation. @@ -127,11 +131,17 @@ fn main() { // Overwrite memory for i in 0..len { *p.add(i) += 1; + if i == 1 { + *p.add(i) = 0; + } } // Put everything back together into a Vec let rebuilt = Vec::from_raw_parts(p, len, cap); - assert!(kani::forall(|i: usize is ..len | rebuilt[i] == original_v[i]+1)); + let rebuilt_ptr = v.as_ptr(); + assert!( + kani::exists!(| i in (0, len) | *rebuilt_ptr.wrapping_byte_offset(4*i as isize) == original_v_ptr.wrapping_byte_offset(4*i as isize) + 1) + ); } } ``` @@ -139,14 +149,19 @@ fn main() { The same principle applies if we want to use the existential quantifier. ```rust -use std::ptr; use std::mem; #[kani::proof] fn main() { - let original_v = vec![kani::any::(); 3]; + let original_v = vec![kani::any::(); 3]; let v = original_v.clone(); - kani::assume(kani::forall(|i: usize is ..v.len() | v[i] < 5)); + let v_len = v.len(); + let v_ptr = v.as_ptr(); + unsafe { + kani::assume( + kani::forall!(|i in (0,v_len) | *v_ptr.wrapping_byte_offset(4*i as isize) < 5), + ); + } // Prevent running `v`'s destructor so we are in complete control // of the allocation. @@ -162,13 +177,16 @@ fn main() { for i in 0..len { *p.add(i) += 1; if i == 1 { - *p.add(i) = 0; + *p.add(i) = 0; } } // Put everything back together into a Vec let rebuilt = Vec::from_raw_parts(p, len, cap); - assert!(kani::exists(|i: usize is ..len | rebuilt[i] == 0)); + let rebuilt_ptr = v.as_ptr(); + assert!( + kani::exists!(| i in (0, len) | *rebuilt_ptr.wrapping_byte_offset(4*i as isize) == 0) + ); } } ``` @@ -181,6 +199,17 @@ The usage of quantifiers should be valid in any part of the Rust code analysed b Kani should have the same support that CBMC has for quantifiers. For more details, see [Quantifiers](https://github.com/diffblue/cbmc/blob/0a69a64e4481473d62496f9975730d24f194884a/doc/cprover-manual/contracts-quantifiers.md). +The implementation of quantifiers in Kani will be based on the following principle: + +- **Single expression without function calls**: CBMC's quantifiers only support + single expressions without function calls. This means that the CBMC expressions generated + from the quantifiers in Kani should be limited to a single expression without any + function calls. + +To achieve this, we will need to implement the function inlining pass in Kani. This + pass will inline all function calls in quantifiers before they are codegened to CBMC + expressions. This will ensure that the generated expressions are compliant with CBMC's + quantifier support. ## Open questions @@ -192,8 +221,7 @@ Kani should have the same support that CBMC has for quantifiers. For more detail interface is familiar to developers, but the code generation is tricky, as CBMC level quantifiers only allow certain kinds of expressions. This necessitates a rewrite of the `Fn` closure to a compliant expression. - - Which kind of expressions should be accepted as a "compliant expression"? - + - Which kind of expressions should be accepted as a "compliant expression"? ## Future possibilities diff --git a/tests/expected/quantifiers/assert_with_exists_fail.expected b/tests/expected/quantifiers/assert_with_exists_fail.expected new file mode 100644 index 000000000000..7c93e835e724 --- /dev/null +++ b/tests/expected/quantifiers/assert_with_exists_fail.expected @@ -0,0 +1,4 @@ +- Status: FAILURE\ +- Description: "assertion with exists"\ + +VERIFICATION:- FAILED diff --git a/tests/expected/quantifiers/assert_with_exists_fail.rs b/tests/expected/quantifiers/assert_with_exists_fail.rs new file mode 100644 index 000000000000..3e2c6cdb3781 --- /dev/null +++ b/tests/expected/quantifiers/assert_with_exists_fail.rs @@ -0,0 +1,9 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#[kani::proof] +fn exists_assert_harness() { + let j = kani::any(); + kani::assume(j > 1); + kani::assert(kani::exists!(|i in (3,5)| i < j ), "assertion with exists"); +} diff --git a/tests/expected/quantifiers/assert_with_exists_pass.expected b/tests/expected/quantifiers/assert_with_exists_pass.expected new file mode 100644 index 000000000000..33abfd607aaa --- /dev/null +++ b/tests/expected/quantifiers/assert_with_exists_pass.expected @@ -0,0 +1,4 @@ +- Status: SUCCESS\ +- Description: "assertion with exists"\ + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/quantifiers/assert_with_exists_pass.rs b/tests/expected/quantifiers/assert_with_exists_pass.rs new file mode 100644 index 000000000000..7d43d6117729 --- /dev/null +++ b/tests/expected/quantifiers/assert_with_exists_pass.rs @@ -0,0 +1,9 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#[kani::proof] +fn exists_assert_harness() { + let j = kani::any(); + kani::assume(j > 2); + kani::assert(kani::exists!(|i in (2,5)| i < j ), "assertion with exists"); +} diff --git a/tests/expected/quantifiers/assert_with_forall_fail.expected b/tests/expected/quantifiers/assert_with_forall_fail.expected new file mode 100644 index 000000000000..d8eaf5db2b9a --- /dev/null +++ b/tests/expected/quantifiers/assert_with_forall_fail.expected @@ -0,0 +1,5 @@ +- Status: FAILURE\ +- Description: "assertion with forall"\ + +VERIFICATION:- FAILED + diff --git a/tests/expected/quantifiers/assert_with_forall_fail.rs b/tests/expected/quantifiers/assert_with_forall_fail.rs new file mode 100644 index 000000000000..c9bb7bbf62dd --- /dev/null +++ b/tests/expected/quantifiers/assert_with_forall_fail.rs @@ -0,0 +1,9 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#[kani::proof] +fn forall_assert_harness() { + let j = kani::any(); + kani::assume(j > 3); + kani::assert(kani::forall!(|i in (2,5)| i < j ), "assertion with forall"); +} diff --git a/tests/expected/quantifiers/assert_with_forall_pass.expected b/tests/expected/quantifiers/assert_with_forall_pass.expected new file mode 100644 index 000000000000..d6f419825425 --- /dev/null +++ b/tests/expected/quantifiers/assert_with_forall_pass.expected @@ -0,0 +1,4 @@ +- Status: SUCCESS\ +- Description: "assertion with forall"\ + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/quantifiers/assert_with_forall_pass.rs b/tests/expected/quantifiers/assert_with_forall_pass.rs new file mode 100644 index 000000000000..5ebf73e7c0ee --- /dev/null +++ b/tests/expected/quantifiers/assert_with_forall_pass.rs @@ -0,0 +1,9 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#[kani::proof] +fn forall_assert_harness() { + let j = kani::any(); + kani::assume(j > 5); + kani::assert(kani::forall!(|i in (2,5)| i < j ), "assertion with forall"); +} diff --git a/tests/expected/quantifiers/assume_with_exists_fail.expected b/tests/expected/quantifiers/assume_with_exists_fail.expected new file mode 100644 index 000000000000..794397c227e8 --- /dev/null +++ b/tests/expected/quantifiers/assume_with_exists_fail.expected @@ -0,0 +1,4 @@ +- Status: FAILURE\ +- Description: "assume with exists"\ + +VERIFICATION:- FAILED diff --git a/tests/expected/quantifiers/assume_with_exists_fail.rs b/tests/expected/quantifiers/assume_with_exists_fail.rs new file mode 100644 index 000000000000..f6df36a6942d --- /dev/null +++ b/tests/expected/quantifiers/assume_with_exists_fail.rs @@ -0,0 +1,9 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#[kani::proof] +fn exists_assume_harness() { + let j = kani::any(); + kani::assume(kani::exists!(|i in (2,4)| i == j)); + kani::assert(j == 3, "assume with exists"); +} diff --git a/tests/expected/quantifiers/contracts_fail.expected b/tests/expected/quantifiers/contracts_fail.expected new file mode 100644 index 000000000000..1d67c28486c1 --- /dev/null +++ b/tests/expected/quantifiers/contracts_fail.expected @@ -0,0 +1,11 @@ +- Status: FAILURE\ +- Description: "|ret| +{ + unsafe{ + let ptr_x = xs.as_ptr(); let ptr_y = ys.as_ptr(); + kani::forall!(| k in (0, 8)| *ptr_x.wrapping_byte_offset(k as isize) + == *ptr_y.wrapping_byte_offset(k as isize)) + } +}" + +VERIFICATION:- FAILED diff --git a/tests/expected/quantifiers/contracts_fail.rs b/tests/expected/quantifiers/contracts_fail.rs new file mode 100644 index 000000000000..a1b87ebc8633 --- /dev/null +++ b/tests/expected/quantifiers/contracts_fail.rs @@ -0,0 +1,24 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +/// Copy only first 7 elements and left the last one unchanged. +#[kani::ensures(|ret| { unsafe{ + let ptr_x = xs.as_ptr(); + let ptr_y = ys.as_ptr(); + kani::forall!(| k in (0, 8)| *ptr_x.wrapping_byte_offset(k as isize) == *ptr_y.wrapping_byte_offset(k as isize))}})] +#[kani::modifies(ys)] +pub fn copy(xs: &mut [u8; 8], ys: &mut [u8; 8]) { + let mut i = 0; + while i < 7 { + ys[i] = xs[i]; + i = i + 1; + } +} + +#[kani::proof_for_contract(copy)] +fn copy_harness() { + let mut xs: [u8; 8] = kani::any(); + let mut ys: [u8; 8] = kani::any(); + copy(&mut xs, &mut ys); +} diff --git a/tests/kani/Quantifiers/array.rs b/tests/kani/Quantifiers/array.rs new file mode 100644 index 000000000000..b59eccd614fb --- /dev/null +++ b/tests/kani/Quantifiers/array.rs @@ -0,0 +1,33 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#[kani::proof] +fn vec_assert_forall_harness() { + let v = vec![10 as u8; 128]; + let ptr = v.as_ptr(); + unsafe { + kani::assert(kani::forall!(|i in (0,128)| *ptr.wrapping_byte_offset(i as isize) == 10), ""); + } +} + +#[kani::proof] +fn slice_assume_forall_harness() { + let arr: [u8; 8] = kani::any(); + let ptr = arr.as_ptr(); + unsafe { + kani::assume(kani::forall!(|i in (0,arr.len())| *ptr.wrapping_byte_offset(i as isize) < 8)); + } + kani::assert(arr[0] < 8, ""); +} + +#[kani::proof] +fn slice_assume_sorted_harness() { + let arr: [u8; 12] = kani::any(); + let ptr = arr.as_ptr(); + unsafe { + kani::assume( + kani::forall!(|i in (0,arr.len()-1)| *ptr.wrapping_byte_offset(i as isize) < *ptr.wrapping_byte_offset((i+1) as isize)), + ); + } + kani::assert(arr[0] < arr[1], ""); +} diff --git a/tests/kani/Quantifiers/contracts.rs b/tests/kani/Quantifiers/contracts.rs new file mode 100644 index 000000000000..d8e36c194774 --- /dev/null +++ b/tests/kani/Quantifiers/contracts.rs @@ -0,0 +1,43 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +#[kani::requires(i==0)] +#[kani::ensures(|ret| { + unsafe{ + let ptr = arr.as_ptr(); kani::forall!(| k in (0, 8)| *ptr.wrapping_byte_offset(k as isize) == 0)}})] +#[kani::modifies(arr)] +pub fn set_zero(arr: &mut [u8; 8], mut i: usize) -> usize { + while i < 8 { + arr[i] = 0; + i = i + 1; + } + i +} + +#[kani::proof_for_contract(set_zero)] +fn set_zero_harness() { + let mut arr: [u8; 8] = kani::any(); + let i: usize = 0; + let _j = set_zero(&mut arr, i); +} + +#[kani::ensures(|ret| { + unsafe{ + let ptr_x = xs.as_ptr(); + let ptr_y = ys.as_ptr(); kani::forall!(| k in (0, 8)| *ptr_x.wrapping_byte_offset(k as isize) == *ptr_y.wrapping_byte_offset(k as isize))}})] +#[kani::modifies(ys)] +pub fn copy(xs: &mut [u8; 8], ys: &mut [u8; 8]) { + let mut i = 0; + while i < 8 { + ys[i] = xs[i]; + i = i + 1; + } +} + +#[kani::proof_for_contract(copy)] +fn copy_harness() { + let mut xs: [u8; 8] = kani::any(); + let mut ys: [u8; 8] = kani::any(); + copy(&mut xs, &mut ys); +} diff --git a/tests/kani/Quantifiers/even.rs b/tests/kani/Quantifiers/even.rs new file mode 100644 index 000000000000..e7f74440d85a --- /dev/null +++ b/tests/kani/Quantifiers/even.rs @@ -0,0 +1,9 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#[kani::proof] +fn quantifier_even_harness() { + let j: usize = kani::any(); + kani::assume(j % 2 == 0 && j < 2000); + kani::assert(kani::exists!(|i in (0, 1000)| i + i == j), ""); +} diff --git a/tests/kani/Quantifiers/from_raw_part_fixme.rs b/tests/kani/Quantifiers/from_raw_part_fixme.rs new file mode 100644 index 000000000000..2f06161847bf --- /dev/null +++ b/tests/kani/Quantifiers/from_raw_part_fixme.rs @@ -0,0 +1,45 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! FIXME: + +use std::mem; + +#[kani::proof] +fn main() { + let original_v = vec![kani::any::(); 3]; + let v = original_v.clone(); + let v_len = v.len(); + let v_ptr = v.as_ptr(); + unsafe { + kani::assume( + kani::forall!(|i in (0,v_len) | *v_ptr.wrapping_byte_offset(4*i as isize) < 5), + ); + } + + // Prevent running `v`'s destructor so we are in complete control + // of the allocation. + let mut v = mem::ManuallyDrop::new(v); + + // Pull out the various important pieces of information about `v` + let p = v.as_mut_ptr(); + let len = v.len(); + let cap = v.capacity(); + + unsafe { + // Overwrite memory + for i in 0..len { + *p.add(i) += 1; + if i == 1 { + *p.add(i) = 0; + } + } + + // Put everything back together into a Vec + let rebuilt = Vec::from_raw_parts(p, len, cap); + let rebuilt_ptr = v.as_ptr(); + assert!( + kani::exists!(| i in (0, len) | *rebuilt_ptr.wrapping_byte_offset(4*i as isize) == 0) + ); + } +} diff --git a/tests/kani/Quantifiers/no_array.rs b/tests/kani/Quantifiers/no_array.rs new file mode 100644 index 000000000000..9681e4ccf0fa --- /dev/null +++ b/tests/kani/Quantifiers/no_array.rs @@ -0,0 +1,48 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#[kani::proof] +fn forall_assert_harness() { + let j = kani::any(); + kani::assume(j > 5); + kani::assert(kani::forall!(|i in (2,5)| i < j ), ""); +} + +#[kani::proof] +fn forall_assume_harness() { + let j = kani::any(); + kani::assume(kani::forall!(|i in (2,5)| i < j)); + kani::assert(j > 4, ""); +} + +fn comp(x: usize, y: usize) -> bool { + x > y +} + +#[kani::proof] +fn forall_function_harness() { + let j = kani::any(); + kani::assume(j > 5); + kani::assert(kani::forall!(|i in (2,5)| comp(j, i) ), ""); +} + +#[kani::proof] +fn exists_assert_harness() { + let j = kani::any(); + kani::assume(j > 2); + kani::assert(kani::exists!(|i in (2,5)| i < j ), ""); +} + +#[kani::proof] +fn exists_assume_harness() { + let j = kani::any(); + kani::assume(kani::exists!(|i in (2,4)| i == j)); + kani::assert(j == 3 || j == 2, ""); +} + +#[kani::proof] +fn exists_function_harness() { + let j = kani::any(); + kani::assume(j > 2); + kani::assert(kani::exists!(|i in (2,5)| comp(j, i) ), ""); +} From e232f169645f00e96c7c4942c197325f8d947e23 Mon Sep 17 00:00:00 2001 From: thanhnguyen-aws Date: Tue, 13 May 2025 17:03:16 -0700 Subject: [PATCH 140/161] Toolchain upgrade to nightly-2025-05-07 (#4070) This PR upgrades the toolchain to nightly-2025-05-07. Some wrong coverage expected tests are fixed. Relevant uptream PR: https://github.com/rust-lang/rust/commit/77a7ae4e9f coverage: Handle hole spans without dividing spans into buckets https://github.com/rust-lang/rust/commit/4d5a1acebf coverage: Only merge adjacent coverage spans General idea: The spans of BasicCoverageBlock are not merged automatically , so we have to do that from Kani side. Resolves #4063 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../codegen_cprover_gotoc/codegen/function.rs | 33 +++++++++++++++---- rust-toolchain.toml | 2 +- tests/coverage/abort/expected | 2 +- tests/coverage/assert_eq/expected | 2 +- tests/coverage/break/expected | 2 +- tests/coverage/debug-assert/expected | 2 +- tests/coverage/early-return/expected | 2 +- 7 files changed, 33 insertions(+), 12 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs index de17c4b33879..2cd693fa2256 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs @@ -258,6 +258,24 @@ pub mod rustc_smir { region_from_coverage(tcx, bcb, instance) } + pub fn merge_source_region(source_regions: Vec) -> SourceRegion { + let start_line = source_regions.iter().map(|sr| sr.start_line).min().unwrap(); + let start_col = source_regions + .iter() + .filter(|sr| sr.start_line == start_line) + .map(|sr| sr.start_col) + .min() + .unwrap(); + let end_line = source_regions.iter().map(|sr| sr.end_line).max().unwrap(); + let end_col = source_regions + .iter() + .filter(|sr| sr.end_line == end_line) + .map(|sr| sr.end_col) + .max() + .unwrap(); + SourceRegion { start_line, start_col, end_line, end_col } + } + /// Retrieves the `SourceRegion` associated with a `BasicCoverageBlock` object. /// /// Note: This function could be in the internal `rustc` impl for `Coverage`. @@ -269,22 +287,25 @@ pub mod rustc_smir { // We need to pull the coverage info from the internal MIR instance. let instance_def = rustc_smir::rustc_internal::internal(tcx, instance.def.def_id()); let body = tcx.instance_mir(rustc_middle::ty::InstanceKind::Item(instance_def)); - + let filename = rustc_internal::stable(body.span).get_filename(); // Some functions, like `std` ones, may not have coverage info attached // to them because they have been compiled without coverage flags. if let Some(cov_info) = &body.function_coverage_info { // Iterate over the coverage mappings and match with the coverage term. + let mut source_regions: Vec = Vec::new(); for mapping in &cov_info.mappings { let Code { bcb } = mapping.kind else { unreachable!() }; let source_map = tcx.sess.source_map(); let file = source_map.lookup_source_file(mapping.span.lo()); - if bcb == coverage { - return Some(( - make_source_region(source_map, &file, mapping.span).unwrap(), - rustc_internal::stable(mapping.span).get_filename(), - )); + if bcb == coverage + && let Some(source_region) = make_source_region(source_map, &file, mapping.span) + { + source_regions.push(source_region); } } + if !source_regions.is_empty() { + return Some((merge_source_region(source_regions), filename)); + } } None } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 0fb26468fadc..f1eb90721eb6 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-05-06" +channel = "nightly-2025-05-07" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/coverage/abort/expected b/tests/coverage/abort/expected index 0334841a880e..ae63575af3bc 100644 --- a/tests/coverage/abort/expected +++ b/tests/coverage/abort/expected @@ -8,7 +8,7 @@ 8| | #[kani::proof]\ 9| 1| fn main() {\ 10| 1| for i in 0..4 {\ - 11| | if i == 1 {\ + 11| 1| if i == 1 {\ 12| | // This comes first and it should be reachable.\ 13| 1| process::abort();\ 14| 1| }\ diff --git a/tests/coverage/assert_eq/expected b/tests/coverage/assert_eq/expected index 0cc1e01fbca9..8665bb8bcbeb 100644 --- a/tests/coverage/assert_eq/expected +++ b/tests/coverage/assert_eq/expected @@ -5,7 +5,7 @@ 5| 1| fn main() {\ 6| 1| let x: i32 = kani::any();\ 7| 1| let y = if x > 10 { 15 } else { 33 };\ - 8| 0| if y > 50 ```{'''\ + 8| 1| if y > 50 ```{'''\ 9| 0| ``` assert_eq!(y, 55);'''\ 10| 1| ``` }'''\ 11| | }\ diff --git a/tests/coverage/break/expected b/tests/coverage/break/expected index e1570030f6ae..25d192336b7c 100644 --- a/tests/coverage/break/expected +++ b/tests/coverage/break/expected @@ -3,7 +3,7 @@ 3| | \ 4| 1| fn find_positive(nums: &[i32]) -> Option {\ 5| 1| for &num in nums {\ - 6| | if num > 0 {\ + 6| 1| if num > 0 {\ 7| 1| return Some(num);\ 8| 1| } \ 9| | }\ diff --git a/tests/coverage/debug-assert/expected b/tests/coverage/debug-assert/expected index 82ad7c992ca3..d36fc11843d0 100644 --- a/tests/coverage/debug-assert/expected +++ b/tests/coverage/debug-assert/expected @@ -9,7 +9,7 @@ 9| | #[kani::proof]\ 10| 1| fn main() {\ 11| 1| for i in 0..4 {\ - 12| 0| debug_assert!(i > 0, ```"This should fail and stop the execution"''');\ + 12| 1| debug_assert!(i > 0, ```"This should fail and stop the execution"''');\ 13| 0| ```assert!(i == 0''', ```"This should be unreachable"''');\ 14| | }\ 15| | }\ diff --git a/tests/coverage/early-return/expected b/tests/coverage/early-return/expected index efdd71ee91f0..4df1f1922603 100644 --- a/tests/coverage/early-return/expected +++ b/tests/coverage/early-return/expected @@ -3,7 +3,7 @@ 3| | \ 4| 1| fn find_index(nums: &[i32], target: i32) -> Option {\ 5| 1| for (index, &num) in nums.iter().enumerate() {\ - 6| | if num == target {\ + 6| 1| if num == target {\ 7| 1| return Some(index);\ 8| 1| } \ 9| | }\ From b6259a21d656c32d7034b50fca1c479fe286cf61 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 08:31:05 +0300 Subject: [PATCH 141/161] Automatic toolchain upgrade to nightly-2025-05-08 (#4071) Update Rust toolchain from nightly-2025-05-07 to nightly-2025-05-08 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f1eb90721eb6..152f16e641ce 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-05-07" +channel = "nightly-2025-05-08" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 2227614c99bf75a08ea14b23ad5ad42bfbac117a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 09:57:08 -0400 Subject: [PATCH 142/161] Automatic toolchain upgrade to nightly-2025-05-09 (#4072) Update Rust toolchain from nightly-2025-05-08 to nightly-2025-05-09 without any other source changes. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 152f16e641ce..44ee040f721c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-05-08" +channel = "nightly-2025-05-09" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 85b6989a6a6596c4b8b8ef6127d1fdd4de1e509a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 11:40:57 -0700 Subject: [PATCH 143/161] Automatic toolchain upgrade to nightly-2025-05-10 (#4073) Update Rust toolchain from nightly-2025-05-09 to nightly-2025-05-10 without any other source changes. Co-authored-by: carolynzech <71352687+carolynzech@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 44ee040f721c..e5ba2f83b399 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-05-09" +channel = "nightly-2025-05-10" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From b9f05def53fcd1d335a114899ad2614ba7761272 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Wed, 14 May 2025 23:17:08 -0400 Subject: [PATCH 144/161] Clippy/Stylistic Fixes (#4074) Various style nits I stumbled across while looking at our codegen code more closely. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .github/workflows/format-check.yml | 2 +- cprover_bindings/src/goto_program/typ.rs | 71 ------------------- .../codegen_cprover_gotoc/codegen/assert.rs | 1 - kani-compiler/src/kani_middle/resolve.rs | 4 +- .../src/kani_middle/transform/body.rs | 5 +- 5 files changed, 5 insertions(+), 78 deletions(-) diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml index a764132034e9..16afb80a1b5e 100644 --- a/.github/workflows/format-check.yml +++ b/.github/workflows/format-check.yml @@ -47,7 +47,7 @@ jobs: - name: 'Run Clippy' run: | - cargo clippy --workspace -- -D warnings + cargo clippy --workspace --tests -- -D warnings RUSTFLAGS="--cfg=kani_sysroot" cargo clippy --workspace -- -D warnings - name: 'Print Clippy Statistics' diff --git a/cprover_bindings/src/goto_program/typ.rs b/cprover_bindings/src/goto_program/typ.rs index 5a0ad76b81c6..df1503b60201 100644 --- a/cprover_bindings/src/goto_program/typ.rs +++ b/cprover_bindings/src/goto_program/typ.rs @@ -1427,69 +1427,6 @@ impl Type { } types } - - /// Generate a string which uniquely identifies the given type - /// while also being a valid variable/funcion name - pub fn to_identifier(&self) -> String { - // Use String instead of InternedString, since we don't want to intern temporaries. - match self { - Type::Array { typ, size } => { - format!("array_of_{size}_{}", typ.to_identifier()) - } - Type::Bool => "bool".to_string(), - Type::CBitField { width, typ } => { - format!("cbitfield_of_{width}_{}", typ.to_identifier()) - } - Type::CInteger(int_kind) => format!("c_int_{int_kind:?}"), - // e.g. `int my_func(double x, float_y) {` - // => "code_from_double_float_to_int" - Type::Code { parameters, return_type } => { - let parameter_string = parameters - .iter() - .map(|param| param.typ().to_identifier()) - .collect::>() - .join("_"); - let return_string = return_type.to_identifier(); - format!("code_from_{parameter_string}_to_{return_string}") - } - Type::Constructor => "constructor".to_string(), - Type::Double => "double".to_string(), - Type::Empty => "empty".to_string(), - Type::FlexibleArray { typ } => format!("flexarray_of_{}", typ.to_identifier()), - Type::Float => "float".to_string(), - Type::Float16 => "float16".to_string(), - Type::Float128 => "float128".to_string(), - Type::IncompleteStruct { tag } => tag.to_string(), - Type::IncompleteUnion { tag } => tag.to_string(), - Type::InfiniteArray { typ } => { - format!("infinite_array_of_{}", typ.to_identifier()) - } - Type::Integer => "integer".to_string(), - Type::Pointer { typ } => format!("pointer_to_{}", typ.to_identifier()), - Type::Signedbv { width } => format!("signed_bv_{width}"), - Type::Struct { tag, .. } => format!("struct_{tag}"), - Type::StructTag(tag) => format!("struct_tag_{tag}"), - Type::TypeDef { name: tag, .. } => format!("type_def_{tag}"), - Type::Union { tag, .. } => format!("union_{tag}"), - Type::UnionTag(tag) => format!("union_tag_{tag}"), - Type::Unsignedbv { width } => format!("unsigned_bv_{width}"), - // e.g. `int my_func(double x, float_y, ..) {` - // => "variadic_code_from_double_float_to_int" - Type::VariadicCode { parameters, return_type } => { - let parameter_string = parameters - .iter() - .map(|param| param.typ().to_identifier()) - .collect::>() - .join("_"); - let return_string = return_type.to_identifier(); - format!("variadic_code_from_{parameter_string}_to_{return_string}") - } - Type::Vector { typ, size } => { - let typ = typ.to_identifier(); - format!("vec_of_{size}_{typ}") - } - } - } } #[cfg(test)] @@ -1509,14 +1446,6 @@ mod type_tests { assert_eq!(type_def.type_name().unwrap().to_string(), format!("tag-{NAME}")); } - #[test] - fn check_typedef_identifier() { - let type_def = Bool.to_typedef(NAME); - let id = type_def.to_identifier(); - assert!(id.ends_with(NAME)); - assert!(id.starts_with("type_def")); - } - #[test] fn check_typedef_create() { assert!(matches!(Bool.to_typedef(NAME), TypeDef { .. })); diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs index 2392a801809f..b82a0c108424 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs @@ -101,7 +101,6 @@ pub enum PropertyClass { Unreachable, } -#[allow(dead_code)] impl PropertyClass { pub fn as_str(&self) -> &str { self.as_ref() diff --git a/kani-compiler/src/kani_middle/resolve.rs b/kani-compiler/src/kani_middle/resolve.rs index 9b46eb2e7d09..060f68ac88f7 100644 --- a/kani-compiler/src/kani_middle/resolve.rs +++ b/kani-compiler/src/kani_middle/resolve.rs @@ -747,7 +747,7 @@ mod tests { fn length_one_item_prefix() { let generic_args = "::"; let name = "unchecked_add"; - let item_path = format!("NonZero{}::{}", generic_args, name); + let item_path = format!("NonZero{generic_args}::{name}"); assert!(last_two_items_of_path_match(&item_path, generic_args, name)) } @@ -755,7 +755,7 @@ mod tests { fn length_three_item_prefix() { let generic_args = "::"; let name = "unchecked_add"; - let item_path = format!("core::num::NonZero{}::{}", generic_args, name); + let item_path = format!("core::num::NonZero{generic_args}::{name}"); assert!(last_two_items_of_path_match(&item_path, generic_args, name)) } diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index 6f7b59041d12..60b99e5cf13d 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // //! Utility functions that allow us to modify a function body. +//! +//! TODO: We should not need this code anymore now that https://github.com/rust-lang/rust/pull/138536 is merged use crate::kani_middle::kani_functions::KaniHook; use crate::kani_queries::QueryDb; @@ -54,12 +56,10 @@ impl MutableBody { &self.locals } - #[allow(dead_code)] pub fn arg_count(&self) -> usize { self.arg_count } - #[allow(dead_code)] pub fn var_debug_info(&self) -> &Vec { &self.var_debug_info } @@ -328,7 +328,6 @@ impl MutableBody { /// `InsertPosition` is `InsertPosition::Before`, `source` will point to the same instruction as /// before. If `InsertPosition` is `InsertPosition::After`, `source` will point to the /// terminator of the newly inserted basic block. - #[allow(dead_code)] pub fn insert_bb( &mut self, mut bb: BasicBlock, From d33b962bc1f269e8a783167f6c6282125a98e8e5 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Wed, 14 May 2025 20:32:18 -0700 Subject: [PATCH 145/161] Upgrade toolchain to 2025-05-14 (#4076) By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. Co-authored-by: Carolyn Zech --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e5ba2f83b399..e8a4287eb3f8 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-05-10" +channel = "nightly-2025-05-14" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 1f36f69bead3f081c3a44b39cb00ef2ddf05be64 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Wed, 14 May 2025 23:59:30 -0400 Subject: [PATCH 146/161] Autoharness argument validation: only error on `--quiet` if `--list` was passed (#4069) The autoharness subcommand doesn't support the `--pretty` format with `--quiet` when running "autoharness --list," but we should only error if `--list` was actually passed. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- kani-driver/src/args/autoharness_args.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kani-driver/src/args/autoharness_args.rs b/kani-driver/src/args/autoharness_args.rs index c5ddd25302fb..8b7b66c4cd7c 100644 --- a/kani-driver/src/args/autoharness_args.rs +++ b/kani-driver/src/args/autoharness_args.rs @@ -94,7 +94,8 @@ impl ValidateArgs for CargoAutoharnessArgs { )); } - if self.common_autoharness_args.format == Format::Pretty + if self.common_autoharness_args.list + && self.common_autoharness_args.format == Format::Pretty && self.verify_opts.common_args.quiet { return Err(Error::raw( @@ -141,7 +142,8 @@ impl ValidateArgs for StandaloneAutoharnessArgs { )); } - if self.common_autoharness_args.format == Format::Pretty + if self.common_autoharness_args.list + && self.common_autoharness_args.format == Format::Pretty && self.verify_opts.common_args.quiet { return Err(Error::raw( From 526164378b67aa78c9c8805e455fa35226552ea2 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Fri, 16 May 2025 13:08:13 -0700 Subject: [PATCH 147/161] Upgrade Rust toolchain to 2025-05-16 (#4080) Resolves #4078 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e8a4287eb3f8..afa4f4a0bbb9 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-05-14" +channel = "nightly-2025-05-16" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 6ddcabd873e29a821f794430abd808c9b08110db Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 17 May 2025 08:16:53 +0300 Subject: [PATCH 148/161] Automatic toolchain upgrade to nightly-2025-05-17 (#4081) Update Rust toolchain from nightly-2025-05-16 to nightly-2025-05-17 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index afa4f4a0bbb9..e34b80b79f4f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-05-16" +channel = "nightly-2025-05-17" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 8b586cd8983ad6ce7ad5611814209842ecf0bf47 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Sat, 17 May 2025 14:05:36 -0700 Subject: [PATCH 149/161] Add setup scripts for Ubuntu 20.04 (#4082) Background: 1. Kani currently cannot be installed using `cargo install kani-verifier` on Ubuntu 20.04 because of the older glibc version, and thus needs to be built from source. 2. The `scripts/setup/ubuntu-20.04` directory is a symlink to `scripts/setup/ubuntu` which is incorrect because the installation scripts in the `ubuntu` directory assume the existence of a deb package for CBMC which is not the case for Ubuntu 20.04. To make it easier for users to build from source on Ubuntu 20.04, this PR adds the necessary setup scripts under a new `scripts/setup/ubuntu-20.04` directory. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- docs/src/build-from-source.md | 1 + scripts/setup/ubuntu-20.04 | 1 - scripts/setup/ubuntu-20.04/install_cbmc.sh | 32 +++++++++++++++ scripts/setup/ubuntu-20.04/install_deps.sh | 40 +++++++++++++++++++ .../setup/ubuntu-20.04/install_doc_deps.sh | 8 ++++ 5 files changed, 81 insertions(+), 1 deletion(-) delete mode 120000 scripts/setup/ubuntu-20.04 create mode 100755 scripts/setup/ubuntu-20.04/install_cbmc.sh create mode 100755 scripts/setup/ubuntu-20.04/install_deps.sh create mode 100755 scripts/setup/ubuntu-20.04/install_doc_deps.sh diff --git a/docs/src/build-from-source.md b/docs/src/build-from-source.md index c3d7ecfd4b9a..e7e43b07c6c8 100644 --- a/docs/src/build-from-source.md +++ b/docs/src/build-from-source.md @@ -27,6 +27,7 @@ is following our CI scripts: git clone https://github.com/model-checking/kani.git cd kani git submodule update --init + # For Ubuntu 20.04, use: `./scripts/setup/ubuntu-20.04/install_deps.sh` ./scripts/setup/ubuntu/install_deps.sh # If you haven't already (or from https://rustup.rs/): ./scripts/setup/install_rustup.sh diff --git a/scripts/setup/ubuntu-20.04 b/scripts/setup/ubuntu-20.04 deleted file mode 120000 index 7d13753d73e8..000000000000 --- a/scripts/setup/ubuntu-20.04 +++ /dev/null @@ -1 +0,0 @@ -ubuntu \ No newline at end of file diff --git a/scripts/setup/ubuntu-20.04/install_cbmc.sh b/scripts/setup/ubuntu-20.04/install_cbmc.sh new file mode 100755 index 000000000000..f9744f51f61f --- /dev/null +++ b/scripts/setup/ubuntu-20.04/install_cbmc.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +set -eu + +# Source kani-dependencies to get CBMC_VERSION +source kani-dependencies + +if [ -z "${CBMC_VERSION:-}" ]; then + echo "$0: Error: CBMC_VERSION is not specified" + exit 1 +fi + +# Binaries are not released for Ubuntu 20.04, so build from source +WORK_DIR=$(mktemp -d) +git clone \ + --branch cbmc-${CBMC_VERSION} --depth 1 \ + https://github.com/diffblue/cbmc \ + "${WORK_DIR}" + +pushd "${WORK_DIR}" + +cmake -S . -Bbuild -DWITH_JBMC=OFF -Dsat_impl="minisat2;cadical" \ + -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 \ + -DCMAKE_CXX_STANDARD_LIBRARIES=-lstdc++fs \ + -DCMAKE_CXX_FLAGS=-Wno-error=register +cmake --build build -- -j$(nproc) +sudo make -C build install + +popd +rm -rf "${WORK_DIR}" diff --git a/scripts/setup/ubuntu-20.04/install_deps.sh b/scripts/setup/ubuntu-20.04/install_deps.sh new file mode 100755 index 000000000000..d9e272d18a84 --- /dev/null +++ b/scripts/setup/ubuntu-20.04/install_deps.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +set -eu + +# Dependencies. +DEPS=( + bison + cmake + curl + flex + # CBMC needs g++-10 which doesn't come by default on Ubuntu 20.04 + g++-10 + gcc + git + gpg-agent + make + patch + wget + zlib1g + zlib1g-dev +) + +set -x + +# Github promises weekly build image updates, but recommends running +# `sudo apt-get update` before installing packages in case the `apt` +# index is stale. This prevents package installation failures. +# https://docs.github.com/en/actions/using-github-hosted-runners/customizing-github-hosted-runners#installing-software-on-ubuntu-runners +sudo apt-get --yes update + +sudo DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends --yes "${DEPS[@]}" + +# Get the directory containing this script +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +${SCRIPT_DIR}/install_cbmc.sh +# The Kissat installation script is platform-independent, so is placed one level up +${SCRIPT_DIR}/../install_kissat.sh diff --git a/scripts/setup/ubuntu-20.04/install_doc_deps.sh b/scripts/setup/ubuntu-20.04/install_doc_deps.sh new file mode 100755 index 000000000000..e3f4dd2c0d74 --- /dev/null +++ b/scripts/setup/ubuntu-20.04/install_doc_deps.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +set -eux + +cargo install mdbook-graphviz +DEBIAN_FRONTEND=noninteractive sudo apt-get install --no-install-recommends --yes graphviz From cfb9aaf17830d7ec339ad304f63a916380e41958 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 18 May 2025 09:35:13 +0200 Subject: [PATCH 150/161] Automatic toolchain upgrade to nightly-2025-05-18 (#4083) Update Rust toolchain from nightly-2025-05-17 to nightly-2025-05-18 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e34b80b79f4f..2d153f324ca1 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-05-17" +channel = "nightly-2025-05-18" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From d15a0439319bc2c4169db4b885f90aa656279fc5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 07:41:00 +0200 Subject: [PATCH 151/161] Automatic cargo update to 2025-05-19 (#4086) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 378eb56d5477..fb56c611dfb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,9 +150,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "brownstone" @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.22" +version = "1.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" +checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" dependencies = [ "shlex", ] @@ -590,9 +590,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", "windows-sys 0.59.0", From 5d76510f8881153292124176d02300e6c13c2ffa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 07:42:04 +0200 Subject: [PATCH 152/161] Automatic toolchain upgrade to nightly-2025-05-19 (#4085) Update Rust toolchain from nightly-2025-05-18 to nightly-2025-05-19 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 2d153f324ca1..157c6c3e290b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-05-18" +channel = "nightly-2025-05-19" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From c33df196f32484bde4b8b31fde3abe4387fe2f44 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 09:43:28 -0400 Subject: [PATCH 153/161] Automatic toolchain upgrade to nightly-2025-05-20 (#4091) Update Rust toolchain from nightly-2025-05-19 to nightly-2025-05-20 without any other source changes. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 157c6c3e290b..00d460192bcc 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-05-19" +channel = "nightly-2025-05-20" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 92bc45da85ff70edb5a341f5566f150721150247 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 09:46:24 -0400 Subject: [PATCH 154/161] Bump tests/perf/s2n-quic from `5f323b7` to `22434aa` (#4089) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `5f323b7` to `22434aa`.
Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/perf/s2n-quic | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 5f323b771557..22434aa54178 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 5f323b77155766d3878755e5de48972df4b47344 +Subproject commit 22434aa541781e42924bcf9856cd435250150344 From 0b68df0b8ed585b4bae9d6c07db64875137f1ae7 Mon Sep 17 00:00:00 2001 From: thanhnguyen-aws Date: Wed, 21 May 2025 11:32:16 -0700 Subject: [PATCH 155/161] Fix the error that Kani panics when there is no external parameter in quantifier's closure. (#4088) Previously, if we run Kani on the following harness: ```Rust #[kani::proof] fn test() { let quan = kani::forall!(|j in (0, 100)| j < 100); assert!(quan); } ``` it will panic: ``` thread 'rustc' panicked at kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs:827:43: index out of bounds: the len is 2 but the index is 2 ``` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../codegen/statement.rs | 40 ++++++++++++++++++- .../codegen_cprover_gotoc/overrides/hooks.rs | 9 ++++- ...ntifier_with_no_external_variable.expected | 5 +++ .../quantifier_with_no_external_variable.rs | 10 +++++ 4 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 tests/expected/quantifiers/quantifier_with_no_external_variable.expected create mode 100644 tests/expected/quantifiers/quantifier_with_no_external_variable.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 19ad12b307b3..3fb7b58ca91c 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -12,6 +12,7 @@ use rustc_abi::{FieldsShape, Primitive, TagEncoding, Variants}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{List, TypingEnv}; use rustc_smir::rustc_internal; +use stable_mir::CrateDef; use stable_mir::abi::{ArgAbi, FnAbi, PassMode}; use stable_mir::mir::mono::{Instance, InstanceKind}; use stable_mir::mir::{ @@ -567,12 +568,41 @@ impl GotocCtx<'_> { /// Generate Goto-C for each argument to a function call. /// /// N.B. public only because instrinsics use this directly, too. - pub(crate) fn codegen_funcall_args(&mut self, fn_abi: &FnAbi, args: &[Operand]) -> Vec { + pub(crate) fn codegen_funcall_args_for_quantifiers( + &mut self, + fn_abi: &FnAbi, + args: &[Operand], + ) -> Vec { let fargs: Vec = args .iter() .enumerate() .filter_map(|(i, op)| { // Functions that require caller info will have an extra parameter. + let arg_abi = &fn_abi.args.get(i); + let ty = self.operand_ty_stable(op); + if ty.kind().is_bool() { + Some(self.codegen_operand_stable(op).cast_to(Type::c_bool())) + } else if ty.kind().is_closure() + || (arg_abi.is_none_or(|abi| abi.mode != PassMode::Ignore)) + { + Some(self.codegen_operand_stable(op)) + } else { + None + } + }) + .collect(); + debug!(?fargs, args_abi=?fn_abi.args, "codegen_funcall_args"); + fargs + } + + /// Generate Goto-C for each argument to a function call. + /// + /// N.B. public only because instrinsics use this directly, too. + pub(crate) fn codegen_funcall_args(&mut self, fn_abi: &FnAbi, args: &[Operand]) -> Vec { + let fargs: Vec = args + .iter() + .enumerate() + .filter_map(|(i, op)| { let arg_abi = &fn_abi.args.get(i); let ty = self.operand_ty_stable(op); if ty.kind().is_bool() { @@ -639,7 +669,13 @@ impl GotocCtx<'_> { let mut fargs = if args.is_empty() || fn_def.fn_sig().unwrap().value.abi != Abi::RustCall { - self.codegen_funcall_args(&fn_abi, args) + if instance.def.name() == "kani::internal::kani_forall" + || (instance.def.name() == "kani::internal::kani_exists") + { + self.codegen_funcall_args_for_quantifiers(&fn_abi, args) + } else { + self.codegen_funcall_args(&fn_abi, args) + } } else { let (untupled, first_args) = args.split_last().unwrap(); let mut fargs = self.codegen_funcall_args(&fn_abi, first_args); diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index cf614116a0c2..6c6319baf4eb 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -823,8 +823,13 @@ fn handle_quantifier( let upper_bound = &fargs[1]; let closure_call_expr = find_closure_call_expr(&instance, gcx, loc) .unwrap_or_else(|| unreachable!("Failed to find closure call expression")); - - let predicate = Expr::address_of(fargs[2].clone()); + let closure_arg = fargs[2].clone(); + let predicate = if closure_arg.is_symbol() { + Expr::address_of(closure_arg) + } else { + let predicate_ty = fargs[2].typ().clone().to_pointer(); + Expr::nondet(predicate_ty) + }; // Quantified variable. let base_name = "kani_quantified_var".to_string(); diff --git a/tests/expected/quantifiers/quantifier_with_no_external_variable.expected b/tests/expected/quantifiers/quantifier_with_no_external_variable.expected new file mode 100644 index 000000000000..0c27dc76ad41 --- /dev/null +++ b/tests/expected/quantifiers/quantifier_with_no_external_variable.expected @@ -0,0 +1,5 @@ +- Status: SUCCESS\ +- Description: "assertion failed: quan" + +VERIFICATION:- SUCCESSFUL + diff --git a/tests/expected/quantifiers/quantifier_with_no_external_variable.rs b/tests/expected/quantifiers/quantifier_with_no_external_variable.rs new file mode 100644 index 000000000000..dabf4d9f2137 --- /dev/null +++ b/tests/expected/quantifiers/quantifier_with_no_external_variable.rs @@ -0,0 +1,10 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +/// Quantifier with no external variable in the closure + +#[kani::proof] +fn test() { + let quan = kani::exists!(|j in (0, 100)| j == 0); + assert!(quan); +} From 61380f12315304f31a9f32011a976eceb4b0de5c Mon Sep 17 00:00:00 2001 From: Zyad Hassan Date: Thu, 26 Jun 2025 16:37:24 -0700 Subject: [PATCH 156/161] Upgrade stable branch to Rust 1.88.0 --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 00d460192bcc..80e86e534993 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2025-05-20" +channel = "1.88.0" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 3843913a3f668ccd5e28ab20aa79c7a1b6e24a7d Mon Sep 17 00:00:00 2001 From: Zyad Hassan Date: Thu, 26 Jun 2025 16:58:16 -0700 Subject: [PATCH 157/161] Fix llbc backend --- kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs b/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs index 219bc48c4d91..546a3aa4f127 100644 --- a/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs +++ b/kani-compiler/src/codegen_aeneas_llbc/mir_to_ullbc/mod.rs @@ -813,7 +813,7 @@ impl<'a, 'tcx> Context<'a, 'tcx> { let disambiguator = CharonDisambiguator::new(data.disambiguator as usize); use rustc_hir::definitions::DefPathData; match &data.data { - DefPathData::TypeNs(Some(symbol)) => { + DefPathData::TypeNs(symbol) => { error_assert!(self, span, data.disambiguator == 0); // Sanity check name.push(CharonPathElem::Ident(symbol.to_string(), disambiguator)); } @@ -956,7 +956,7 @@ impl<'a, 'tcx> Context<'a, 'tcx> { let disambiguator = CharonDisambiguator::new(data.disambiguator as usize); use rustc_hir::definitions::DefPathData; match &data.data { - DefPathData::TypeNs(Some(symbol)) => { + DefPathData::TypeNs(symbol) => { error_assert!(self, span, data.disambiguator == 0); // Sanity check name.push(CharonPathElem::Ident(symbol.to_string(), disambiguator)); } @@ -1063,7 +1063,7 @@ impl<'a, 'tcx> Context<'a, 'tcx> { let disambiguator = CharonDisambiguator::new(data.disambiguator as usize); use rustc_hir::definitions::DefPathData; match &data.data { - DefPathData::TypeNs(Some(symbol)) => { + DefPathData::TypeNs(symbol) => { error_assert!(self, span, data.disambiguator == 0); // Sanity check name.push(CharonPathElem::Ident(symbol.to_string(), disambiguator)); } From 5dd075ccc9f37ec6b3cceaced01aec8165703641 Mon Sep 17 00:00:00 2001 From: Zyad Hassan Date: Thu, 26 Jun 2025 16:59:10 -0700 Subject: [PATCH 158/161] Fix typo in labeler.yml --- .github/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 1195ee4a14d2..c80b7492435b 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -8,6 +8,6 @@ Z-BenchCI: - any: - changed-files: - - any-glob-to-any-file: ['kani-compiler/**', 'kani-driver/src/call-*', 'cprover_bindings/**', 'library/**'] + - any-glob-to-any-file: ['kani-compiler/**', 'kani-driver/src/call_*', 'cprover_bindings/**', 'library/**'] - any-glob-to-any-file: ['rust-toolchain.toml', 'Cargo.lock'] - any-glob-to-any-file: ['kani-dependencies'] From 8f4633dd9c0ca2666e89576e904d2c11d343a58c Mon Sep 17 00:00:00 2001 From: Zyad Hassan Date: Thu, 26 Jun 2025 17:03:30 -0700 Subject: [PATCH 159/161] Add write permissions to issues --- .github/workflows/extra_jobs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/extra_jobs.yml b/.github/workflows/extra_jobs.yml index ddf242ba3956..a27a07433fc4 100644 --- a/.github/workflows/extra_jobs.yml +++ b/.github/workflows/extra_jobs.yml @@ -28,6 +28,7 @@ jobs: name: Auto Label permissions: contents: read + issues: write pull-requests: write outputs: all-labels: ${{ steps.labeler.outputs.all-labels }} From ee6e0e460e09eda8302a29041012fd46c04c1a0b Mon Sep 17 00:00:00 2001 From: Zyad Hassan Date: Fri, 27 Jun 2025 09:50:46 -0700 Subject: [PATCH 160/161] Revert changes --- .github/labeler.yml | 2 +- .github/workflows/extra_jobs.yml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index c80b7492435b..1195ee4a14d2 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -8,6 +8,6 @@ Z-BenchCI: - any: - changed-files: - - any-glob-to-any-file: ['kani-compiler/**', 'kani-driver/src/call_*', 'cprover_bindings/**', 'library/**'] + - any-glob-to-any-file: ['kani-compiler/**', 'kani-driver/src/call-*', 'cprover_bindings/**', 'library/**'] - any-glob-to-any-file: ['rust-toolchain.toml', 'Cargo.lock'] - any-glob-to-any-file: ['kani-dependencies'] diff --git a/.github/workflows/extra_jobs.yml b/.github/workflows/extra_jobs.yml index a27a07433fc4..ddf242ba3956 100644 --- a/.github/workflows/extra_jobs.yml +++ b/.github/workflows/extra_jobs.yml @@ -28,7 +28,6 @@ jobs: name: Auto Label permissions: contents: read - issues: write pull-requests: write outputs: all-labels: ${{ steps.labeler.outputs.all-labels }} From c706cf537d5525f99ae7bf16448a45b6ae17e5e9 Mon Sep 17 00:00:00 2001 From: Zyad Hassan Date: Fri, 27 Jun 2025 09:50:57 -0700 Subject: [PATCH 161/161] Introduce compiler timing script & CI job (#4154) We have an existing `benchcomp` script to measure Kani's performance in CI, but it is prone to noise and only focuses on the end-to-end performance of compilation and verification together, leaving a need for more granular measurements. This script focuses solely on changes in the runtime of the Kani compiler and runs with warm ups, repeats and outlier detection (based on the rust compiler's method in [`rustc-perf`](https://github.com/rust-lang/rustc-perf)) in an attempt to limit noise. The new `compile-timer-short` CI job uses this script on a subset of the `perf` tests (currently excluding `s2n-quic`, `kani-lib/arbitrary` & `misc/display-trait`) to produce a `benchcomp`-like table comparing the compiler performance per-crate before and after a given commit. This also modifies our auto-labeller to ensure end-to-end benchmarks (like `benchcomp`) and the new compiler-specific ones are only run when the parts of Kani that they profile have changed. We manually tested the CI job on my personal fork (see [this regressing run](https://github.com/AlexanderPortland/kani/actions/runs/15788016660?pr=6) from a test PR that intentionally slows down the compiler). Resolves #2442 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .github/labeler.yml | 15 +- .github/workflows/bench-compiler.yml | 140 ++++++++++ .../workflows/{bench.yml => bench-e2e.yml} | 2 +- .github/workflows/extra_jobs.yml | 15 +- Cargo.lock | 9 + Cargo.toml | 1 + kani-driver/src/call_cargo.rs | 6 + .../benchcomp/visualizers/__init__.py | 5 + tools/benchcomp/test/test_regression.py | 10 + tools/compile-timer/Cargo.toml | 24 ++ tools/compile-timer/README.md | 19 ++ tools/compile-timer/src/common.rs | 61 +++++ tools/compile-timer/src/compile-analyzer.rs | 240 ++++++++++++++++++ tools/compile-timer/src/compile-timer.rs | 184 ++++++++++++++ 14 files changed, 724 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/bench-compiler.yml rename .github/workflows/{bench.yml => bench-e2e.yml} (98%) create mode 100644 tools/compile-timer/Cargo.toml create mode 100644 tools/compile-timer/README.md create mode 100644 tools/compile-timer/src/common.rs create mode 100644 tools/compile-timer/src/compile-analyzer.rs create mode 100644 tools/compile-timer/src/compile-timer.rs diff --git a/.github/labeler.yml b/.github/labeler.yml index 1195ee4a14d2..9ef0a5a3c742 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -5,9 +5,20 @@ # # Note that we enable dot, so "**" matches all files in a folder -Z-BenchCI: +Z-EndToEndBenchCI: - any: - changed-files: - - any-glob-to-any-file: ['kani-compiler/**', 'kani-driver/src/call-*', 'cprover_bindings/**', 'library/**'] + - any-glob-to-any-file: ['kani-compiler/**', 'kani-driver/src/call_*', 'cprover_bindings/**', 'library/**'] - any-glob-to-any-file: ['rust-toolchain.toml', 'Cargo.lock'] - any-glob-to-any-file: ['kani-dependencies'] + +Z-CompilerBenchCI: +- any: + # we want to run compiler benchmarks if: + - changed-files: + # any parts of the compiler change + - any-glob-to-any-file: ['kani-compiler/**', 'cprover_bindings/**', 'library/**'] + # the way we call the compiler changes + - any-glob-to-any-file: ['kani-driver/src/call_cargo.rs', 'kani-driver/src/call_single_file.rs'] + # or if our dependencies change + - any-glob-to-any-file: ['rust-toolchain.toml', 'Cargo.lock'] \ No newline at end of file diff --git a/.github/workflows/bench-compiler.yml b/.github/workflows/bench-compiler.yml new file mode 100644 index 000000000000..c7dbe7d88747 --- /dev/null +++ b/.github/workflows/bench-compiler.yml @@ -0,0 +1,140 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# Run performance benchmarks comparing the compiler performance of two different Kani versions. +name: Kani Compiler Performance Benchmarks +on: + push: + branches: + - 'main' + workflow_call: + +jobs: + compile-timer-short: + runs-on: ubuntu-24.04 + steps: + - name: Save push event HEAD and HEAD~ to environment variables + if: ${{ github.event_name == 'push' }} + run: | + echo "NEW_REF=${{ github.event.after}}" | tee -a "$GITHUB_ENV" + echo "OLD_REF=${{ github.event.before }}" | tee -a "$GITHUB_ENV" + - name: Save pull request HEAD and base to environment variables + if: ${{ contains(fromJSON('["pull_request", "pull_request_target"]'), github.event_name) }} + run: | + echo "OLD_REF=${{ github.event.pull_request.base.sha }}" | tee -a "$GITHUB_ENV" + echo "NEW_REF=${{ github.event.pull_request.head.sha }}" | tee -a "$GITHUB_ENV" + - name: Check out Kani (old variant) + uses: actions/checkout@v4 + with: + path: ./old + ref: ${{ env.OLD_REF }} + fetch-depth: 2 + + - name: Check out Kani (new variant) + uses: actions/checkout@v4 + with: + path: ./new + ref: ${{ env.NEW_REF }} + fetch-depth: 1 + + - name: Set up Kani Dependencies (old variant) + uses: ./old/.github/actions/setup + with: + os: ubuntu-24.04 + kani_dir: old + + - name: Set up Kani Dependencies (new variant) + uses: ./new/.github/actions/setup + with: + os: ubuntu-24.04 + kani_dir: new + + - name: Copy benchmarks from new to old + run: rm -rf ./old/tests/perf ; cp -r ./new/tests/perf ./old/tests/ + + - name: Build `compile-timer` in old + run: cd old/tools/compile-timer && cargo build --release + - name: Build `kani` in old + run: cd old && cargo build-dev --release + + - name: Build `compile-timer` in new + run: cd new/tools/compile-timer && cargo build --release + - name: Build `kani` in new + run: cd new && cargo build-dev --release + + - name: Run `compile-timer` on old + run: | + export PATH="${{ github.workspace }}/old/scripts:$PATH" + cd old/tests/perf && ../../target/release/compile-timer --out-path compile-times-old.json --ignore kani-lib --ignore display_trait --ignore s2n-quic + - name: Run `compile-timer` on new + run: | + export PATH="${{ github.workspace }}/new/scripts:$PATH" + cd new/tests/perf && ../../target/release/compile-timer --out-path compile-times-new.json --ignore kani-lib --ignore display_trait --ignore s2n-quic + - name: Run analysis between the two + run: ./new/target/release/compile-analyzer --path-pre old/tests/perf/compile-times-old.json --path-post new/tests/perf/compile-times-new.json --only-markdown --suite-name short >> "$GITHUB_STEP_SUMMARY" + + compile-timer-long: + runs-on: ubuntu-24.04 + steps: + - name: Save push event HEAD and HEAD~ to environment variables + if: ${{ github.event_name == 'push' }} + run: | + echo "NEW_REF=${{ github.event.after}}" | tee -a "$GITHUB_ENV" + echo "OLD_REF=${{ github.event.before }}" | tee -a "$GITHUB_ENV" + - name: Save pull request HEAD and base to environment variables + if: ${{ contains(fromJSON('["pull_request", "pull_request_target"]'), github.event_name) }} + run: | + echo "OLD_REF=${{ github.event.pull_request.base.sha }}" | tee -a "$GITHUB_ENV" + echo "NEW_REF=${{ github.event.pull_request.head.sha }}" | tee -a "$GITHUB_ENV" + + - name: Check out Kani (old variant) + uses: actions/checkout@v4 + with: + path: ./old + ref: ${{ env.OLD_REF }} + fetch-depth: 2 + + - name: Check out Kani (new variant) + uses: actions/checkout@v4 + with: + path: ./new + ref: ${{ env.NEW_REF }} + fetch-depth: 1 + + - name: Set up Kani Dependencies (old variant) + uses: ./old/.github/actions/setup + with: + os: ubuntu-24.04 + kani_dir: old + + - name: Set up Kani Dependencies (new variant) + uses: ./new/.github/actions/setup + with: + os: ubuntu-24.04 + kani_dir: new + + # Ensures that a PR changing the benchmarks will have the new benchmarks run + # for both commits. + - name: Copy benchmarks from new to old + run: rm -rf ./old/tests/perf ; cp -r ./new/tests/perf ./old/tests/ + + - name: Build `compile-timer` in old + run: cd old/tools/compile-timer && cargo build --release + - name: Build `kani` in old + run: cd old && cargo build-dev --release + + - name: Build `compile-timer` in new + run: cd new/tools/compile-timer && cargo build --release + - name: Build `kani` in new + run: cd new && cargo build-dev --release + + - name: Run `compile-timer` on old + run: | + export PATH="${{ github.workspace }}/old/scripts:$PATH" + cd old/tests/perf/s2n-quic && ../../../target/release/compile-timer --out-path compile-times-old.json --also-visit quic/s2n-quic-core --also-visit quic/s2n-quic-platform --also-visit common/s2n-codec --skip-current + - name: Run `compile-timer` on new + run: | + export PATH="${{ github.workspace }}/new/scripts:$PATH" + cd new/tests/perf/s2n-quic && ../../../target/release/compile-timer --out-path compile-times-new.json --also-visit quic/s2n-quic-core --also-visit quic/s2n-quic-platform --also-visit common/s2n-codec --skip-current + - name: Run analysis between the two + run: ./new/target/release/compile-analyzer --path-pre old/tests/perf/s2n-quic/compile-times-old.json --path-post new/tests/perf/s2n-quic/compile-times-new.json --only-markdown --suite-name long >> "$GITHUB_STEP_SUMMARY" \ No newline at end of file diff --git a/.github/workflows/bench.yml b/.github/workflows/bench-e2e.yml similarity index 98% rename from .github/workflows/bench.yml rename to .github/workflows/bench-e2e.yml index dcd5b5004f2f..954b84c727c4 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench-e2e.yml @@ -6,7 +6,7 @@ # This workflow will run when: # - Changes are pushed to 'main'. # - Triggered by another workflow -name: Kani Performance Benchmarks +name: Kani End-To-End Performance Benchmarks on: push: branches: diff --git a/.github/workflows/extra_jobs.yml b/.github/workflows/extra_jobs.yml index ddf242ba3956..416fe94072db 100644 --- a/.github/workflows/extra_jobs.yml +++ b/.github/workflows/extra_jobs.yml @@ -43,9 +43,16 @@ jobs: with: dot: true - verification-bench: - name: Verification Benchmarks + end-to-end-bench: + name: End-to-End Benchmarks needs: auto-label permissions: {} - if: ${{ contains(needs.auto-label.outputs.all-labels, 'Z-BenchCI') && github.event_name != 'merge_group' }} - uses: ./.github/workflows/bench.yml + if: ${{ contains(needs.auto-label.outputs.all-labels, 'Z-EndToEndBenchCI') && github.event_name != 'merge_group' }} + uses: ./.github/workflows/bench-e2e.yml + + compiler-bench: + name: Compiler Benchmarks + needs: auto-label + permissions: {} + if: ${{ contains(needs.auto-label.outputs.all-labels, 'Z-CompilerBenchCI') && github.event_name != 'merge_group' }} + uses: ./.github/workflows/bench-compiler.yml diff --git a/Cargo.lock b/Cargo.lock index fb56c611dfb7..00aecfa95664 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -341,6 +341,15 @@ dependencies = [ "unicode-width 0.2.0", ] +[[package]] +name = "compile-timer" +version = "0.1.0" +dependencies = [ + "clap", + "serde", + "serde_json", +] + [[package]] name = "compiletest" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 455ee300bfef..d14ad335241a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ members = [ "tools/build-kani", "tools/kani-cov", "tools/scanner", + "tools/compile-timer", "kani-driver", "kani-compiler", "kani_metadata", diff --git a/kani-driver/src/call_cargo.rs b/kani-driver/src/call_cargo.rs index af2e3f192e45..a61e3f532d86 100644 --- a/kani-driver/src/call_cargo.rs +++ b/kani-driver/src/call_cargo.rs @@ -356,7 +356,13 @@ crate-type = ["lib"] && t1.doc == t2.doc) } + let compile_start = std::time::Instant::now(); let artifacts = self.run_build(cargo_cmd)?; + if std::env::var("TIME_COMPILER").is_ok() { + // conditionally print the compilation time for debugging & use by `compile-timer` + // doesn't just use the existing `--debug` flag because the number of prints significantly affects performance + println!("BUILT {} IN {:?}μs", target.name, compile_start.elapsed().as_micros()); + } debug!(?artifacts, "run_build_target"); // We generate kani specific artifacts only for the build target. The build target is diff --git a/tools/benchcomp/benchcomp/visualizers/__init__.py b/tools/benchcomp/benchcomp/visualizers/__init__.py index 865386900639..8963e444e7a4 100644 --- a/tools/benchcomp/benchcomp/visualizers/__init__.py +++ b/tools/benchcomp/benchcomp/visualizers/__init__.py @@ -265,12 +265,17 @@ def _get_template(): Scatterplot axis ranges are {{ d["scaled_metrics"][metric]["min_value"] }} (bottom/left) to {{ d["scaled_metrics"][metric]["max_value"] }} (top/right). {% endif -%} +
Breakdown by harness + | Benchmark | {% for variant in d["variants"][metric] %} {{ variant }} |{% endfor %} | --- |{% for variant in d["variants"][metric] %} --- |{% endfor -%} {% for bench_name, bench_variants in benchmarks.items () %} | {{ bench_name }} {% for variant in d["variants"][metric] -%} | {{ bench_variants[variant] }} {% endfor %}| {%- endfor %} + +
+ {% endfor -%} """) diff --git a/tools/benchcomp/test/test_regression.py b/tools/benchcomp/test/test_regression.py index ccf2259f7f0b..00256aa891ab 100644 --- a/tools/benchcomp/test/test_regression.py +++ b/tools/benchcomp/test/test_regression.py @@ -477,18 +477,28 @@ def test_markdown_results_table(self): ``` Scatterplot axis ranges are 5 (bottom/left) to 10 (top/right). +
Breakdown by harness + | Benchmark | variant_1 | variant_2 | ratio | | --- | --- | --- | --- | | bench_1 | 5 | 10 | **2.0** | | bench_2 | 10 | 5 | 0.5 | +
+ + ## success +
Breakdown by harness + | Benchmark | variant_1 | variant_2 | notes | | --- | --- | --- | --- | | bench_1 | True | True | | | bench_2 | True | False | regressed | | bench_3 | False | True | newly passing | + +
+ """)) diff --git a/tools/compile-timer/Cargo.toml b/tools/compile-timer/Cargo.toml new file mode 100644 index 000000000000..f826d25f842c --- /dev/null +++ b/tools/compile-timer/Cargo.toml @@ -0,0 +1,24 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +[package] +name = "compile-timer" +version = "0.1.0" +edition = "2024" +license = "MIT OR Apache-2.0" + +[dependencies] +clap = { version = "4.5.40", features = ["derive"] } +serde = {version = "1.0.219", features = ["derive"]} +serde_json = "1.0.140" + +[[bin]] +name = "compile-timer" +path = "src/compile-timer.rs" + +[[bin]] +name = "compile-analyzer" +path = "src/compile-analyzer.rs" + +[lints] +workspace = true \ No newline at end of file diff --git a/tools/compile-timer/README.md b/tools/compile-timer/README.md new file mode 100644 index 000000000000..de9821f91e88 --- /dev/null +++ b/tools/compile-timer/README.md @@ -0,0 +1,19 @@ +# Compile-Timer +This is a simple script for timing the Kani compiler's end-to-end performance on crates. + +## Setup +You can run it by first compiling Kani (with `cargo build-dev --release` in the project root), then building this script (with `cargo build --release` in this `compile-timer` directory). This will build new `compile-timer` & `compile-analyzer` binaries in `kani/target/release`. + +## Recording Compiler Times with `compile-timer` +After doing that, you should make sure you have Kani on your $PATH (see instructions [here](https://model-checking.github.io/kani/build-from-source.html#adding-kani-to-your-path)) after which you can run `compile-timer --out-path [OUT_JSON_FILE]` in any directory to profile the compiler's performance on it. + +By default, the script recursively goes into directories and will use `cargo kani` to profile any Rust projects it encounters (which it determines by looking for a `Cargo.toml`). You can tell it to ignore specific subtrees by passing in the `--ignore [DIR_NAME]` flag. + +## Visualizing Compiler Times with `compile-analyzer` +`compile-timer` itself will have some debug output including each individual run's time and aggregates for each crate. + +`compile-analyzer` is specifically for comparing performance across multiple commits. + +Once you've run `compile-timer` on both commits, you can run `compile-analyzer --path-pre [FIRST_JSON_FILE] --path-post [SECOND_JSON_FILE]` to see the change in performance going from the first to second commit. + +By default, `compile-analyzer` will just print to the console, but if you specify the `--only-markdown` option, it's output will be formatted for GitHub flavored markdown (as is useful in CI). \ No newline at end of file diff --git a/tools/compile-timer/src/common.rs b/tools/compile-timer/src/common.rs new file mode 100644 index 000000000000..b65ed0de79e8 --- /dev/null +++ b/tools/compile-timer/src/common.rs @@ -0,0 +1,61 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +#![allow(dead_code)] +use serde::{Deserialize, Serialize}; +use std::path::{Path, PathBuf}; +use std::time::Duration; + +#[derive(Debug, Serialize, Deserialize)] +pub struct AggrResult { + pub krate: PathBuf, + pub krate_trimmed_path: String, + /// the stats for only the 25th-75th percentile of runs on this crate, i.e., the interquartile range + pub iqr_stats: Stats, + /// the stats for all runs on this crate + full_stats: Stats, +} + +pub fn krate_trimmed_path(krate: &Path) -> String { + format!( + "{:?}", + krate + .canonicalize() + .unwrap() + .strip_prefix(std::env::current_dir().unwrap().parent().unwrap()) + .unwrap() + ) +} + +impl AggrResult { + pub fn new(krate: PathBuf, iqr_stats: Stats, full_stats: Stats) -> Self { + AggrResult { krate_trimmed_path: krate_trimmed_path(&krate), krate, iqr_stats, full_stats } + } + + pub fn full_std_dev(&self) -> Duration { + self.full_stats.std_dev + } + + pub fn iqr(&self) -> Duration { + self.iqr_stats.range.1 - self.iqr_stats.range.0 + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Stats { + pub avg: Duration, + pub std_dev: Duration, + pub range: (Duration, Duration), +} + +/// Sum the IQR averages and IQR standard deviations respectively for all crates timed. +pub fn aggregate_aggregates(info: &[AggrResult]) -> (Duration, Duration) { + for i in info { + println!("krate {:?} -- {:?}", i.krate, i.iqr_stats.avg); + } + + (info.iter().map(|i| i.iqr_stats.avg).sum(), info.iter().map(|i| i.iqr_stats.std_dev).sum()) +} + +pub fn fraction_of_duration(dur: Duration, frac: f64) -> Duration { + Duration::from_nanos(((dur.as_nanos() as f64) * frac) as u64) +} diff --git a/tools/compile-timer/src/compile-analyzer.rs b/tools/compile-timer/src/compile-analyzer.rs new file mode 100644 index 000000000000..416065131734 --- /dev/null +++ b/tools/compile-timer/src/compile-analyzer.rs @@ -0,0 +1,240 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +#![allow(dead_code)] +use crate::common::{AggrResult, fraction_of_duration}; +use clap::Parser; +use serde_json::Deserializer; +use std::{cmp::max, fs::File, io, path::PathBuf, time::Duration}; +mod common; + +// Constants for detecting 'significant' regressions + +/// The fractional of a sample's standard deviation that it can regress by +/// without being considered a significant regression. +const FRAC_STD_DEV_THRESHOLD: f64 = 2.0; // In this case, 2x the average std deviation. + +/// The fractional amount a run can regress by without it being considered a significant regression. +const FRAC_ABSOLUTE_THRESHOLD: f64 = 0.05; // In this case, 5% of the initial time. + +#[derive(Debug, Parser)] +#[command(version, about, long_about = None)] +struct AnalyzerArgs { + #[arg(short, long, value_name = "FILE")] + path_pre: PathBuf, + + #[arg(short, long, value_name = "FILE")] + path_post: PathBuf, + + #[arg(short, long)] + /// The test suite name to display as part of the output's title + suite_name: Option, + + /// Output results in markdown format + #[arg(short, long)] + only_markdown: bool, +} + +fn main() { + let args = AnalyzerArgs::parse(); + + let (pre_file, post_file) = try_read_files(&args).unwrap(); + + let (pre_ser, post_ser) = + (Deserializer::from_reader(pre_file), Deserializer::from_reader(post_file)); + + let pre_results = pre_ser.into_iter::().collect::>(); + let post_results = post_ser.into_iter::().collect::>(); + + let mut results = pre_results + .into_iter() + .filter_map(Result::ok) + .zip(post_results.into_iter().filter_map(Result::ok)) + .collect::>(); + + sort_results(&mut results); + + if args.only_markdown { + print_markdown(results.as_slice(), args.suite_name); + } else { + print_to_terminal(results.as_slice()); + } +} + +/// Sort results based on percentage change, with high magnitude regressions first, then low +/// magnitude regressions, low magnitude improvements and finally high magnitude improvements. +fn sort_results(results: &mut [(AggrResult, AggrResult)]) { + results.sort_by_key(|a| { + -(signed_percent_diff(&a.0.iqr_stats.avg, &a.1.iqr_stats.avg).abs() * 1000_f64) as i64 + }); +} + +/// Print results in a markdown format (for GitHub actions). +fn print_markdown(results: &[(AggrResult, AggrResult)], suite_name: Option) { + let suite_text = if let Some(suite_name) = suite_name { + format!(" (`{suite_name}` suite)") + } else { + "".to_string() + }; + println!("# Compiletime Results{suite_text}"); + let total_pre = results.iter().map(|i| i.0.iqr_stats.avg).sum(); + let total_post = results.iter().map(|i| i.1.iqr_stats.avg).sum(); + println!( + "### *on the whole: {:.2?} → {:.2?} —* {}", + total_pre, + total_post, + diff_string(total_pre, total_post) + ); + // Note that we have to call the fourth column "heterogeneousness" because the color-formatted + // diff will cut off if the column isn't wide enough for it, so verbosity is required. + println!( + "| test crate | old compile time | new compile time | heterogeneousness (percentage difference) | verdict |" + ); + println!("| - | - | - | - | - |"); + let regressions = results + .iter() + .map(|(pre_res, post_res)| { + assert!(pre_res.krate_trimmed_path == post_res.krate_trimmed_path); + let pre_time = pre_res.iqr_stats.avg; + let post_time = post_res.iqr_stats.avg; + + let verdict = verdict_on_change(pre_res, post_res); + // emphasize output of crate name if it had a suspected regression + let emph = if verdict.is_regression() { "**" } else { "" }; + println!( + "| {emph}{}{emph} | {:.2?} | {:.2?} | {} | {:?} |", + pre_res.krate_trimmed_path, + pre_time, + post_time, + diff_string(pre_time, post_time), + verdict + ); + (&pre_res.krate_trimmed_path, verdict) + }) + .filter_map( + |(krate, verdict)| if verdict.is_regression() { Some(krate.clone()) } else { None }, + ) + .collect::>(); + + let footnote_number = 1; + println!( + "\n[^{footnote_number}]: threshold: max({FRAC_STD_DEV_THRESHOLD} x std_dev, {FRAC_ABSOLUTE_THRESHOLD} x initial_time)." + ); + + if regressions.is_empty() { + println!("## No suspected regressions[^{footnote_number}]!"); + } else { + println!( + "## Failing because of {} suspected regressions[^{footnote_number}]:", + regressions.len() + ); + println!("{}", regressions.join(", ")); + std::process::exit(1); + } +} + +/// Print results for a terminal output. +fn print_to_terminal(results: &[(AggrResult, AggrResult)]) { + let krate_column_len = results + .iter() + .map(|(a, b)| max(a.krate_trimmed_path.len(), b.krate_trimmed_path.len())) + .max() + .unwrap(); + + for (pre_res, post_res) in results { + assert!(pre_res.krate == post_res.krate); + let pre_time = pre_res.iqr_stats.avg; + let post_time = post_res.iqr_stats.avg; + + let change_dir = if post_time > pre_time { + "↑" + } else if post_time == pre_time { + "-" + } else { + "↓" + }; + let change_amount = (pre_time.abs_diff(post_time).as_micros() as f64 + / post_time.as_micros() as f64) + * 100_f64; + + println!( + "krate {:krate_column_len$} -- [{:.2?} => {:.2?} ({change_dir}{change_amount:5.2}%)] {:?}", + pre_res.krate_trimmed_path, + pre_time, + post_time, + verdict_on_change(pre_res, post_res) + ); + } +} + +/// Classify a change into a [Verdict], determining whether it was an improvement, regression, +/// or likely just noise based on provided thresholds. +fn verdict_on_change(pre: &AggrResult, post: &AggrResult) -> Verdict { + let (pre_time, post_time) = (pre.iqr_stats.avg, post.iqr_stats.avg); + + if post_time.abs_diff(pre_time) < fraction_of_duration(pre_time, FRAC_ABSOLUTE_THRESHOLD) { + return Verdict::ProbablyNoise(NoiseExplanation::SmallPercentageChange); + } + + let avg_std_dev = (pre.full_std_dev() + post.full_std_dev()) / 2; + if post_time.abs_diff(pre_time) < fraction_of_duration(avg_std_dev, FRAC_STD_DEV_THRESHOLD) { + return Verdict::ProbablyNoise(NoiseExplanation::SmallComparedToStdDevOf(avg_std_dev)); + } + + if pre.iqr_stats.avg > post.iqr_stats.avg { + return Verdict::Improved; + } + + Verdict::PotentialRegression { sample_std_dev: avg_std_dev } +} + +fn signed_percent_diff(pre: &Duration, post: &Duration) -> f64 { + let change_amount = (pre.abs_diff(*post).as_micros() as f64 / pre.as_micros() as f64) * 100_f64; + if post < pre { -change_amount } else { change_amount } +} + +fn diff_string(pre: Duration, post: Duration) -> String { + let change_dir = if post > pre { + "$\\color{red}\\textsf{↑ " + } else if post == pre { + "$\\color{black}\\textsf{- " + } else { + "$\\color{green}\\textsf{↓ " + }; + let change_amount = signed_percent_diff(&pre, &post).abs(); + format!("{change_dir}{:.2?} ({change_amount:.2}\\\\%)}}$", pre.abs_diff(post)) +} + +#[derive(Debug)] +enum Verdict { + /// This crate now compiles faster! + Improved, + /// This crate compiled slower, but likely because of OS noise. + ProbablyNoise(NoiseExplanation), + /// This crate compiled slower, potentially indicating a true performance regression. + PotentialRegression { sample_std_dev: std::time::Duration }, +} + +#[derive(Debug)] +/// The reason a regression was flagged as likely noise rather than a true performance regression. +enum NoiseExplanation { + /// The increase in compile time is so small compared to the + /// sample's standard deviation (< [FRAC_STD_DEV_THRESHOLD] * std_dev) + /// that it is probably just sampling noise. + SmallComparedToStdDevOf(std::time::Duration), + /// The percentage increase in compile time is so small (< [FRAC_ABSOLUTE_THRESHOLD]), + /// the difference is likely insignificant. + SmallPercentageChange, +} + +impl Verdict { + fn is_regression(&self) -> bool { + matches!(self, Verdict::PotentialRegression { sample_std_dev: _ }) + } +} + +fn try_read_files(c: &AnalyzerArgs) -> io::Result<(File, File)> { + io::Result::Ok(( + File::open(c.path_pre.canonicalize()?)?, + File::open(c.path_post.canonicalize()?)?, + )) +} diff --git a/tools/compile-timer/src/compile-timer.rs b/tools/compile-timer/src/compile-timer.rs new file mode 100644 index 000000000000..4c7799d40ff4 --- /dev/null +++ b/tools/compile-timer/src/compile-timer.rs @@ -0,0 +1,184 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#![feature(exit_status_error)] + +use crate::common::{AggrResult, Stats, aggregate_aggregates, krate_trimmed_path}; +use clap::Parser; +use serde::Serialize; +use std::fs::File; +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; +use std::time::Duration; +mod common; + +#[derive(Debug, Parser)] +#[command(version, about, long_about = None)] +struct TimerArgs { + /// Sets a custom config file + #[arg(short, long, value_name = "FILE")] + out_path: PathBuf, + + /// Ignore compiling the current directory + #[arg(short, long)] + skip_current: bool, + + /// The paths of additional paths to visit beyond the current subtree + #[arg(short, long, value_name = "DIR")] + also_visit: Vec, + + /// The names or paths of files to ignore + #[arg(short, long)] + ignore: Vec, +} + +/// The number of untimed runs to do before starting timed runs. +/// We need at least one warm-up run to make sure crates are fetched & cached in +/// the local `.cargo/registry` folder. Otherwise the first run will be significantly slower. +const WARMUP_RUNS: usize = 1; +const TIMED_RUNS: usize = 10; + +fn main() { + let args = TimerArgs::parse(); + + let (mut to_visit, mut res) = (vec![], vec![]); + to_visit.extend(args.also_visit.into_iter().rev()); + if !args.skip_current { + let current_directory = std::env::current_dir().expect("should be run in a directory"); + to_visit.push(current_directory); + } + + let mut out_ser = serde_json::Serializer::pretty(File::create(&args.out_path).unwrap()); + let run_start = std::time::Instant::now(); + + // recursively visit subdirectories to time the compiler on all rust projects + while let Some(next) = to_visit.pop() { + let next = next.canonicalize().unwrap(); + let path_to_toml = next.join("Cargo.toml"); + + if path_to_toml.exists() && path_to_toml.is_file() { + // in rust crate so we want to profile it + println!("[!] profiling in {}", krate_trimmed_path(&next)); + let new_res = profile_on_crate(&next); + new_res.serialize(&mut out_ser).unwrap(); + res.push(new_res); + } else { + // we want want to recur and visit all directories that aren't explicitly ignored + to_visit.extend(std::fs::read_dir(next).unwrap().filter_map(|entry| { + if let Ok(entry) = entry { + let path = entry.path(); + if path.is_dir() && !args.ignore.iter().any(|ignored| path.ends_with(ignored)) { + return Some(path); + } + } + None + })); + } + } + + println!("[!] total info is {:?}", aggregate_aggregates(&res)); + + print!("\t [*] run took {:?}", run_start.elapsed()); +} + +/// Profile a crate by running a certain number of untimed warmup runs and then +/// a certain number of timed runs, returning aggregates of the timing results. +fn profile_on_crate(absolute_path: &std::path::PathBuf) -> AggrResult { + let _warmup_results = (0..WARMUP_RUNS) + .map(|i| { + print!("\t[*] running warmup {}/{WARMUP_RUNS}", i + 1); + let _ = std::io::stdout().flush(); + let res = run_command_in(absolute_path); + println!(" -- {res:?}"); + res + }) + .collect::>(); + + let timed_results = (0..TIMED_RUNS) + .map(|i| { + print!("\t[*] running timed run {}/{TIMED_RUNS}", i + 1); + let _ = std::io::stdout().flush(); + let res = run_command_in(absolute_path); + println!(" -- {res:?}"); + res + }) + .collect::>(); + + let aggr = aggregate_results(absolute_path, &timed_results); + println!("\t[!] results for {absolute_path:?} are in! {aggr:?}"); + + aggr +} + +type RunResult = Duration; +/// Run `cargo kani` in a crate and parse out the compiler timing info outputted +/// by the `TIME_COMPILER` environment variable. +fn run_command_in(absolute_path: &Path) -> RunResult { + // `cargo clean` first to ensure the compiler is fully run again + let _ = Command::new("cargo") + .current_dir(absolute_path) + .arg("clean") + .stdout(Stdio::null()) + .output() + .expect("cargo clean should succeed"); + + // do the actual compiler run (using `--only-codegen` to save time) + let kani_output = Command::new("cargo") + .current_dir(absolute_path) + .arg("kani") + .arg("--only-codegen") + .env("TIME_COMPILER", "true") + .output() + .expect("cargo kani should succeed"); + + // parse the output bytes into a string + let out_str = String::from_utf8(kani_output.stdout).expect("utf8 conversion should succeed"); + + if !kani_output.status.success() { + println!( + "the `TIME_COMPILER=true cargo kani --only-codegen` command failed in {absolute_path:?} with output -- {out_str:?}" + ); + panic!("cargo kani command failed"); + } + + // parse that string for the compiler build information + // and if it's built multiple times (which could happen in a workspace with multiple crates), + // we just sum up the total time + out_str.split("\n").filter(|line| line.starts_with("BUILT")).map(extract_duration).sum() +} + +fn extract_duration(s: &str) -> Duration { + let s = &s[s.find("IN").unwrap_or(0)..]; + let micros = s.chars().filter(|c| c.is_ascii_digit()).collect::().parse().ok().unwrap(); + + Duration::from_micros(micros) +} + +fn aggregate_results(path: &Path, results: &[Duration]) -> AggrResult { + assert!(results.len() == TIMED_RUNS); + + // sort and calculate the subset of times in the interquartile range + let mut sorted = results.to_vec(); + sorted.sort(); + let iqr_bounds = (results.len() / 4, results.len() * 3 / 4); + let iqr_durations = sorted + .into_iter() + .enumerate() + .filter_map(|(i, v)| if i >= iqr_bounds.0 && i <= iqr_bounds.1 { Some(v) } else { None }) + .collect::>(); + + AggrResult::new(path.to_path_buf(), result_stats(&iqr_durations), result_stats(results)) +} + +/// Record the stats from a subset slice of timing runs. +fn result_stats(results: &[Duration]) -> Stats { + let avg = results.iter().sum::() / results.len().try_into().unwrap(); + let range = (*results.iter().min().unwrap(), *results.iter().max().unwrap()); + + let deviations = results.iter().map(|x| x.abs_diff(avg).as_micros().pow(2)).sum::(); + let std_dev = + Duration::from_micros((deviations / results.len() as u128).isqrt().try_into().unwrap()); + + Stats { avg, std_dev, range } +}