From 129375fa036040ffc189ccbc7410fb0bec4f3ee8 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 7 Feb 2024 18:56:43 +0100 Subject: [PATCH 001/225] Update cargo dependencies (#3004) This is to fix recent bookrunner failures, such as https://github.com/model-checking/kani/actions/runs/7813237161/job/21311963018?pr=2996 mdbook-graphviz should be re-instated once its dependency chain has been fixed. --- Cargo.lock | 100 +++++++++++-------- kani-compiler/Cargo.toml | 4 +- kani-compiler/src/args.rs | 4 +- kani-driver/Cargo.toml | 6 +- kani_metadata/Cargo.toml | 4 +- kani_metadata/src/cbmc_solver.rs | 14 +-- scripts/setup/ubuntu/install_doc_deps.sh | 2 +- tools/bookrunner/Cargo.toml | 3 +- tools/bookrunner/librustdoc/Cargo.toml | 2 +- tools/bookrunner/librustdoc/html/markdown.rs | 2 +- tools/bookrunner/src/books.rs | 12 ++- tools/bookrunner/src/litani.rs | 2 +- tools/build-kani/Cargo.toml | 2 +- 13 files changed, 84 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 13594113134e..fb7478a425e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,9 +48,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" @@ -67,7 +67,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -77,7 +77,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -110,6 +110,7 @@ version = "0.1.0" dependencies = [ "Inflector", "pulldown-cmark", + "pulldown-cmark-escape", "rustdoc", "serde", "serde_json", @@ -218,8 +219,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c64043d6c7b7a4c58e39e7efccfdea7b93d885a795d0c054a69dbbf4dd52686" dependencies = [ "crossterm", - "strum", - "strum_macros", + "strum 0.25.0", + "strum_macros 0.25.3", "unicode-width", ] @@ -250,7 +251,7 @@ dependencies = [ "lazy_static", "libc", "unicode-width", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -340,7 +341,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -402,7 +403,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -452,8 +453,8 @@ dependencies = [ "serde", "serde_json", "shell-words", - "strum", - "strum_macros", + "strum 0.26.1", + "strum_macros 0.26.1", "tracing", "tracing-subscriber", ] @@ -477,8 +478,8 @@ dependencies = [ "rustc-demangle", "serde", "serde_json", - "strum", - "strum_macros", + "strum 0.26.1", + "strum_macros 0.26.1", "tempfile", "toml", "tracing", @@ -512,8 +513,8 @@ dependencies = [ "clap", "cprover_bindings", "serde", - "strum", - "strum_macros", + "strum 0.26.1", + "strum_macros 0.26.1", ] [[package]] @@ -618,9 +619,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", ] @@ -765,15 +766,21 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.9.6" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" +checksum = "dce76ce678ffc8e5675b22aa1405de0b7037e2fdf8913fea40d1926c6fe1e6e7" dependencies = [ "bitflags 2.4.2", "memchr", "unicase", ] +[[package]] +name = "pulldown-cmark-escape" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d8f9aa0e3cbcfaf8bf00300004ee3b72f74770f9cbac93f6928771f613276b" + [[package]] name = "quote" version = "1.0.35" @@ -909,7 +916,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -1061,6 +1068,12 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +[[package]] +name = "strum" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f" + [[package]] name = "strum_macros" version = "0.25.3" @@ -1074,6 +1087,19 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "strum_macros" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.48", +] + [[package]] name = "syn" version = "1.0.109" @@ -1097,15 +1123,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.9.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -1140,9 +1165,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6a4b9e8023eb94392d3dca65d717c53abc5dad49c07cb65bb8fcd87115fa325" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" dependencies = [ "serde", "serde_spanned", @@ -1161,9 +1186,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951" dependencies = [ "indexmap", "serde", @@ -1319,15 +1344,15 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "which" -version = "5.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14" +checksum = "7fa5e0c10bf77f44aac573e498d1a82d5fbd5e91f6fc0a99e7be4b38e85e101c" dependencies = [ "either", "home", "once_cell", "rustix", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1361,15 +1386,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -1495,9 +1511,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.37" +version = "0.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cad8365489051ae9f054164e459304af2e7e9bb407c958076c8bf4aef52da5" +checksum = "5389a154b01683d28c77f8f68f49dea75f0a4da32557a58f68ee51ebba472d29" dependencies = [ "memchr", ] diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index b93ce2d135b8..f5c1163fe564 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -19,8 +19,8 @@ num = { version = "0.4.0", optional = true } regex = "1.7.0" serde = { version = "1", optional = true } serde_json = "1" -strum = "0.25.0" -strum_macros = "0.25.2" +strum = "0.26" +strum_macros = "0.26" 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"]} diff --git a/kani-compiler/src/args.rs b/kani-compiler/src/args.rs index b174795b7259..69a82b61e19d 100644 --- a/kani-compiler/src/args.rs +++ b/kani-compiler/src/args.rs @@ -1,10 +1,10 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -use strum_macros::{AsRefStr, EnumString, EnumVariantNames}; +use strum_macros::{AsRefStr, EnumString, VariantNames}; use tracing_subscriber::filter::Directive; -#[derive(Debug, Default, Clone, Copy, AsRefStr, EnumString, EnumVariantNames, PartialEq, Eq)] +#[derive(Debug, Default, Clone, Copy, AsRefStr, EnumString, VariantNames, PartialEq, Eq)] #[strum(serialize_all = "snake_case")] pub enum ReachabilityType { /// Start the cross-crate reachability analysis from all harnesses in the local crate. diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index 2a19030840d2..1e6744824c0c 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -27,13 +27,13 @@ rustc-demangle = "0.1.21" pathdiff = "0.2.1" rayon = "1.5.3" comfy-table = "7.0.1" -strum = {version = "0.25.0"} -strum_macros = {version = "0.25.2"} +strum = {version = "0.26"} +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 = "5.0.0" +which = "6" # A good set of suggested dependencies can be found in rustup: # https://github.com/rust-lang/rustup/blob/master/Cargo.toml diff --git a/kani_metadata/Cargo.toml b/kani_metadata/Cargo.toml index 2d40d61c898b..91e22e0224e7 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -13,6 +13,6 @@ publish = false [dependencies] serde = {version = "1", features = ["derive"]} cbmc = { path = "../cprover_bindings", package = "cprover_bindings" } -strum = "0.25.0" -strum_macros = "0.25.2" +strum = "0.26" +strum_macros = "0.26" clap = { version = "4.4.11", features = ["derive"] } diff --git a/kani_metadata/src/cbmc_solver.rs b/kani_metadata/src/cbmc_solver.rs index f6c5c0a54dc4..5265c6f17fa7 100644 --- a/kani_metadata/src/cbmc_solver.rs +++ b/kani_metadata/src/cbmc_solver.rs @@ -2,22 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use serde::{Deserialize, Serialize}; -use strum_macros::{AsRefStr, EnumString, EnumVariantNames}; +use strum_macros::{AsRefStr, EnumString, VariantNames}; /// An enum for CBMC solver options. All variants are handled by Kani, except for /// the `Binary` one, which it passes as is to CBMC's `--external-sat-solver` /// option. -#[derive( - Debug, - Clone, - AsRefStr, - EnumString, - EnumVariantNames, - PartialEq, - Eq, - Serialize, - Deserialize -)] +#[derive(Debug, Clone, AsRefStr, EnumString, VariantNames, PartialEq, Eq, Serialize, Deserialize)] #[strum(serialize_all = "snake_case")] pub enum CbmcSolver { /// CaDiCaL which is available in CBMC as of version 5.77.0 diff --git a/scripts/setup/ubuntu/install_doc_deps.sh b/scripts/setup/ubuntu/install_doc_deps.sh index e3f4dd2c0d74..924687923041 100755 --- a/scripts/setup/ubuntu/install_doc_deps.sh +++ b/scripts/setup/ubuntu/install_doc_deps.sh @@ -4,5 +4,5 @@ set -eux -cargo install mdbook-graphviz +# cargo install mdbook-graphviz DEBIAN_FRONTEND=noninteractive sudo apt-get install --no-install-recommends --yes graphviz diff --git a/tools/bookrunner/Cargo.toml b/tools/bookrunner/Cargo.toml index 6c602ce05fd0..74befa4ca5b7 100644 --- a/tools/bookrunner/Cargo.toml +++ b/tools/bookrunner/Cargo.toml @@ -10,7 +10,8 @@ publish = false [dependencies] Inflector = "0.11.4" -pulldown-cmark = { version = "0.9", default-features = false } +pulldown-cmark = { version = "0.10", default-features = false } +pulldown-cmark-escape = { version = "0.10", default-features = false } rustdoc = { path = "librustdoc" } walkdir = "2.3.2" serde = { version = "1.0", features = ["derive"] } diff --git a/tools/bookrunner/librustdoc/Cargo.toml b/tools/bookrunner/librustdoc/Cargo.toml index f83c6e25803c..aaf3a7100c06 100644 --- a/tools/bookrunner/librustdoc/Cargo.toml +++ b/tools/bookrunner/librustdoc/Cargo.toml @@ -20,7 +20,7 @@ publish = false path = "lib.rs" [dependencies] -pulldown-cmark = { version = "0.9", default-features = false } +pulldown-cmark = { version = "0.10", default-features = false } [package.metadata.rust-analyzer] rustc_private = true diff --git a/tools/bookrunner/librustdoc/html/markdown.rs b/tools/bookrunner/librustdoc/html/markdown.rs index 85235a5bcc6b..1bcd5dad7532 100644 --- a/tools/bookrunner/librustdoc/html/markdown.rs +++ b/tools/bookrunner/librustdoc/html/markdown.rs @@ -122,7 +122,7 @@ pub fn find_testable_code( tests.add_test(text, block_info, line); prev_offset = offset.start; } - Event::Start(Tag::Heading(level, _, _)) => { + Event::Start(Tag::Heading { level, .. }) => { register_header = Some(level as u32); } Event::Text(ref s) if register_header.is_some() => { diff --git a/tools/bookrunner/src/books.rs b/tools/bookrunner/src/books.rs index 0e8b8fb8857d..be0c39497795 100644 --- a/tools/bookrunner/src/books.rs +++ b/tools/bookrunner/src/books.rs @@ -11,7 +11,7 @@ use crate::{ util::{self, FailStep, TestProps}, }; use inflector::cases::{snakecase::to_snake_case, titlecase::to_title_case}; -use pulldown_cmark::{Event, Parser, Tag}; +use pulldown_cmark::{Event, Parser, Tag, TagEnd}; use rustc_span::edition::Edition; use rustdoc::{ doctest::{make_test, Tester}, @@ -99,21 +99,25 @@ impl Book { let mut hierarchy_path: PathBuf = ["tests", "bookrunner", "books", self.name.as_str()].iter().collect(); let mut prev_event_is_text_or_code = false; + let mut current_link_url = String::from(""); for event in parser { match event { - Event::End(Tag::Item) => { + Event::End(TagEnd::Item) => { // Pop the current chapter/section from the hierarchy once // we are done processing it and its subsections. hierarchy_path.pop(); prev_event_is_text_or_code = false; } - Event::End(Tag::Link(_, path, _)) => { + Event::Start(Tag::Link { dest_url, .. }) => { + current_link_url = dest_url.into_string(); + } + Event::End(TagEnd::Link) => { // At the start of the link tag, the hierarchy does not yet // contain the title of the current chapter/section. So, we wait // for the end of the link tag before adding the path and // hierarchy of the current chapter/section to the map. let mut full_path = summary_dir.clone(); - full_path.extend(path.split('/')); + full_path.extend(current_link_url.split('/')); self.hierarchy.insert(full_path, hierarchy_path.clone()); prev_event_is_text_or_code = false; } diff --git a/tools/bookrunner/src/litani.rs b/tools/bookrunner/src/litani.rs index cefe1c4b437e..620aa9c9f0d0 100644 --- a/tools/bookrunner/src/litani.rs +++ b/tools/bookrunner/src/litani.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Utilities to interact with the `Litani` build accumulator. -use pulldown_cmark::escape::StrWrite; +use pulldown_cmark_escape::StrWrite; use serde::Deserialize; use std::collections::HashMap; use std::path::Path; diff --git a/tools/build-kani/Cargo.toml b/tools/build-kani/Cargo.toml index cfaa4a6c48ad..636a7dfaea2e 100644 --- a/tools/build-kani/Cargo.toml +++ b/tools/build-kani/Cargo.toml @@ -13,4 +13,4 @@ publish = false anyhow = "1" cargo_metadata = "0.18.0" clap = { version = "4.4.11", features=["derive"] } -which = "5" +which = "6" From f08a3e99296f669055fa61c640ac916a0033fc31 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 7 Feb 2024 19:56:10 +0100 Subject: [PATCH 002/225] Enable powf*, exp*, log* intrinsics (#2996) CBMC provides approximating implementations of these. Resolves: #2966 --- docs/src/rust-feature-support/intrinsics.md | 16 +++++----- .../codegen/intrinsic.rs | 16 +++++----- tests/kani/Intrinsics/Math/Arith/exp.rs | 24 +++++++++++++++ tests/kani/Intrinsics/Math/Arith/exp2.rs | 22 ++++++++++++++ tests/kani/Intrinsics/Math/Arith/log.rs | 22 ++++++++++++++ tests/kani/Intrinsics/Math/Arith/powf32.rs | 15 ++++++++++ tests/kani/Intrinsics/Math/Arith/powf64.rs | 30 +++++++++++++++++++ 7 files changed, 129 insertions(+), 16 deletions(-) create mode 100644 tests/kani/Intrinsics/Math/Arith/exp.rs create mode 100644 tests/kani/Intrinsics/Math/Arith/exp2.rs create mode 100644 tests/kani/Intrinsics/Math/Arith/log.rs create mode 100644 tests/kani/Intrinsics/Math/Arith/powf32.rs create mode 100644 tests/kani/Intrinsics/Math/Arith/powf64.rs diff --git a/docs/src/rust-feature-support/intrinsics.md b/docs/src/rust-feature-support/intrinsics.md index 0705d3b00f1b..7df1a093943f 100644 --- a/docs/src/rust-feature-support/intrinsics.md +++ b/docs/src/rust-feature-support/intrinsics.md @@ -148,10 +148,10 @@ cttz_nonzero | Yes | | discriminant_value | Yes | | drop_in_place | No | | exact_div | Yes | | -exp2f32 | No | | -exp2f64 | No | | -expf32 | No | | -expf64 | No | | +exp2f32 | Partial | Results are overapproximated | +exp2f64 | Partial | Results are overapproximated | +expf32 | Partial | Results are overapproximated | +expf64 | Partial | Results are overapproximated | fabsf32 | Yes | | fabsf64 | Yes | | fadd_fast | Yes | | @@ -170,8 +170,8 @@ log10f32 | No | | log10f64 | No | | log2f32 | No | | log2f64 | No | | -logf32 | No | | -logf64 | No | | +logf32 | Partial | Results are overapproximated | +logf64 | Partial | Results are overapproximated | maxnumf32 | Yes | | maxnumf64 | Yes | | min_align_of | Yes | | @@ -185,8 +185,8 @@ 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) | -powf32 | No | | -powf64 | No | | +powf32 | Partial | Results are overapproximated | +powf64 | Partial | Results are overapproximated | powif32 | No | | powif64 | No | | pref_align_of | Yes | | diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 3412cba290d2..0709682afa65 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -420,10 +420,10 @@ impl<'tcx> GotocCtx<'tcx> { self.codegen_expr_to_place_stable(place, e) } "exact_div" => self.codegen_exact_div(fargs, place, loc), - "exp2f32" => unstable_codegen!(codegen_simple_intrinsic!(Exp2f)), - "exp2f64" => unstable_codegen!(codegen_simple_intrinsic!(Exp2)), - "expf32" => unstable_codegen!(codegen_simple_intrinsic!(Expf)), - "expf64" => unstable_codegen!(codegen_simple_intrinsic!(Exp)), + "exp2f32" => codegen_simple_intrinsic!(Exp2f), + "exp2f64" => codegen_simple_intrinsic!(Exp2), + "expf32" => codegen_simple_intrinsic!(Expf), + "expf64" => codegen_simple_intrinsic!(Exp), "fabsf32" => codegen_simple_intrinsic!(Fabsf), "fabsf64" => codegen_simple_intrinsic!(Fabs), "fadd_fast" => { @@ -456,8 +456,8 @@ impl<'tcx> GotocCtx<'tcx> { "log10f64" => unstable_codegen!(codegen_simple_intrinsic!(Log10)), "log2f32" => unstable_codegen!(codegen_simple_intrinsic!(Log2f)), "log2f64" => unstable_codegen!(codegen_simple_intrinsic!(Log2)), - "logf32" => unstable_codegen!(codegen_simple_intrinsic!(Logf)), - "logf64" => unstable_codegen!(codegen_simple_intrinsic!(Log)), + "logf32" => codegen_simple_intrinsic!(Logf), + "logf64" => codegen_simple_intrinsic!(Log), "maxnumf32" => codegen_simple_intrinsic!(Fmaxf), "maxnumf64" => codegen_simple_intrinsic!(Fmax), "min_align_of" => codegen_intrinsic_const!(), @@ -474,8 +474,8 @@ impl<'tcx> GotocCtx<'tcx> { "offset" => unreachable!( "Expected `core::intrinsics::unreachable` to be handled by `BinOp::OffSet`" ), - "powf32" => unstable_codegen!(codegen_simple_intrinsic!(Powf)), - "powf64" => unstable_codegen!(codegen_simple_intrinsic!(Pow)), + "powf32" => codegen_simple_intrinsic!(Powf), + "powf64" => codegen_simple_intrinsic!(Pow), "powif32" => unstable_codegen!(codegen_simple_intrinsic!(Powif)), "powif64" => unstable_codegen!(codegen_simple_intrinsic!(Powi)), "pref_align_of" => codegen_intrinsic_const!(), diff --git a/tests/kani/Intrinsics/Math/Arith/exp.rs b/tests/kani/Intrinsics/Math/Arith/exp.rs new file mode 100644 index 000000000000..4f26fc5b5b96 --- /dev/null +++ b/tests/kani/Intrinsics/Math/Arith/exp.rs @@ -0,0 +1,24 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// This test will trigger use of the `expf32` and `expf64` intrinsics, which in turn invoke +// functions modelled in CBMC's math library. These models use approximations as documented in +// CBMC's source code: https://github.com/diffblue/cbmc/blob/develop/src/ansi-c/library/math.c. + +#[kani::proof] +fn verify_exp32() { + let two = 2.0_f32; + let two_sq = std::f32::consts::E * std::f32::consts::E; + let two_exp = two.exp(); + + assert!((two_sq - two_exp).abs() <= 0.192); +} + +#[kani::proof] +fn verify_exp64() { + let two = 2.0_f64; + let two_sq = std::f64::consts::E * std::f64::consts::E; + let two_exp = two.exp(); + + assert!((two_sq - two_exp).abs() <= 0.192); +} diff --git a/tests/kani/Intrinsics/Math/Arith/exp2.rs b/tests/kani/Intrinsics/Math/Arith/exp2.rs new file mode 100644 index 000000000000..91244a383a0a --- /dev/null +++ b/tests/kani/Intrinsics/Math/Arith/exp2.rs @@ -0,0 +1,22 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// This test will trigger use of the `exp2f32` and `exp2f64` intrinsics, which in turn invoke +// functions modelled in CBMC's math library. These models use approximations as documented in +// CBMC's source code: https://github.com/diffblue/cbmc/blob/develop/src/ansi-c/library/math.c. + +#[kani::proof] +fn verify_exp2_32() { + let two = 2.0_f32; + let two_two = two.exp2(); + + assert!((two_two - 4.0).abs() <= 0.345); +} + +#[kani::proof] +fn verify_exp2_64() { + let two = 2.0_f64; + let two_two = two.exp2(); + + assert!((two_two - 4.0).abs() <= 0.345); +} diff --git a/tests/kani/Intrinsics/Math/Arith/log.rs b/tests/kani/Intrinsics/Math/Arith/log.rs new file mode 100644 index 000000000000..1185e8ae4d82 --- /dev/null +++ b/tests/kani/Intrinsics/Math/Arith/log.rs @@ -0,0 +1,22 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// This test will trigger use of the `logf32` and `logf64` intrinsics, which in turn invoke +// functions modelled in CBMC's math library. These models use approximations as documented in +// CBMC's source code: https://github.com/diffblue/cbmc/blob/develop/src/ansi-c/library/math.c. + +#[kani::proof] +fn verify_logf32() { + let e = std::f32::consts::E; + let e_log = e.ln(); + + assert!((e_log - 1.0).abs() <= 0.058); +} + +#[kani::proof] +fn verify_logf64() { + let e = std::f64::consts::E; + let e_log = e.ln(); + + assert!((e_log - 1.0).abs() <= 0.058); +} diff --git a/tests/kani/Intrinsics/Math/Arith/powf32.rs b/tests/kani/Intrinsics/Math/Arith/powf32.rs new file mode 100644 index 000000000000..f289171b64b8 --- /dev/null +++ b/tests/kani/Intrinsics/Math/Arith/powf32.rs @@ -0,0 +1,15 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// This test will trigger use of the `powf32` intrinsic, which in turn invoke functions modelled in +// CBMC's math library. These models use approximations as documented in CBMC's source code: +// https://github.com/diffblue/cbmc/blob/develop/src/ansi-c/library/math.c. + +#[kani::proof] +fn verify_pow() { + let x: f32 = kani::any(); + kani::assume(x.is_normal()); + kani::assume(x > 1.0 && x < u16::MAX.into()); + let x2 = x.powf(2.0); + assert!(x2 >= 0.0); +} diff --git a/tests/kani/Intrinsics/Math/Arith/powf64.rs b/tests/kani/Intrinsics/Math/Arith/powf64.rs new file mode 100644 index 000000000000..e80ad777c09a --- /dev/null +++ b/tests/kani/Intrinsics/Math/Arith/powf64.rs @@ -0,0 +1,30 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// This test will trigger use of the `powf64` intrinsic, which in turn invoke functions modelled in +// CBMC's math library. These models use approximations as documented in CBMC's source code: +// https://github.com/diffblue/cbmc/blob/develop/src/ansi-c/library/math.c. + +pub fn f(a: u64) -> u64 { + const C: f64 = 0.618; + (a as f64).powf(C) as u64 +} + +#[cfg(kani)] +mod verification { + use super::*; + + #[kani::proof] + fn verify_f() { + const LIMIT: u64 = 10; + let x: u64 = kani::any(); + let y: u64 = kani::any(); + // outside these limits our approximation may yield spurious results + kani::assume(x > LIMIT && x < LIMIT * 3); + kani::assume(y > LIMIT && y < LIMIT * 3); + kani::assume(x > y); + let x_ = f(x); + let y_ = f(y); + assert!(x_ >= y_); + } +} From f8fabc4c6e08d2a8e28a39f316c54e8342d9c77e Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 7 Feb 2024 19:07:40 -0800 Subject: [PATCH 003/225] Upgrade Rust toolchain to nightly-2024-01-23 (#2983) Related PRs so far: - https://github.com/rust-lang/rust/pull/119869 - https://github.com/rust-lang/rust/pull/120080 - https://github.com/rust-lang/rust/pull/120128 - https://github.com/rust-lang/rust/pull/119369 - https://github.com/rust-lang/rust/pull/116672 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: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: tautschnig Co-authored-by: Qinheping Hu Co-authored-by: Michael Tautschnig Co-authored-by: Felipe R. Monteiro --- .../codegen_cprover_gotoc/codegen/contract.rs | 2 +- .../codegen/intrinsic.rs | 50 ++++++++++--------- .../codegen_cprover_gotoc/codegen/place.rs | 9 ++-- .../codegen_cprover_gotoc/codegen/rvalue.rs | 17 ++++--- .../src/codegen_cprover_gotoc/codegen/span.rs | 2 +- .../codegen/statement.rs | 8 +-- .../codegen/ty_stable.rs | 40 +++++++-------- .../src/codegen_cprover_gotoc/codegen/typ.rs | 4 +- .../compiler_interface.rs | 2 +- .../context/current_fn.rs | 19 ++++--- .../codegen_cprover_gotoc/overrides/hooks.rs | 4 +- .../src/codegen_cprover_gotoc/utils/utils.rs | 2 +- kani-compiler/src/kani_middle/attributes.rs | 10 ++-- kani-compiler/src/kani_middle/coercion.rs | 18 +++---- kani-compiler/src/kani_middle/mod.rs | 10 ++-- kani-compiler/src/kani_middle/reachability.rs | 20 ++++---- kani-compiler/src/kani_middle/resolve.rs | 9 ++-- kani-compiler/src/kani_middle/stubbing/mod.rs | 8 +-- .../src/sysroot/contracts/bootstrap.rs | 2 +- rust-toolchain.toml | 2 +- .../Intrinsics/AlignOfVal/align_of_basic.rs | 4 +- .../Intrinsics/AlignOfVal/align_of_fat_ptr.rs | 2 +- .../kani/Intrinsics/ConstEval/min_align_of.rs | 4 +- .../Intrinsics/ConstEval/pref_align_of.rs | 4 +- tests/perf/hashset/expected | 2 +- tests/perf/hashset/src/lib.rs | 42 ++++++++-------- 26 files changed, 157 insertions(+), 139 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs index 964286984fc6..2bd3e1ff7c35 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs @@ -39,7 +39,7 @@ impl<'tcx> GotocCtx<'tcx> { let mut instance_under_contract = items.iter().filter_map(|i| match i { MonoItem::Fn(instance @ Instance { def, .. }) - if wrapped_fn == rustc_internal::internal(def.def_id()) => + if wrapped_fn == rustc_internal::internal(tcx, def.def_id()) => { Some(*instance) } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 0709682afa65..6c711e3d20e4 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -3,7 +3,7 @@ //! this module handles intrinsics use super::typ; use super::{bb_label, PropertyClass}; -use crate::codegen_cprover_gotoc::codegen::ty_stable::{pointee_type_stable, pretty_ty}; +use crate::codegen_cprover_gotoc::codegen::ty_stable::pointee_type_stable; use crate::codegen_cprover_gotoc::{utils, GotocCtx}; use crate::unwrap_or_return_codegen_unimplemented_stmt; use cbmc::goto_program::{ @@ -646,7 +646,7 @@ impl<'tcx> GotocCtx<'tcx> { span, format!( "Type check failed for intrinsic `{name}`: Expected {expected}, found {}", - pretty_ty(actual) + self.pretty_ty(actual) ), ); self.tcx.dcx().abort_if_errors(); @@ -779,12 +779,16 @@ impl<'tcx> GotocCtx<'tcx> { if layout.abi.is_uninhabited() { return self.codegen_fatal_error( PropertyClass::SafetyCheck, - &format!("attempted to instantiate uninhabited type `{}`", pretty_ty(*target_ty)), + &format!( + "attempted to instantiate uninhabited type `{}`", + self.pretty_ty(*target_ty) + ), span, ); } - let param_env_and_type = ParamEnv::reveal_all().and(rustc_internal::internal(target_ty)); + let param_env_and_type = + ParamEnv::reveal_all().and(rustc_internal::internal(self.tcx, target_ty)); // Then we check if the type allows "raw" initialization for the cases // where memory is zero-initialized or entirely uninitialized @@ -798,7 +802,7 @@ impl<'tcx> GotocCtx<'tcx> { PropertyClass::SafetyCheck, &format!( "attempted to zero-initialize type `{}`, which is invalid", - pretty_ty(*target_ty) + self.pretty_ty(*target_ty) ), span, ); @@ -817,7 +821,7 @@ impl<'tcx> GotocCtx<'tcx> { PropertyClass::SafetyCheck, &format!( "attempted to leave type `{}` uninitialized, which is invalid", - pretty_ty(*target_ty) + self.pretty_ty(*target_ty) ), span, ); @@ -1285,7 +1289,7 @@ impl<'tcx> GotocCtx<'tcx> { _ => { let err_msg = format!( "simd_shuffle index must be an array of `u32`, got `{}`", - pretty_ty(farg_types[2]) + self.pretty_ty(farg_types[2]) ); utils::span_err(self.tcx, span, err_msg); // Return a dummy value @@ -1378,7 +1382,7 @@ impl<'tcx> GotocCtx<'tcx> { // Packed types ignore the alignment of their fields. if let TyKind::RigidTy(RigidTy::Adt(def, _)) = ty.kind() { - if rustc_internal::internal(def).repr().packed() { + if rustc_internal::internal(self.tcx, def).repr().packed() { unsized_align = sized_align.clone(); } } @@ -1426,9 +1430,9 @@ impl<'tcx> GotocCtx<'tcx> { if rust_ret_type != vector_base_type { let err_msg = format!( "expected return type `{}` (element of input `{}`), found `{}`", - pretty_ty(vector_base_type), - pretty_ty(rust_arg_types[0]), - pretty_ty(rust_ret_type) + self.pretty_ty(vector_base_type), + self.pretty_ty(rust_arg_types[0]), + self.pretty_ty(rust_ret_type) ); utils::span_err(self.tcx, span, err_msg); } @@ -1466,9 +1470,9 @@ impl<'tcx> GotocCtx<'tcx> { if vector_base_type != rust_arg_types[2] { let err_msg = format!( "expected inserted type `{}` (element of input `{}`), found `{}`", - pretty_ty(vector_base_type), - pretty_ty(rust_arg_types[0]), - pretty_ty(rust_arg_types[2]), + self.pretty_ty(vector_base_type), + self.pretty_ty(rust_arg_types[0]), + self.pretty_ty(rust_arg_types[2]), ); utils::span_err(self.tcx, span, err_msg); } @@ -1534,8 +1538,8 @@ impl<'tcx> GotocCtx<'tcx> { "expected return type with length {} (same as input type `{}`), \ found `{}` with length {}", arg1.typ().len().unwrap(), - pretty_ty(rust_arg_types[0]), - pretty_ty(rust_ret_type), + self.pretty_ty(rust_arg_types[0]), + self.pretty_ty(rust_ret_type), ret_typ.len().unwrap() ); utils::span_err(self.tcx, span, err_msg); @@ -1545,8 +1549,8 @@ impl<'tcx> GotocCtx<'tcx> { let (_, rust_base_type) = self.simd_size_and_type(rust_ret_type); let err_msg = format!( "expected return type with integer elements, found `{}` with non-integer `{}`", - pretty_ty(rust_ret_type), - pretty_ty(rust_base_type), + self.pretty_ty(rust_ret_type), + self.pretty_ty(rust_base_type), ); utils::span_err(self.tcx, span, err_msg); } @@ -1740,7 +1744,7 @@ impl<'tcx> GotocCtx<'tcx> { if ret_type_len != n { let err_msg = format!( "expected return type of length {n}, found `{}` with length {ret_type_len}", - pretty_ty(rust_ret_type), + self.pretty_ty(rust_ret_type), ); utils::span_err(self.tcx, span, err_msg); } @@ -1748,10 +1752,10 @@ impl<'tcx> GotocCtx<'tcx> { let err_msg = format!( "expected return element type `{}` (element of input `{}`), \ found `{}` with element type `{}`", - pretty_ty(vec_subtype), - pretty_ty(rust_arg_types[0]), - pretty_ty(rust_ret_type), - pretty_ty(ret_type_subtype), + self.pretty_ty(vec_subtype), + self.pretty_ty(rust_arg_types[0]), + self.pretty_ty(rust_ret_type), + self.pretty_ty(ret_type_subtype), ); utils::span_err(self.tcx, span, err_msg); } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs index fd9ff33e164e..9296b713c862 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs @@ -260,7 +260,7 @@ impl<'tcx> GotocCtx<'tcx> { Ok(parent_expr.member(Self::tuple_fld_name(field_idx), &self.symbol_table)) } TyKind::RigidTy(RigidTy::Adt(def, _)) - if rustc_internal::internal(def).repr().simd() => + if rustc_internal::internal(self.tcx, def).repr().simd() => { Ok(self.codegen_simd_field( parent_expr, @@ -415,7 +415,7 @@ impl<'tcx> GotocCtx<'tcx> { }; let inner_mir_typ_internal = - std_pointee_type(rustc_internal::internal(base_type)).unwrap(); + std_pointee_type(rustc_internal::internal(self.tcx, base_type)).unwrap(); let inner_mir_typ = rustc_internal::stable(inner_mir_typ_internal); let (fat_ptr_mir_typ, fat_ptr_goto_expr) = if self .use_thin_pointer(inner_mir_typ_internal) @@ -436,6 +436,7 @@ impl<'tcx> GotocCtx<'tcx> { ); assert!( self.use_fat_pointer(rustc_internal::internal( + self.tcx, pointee_type(fat_ptr_mir_typ.unwrap()).unwrap() )), "Unexpected type: {:?} -- {:?}", @@ -582,7 +583,7 @@ impl<'tcx> GotocCtx<'tcx> { (variant.name().into(), TypeOrVariant::Variant(variant)) } TyKind::RigidTy(RigidTy::Coroutine(..)) => { - let idx_internal = rustc_internal::internal(idx); + let idx_internal = rustc_internal::internal(self.tcx, idx); ( self.coroutine_variant_name(idx_internal), TypeOrVariant::CoroutineVariant(*idx), @@ -593,7 +594,7 @@ impl<'tcx> GotocCtx<'tcx> { &ty.kind() ), }; - let layout = self.layout_of(rustc_internal::internal(ty)); + let layout = self.layout_of(rustc_internal::internal(self.tcx, ty)); let expr = match &layout.variants { Variants::Single { .. } => before.goto_expr, Variants::Multiple { tag_encoding, .. } => match tag_encoding { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index c4ea258fe79e..e7501df3aac4 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -555,7 +555,7 @@ impl<'tcx> GotocCtx<'tcx> { let layout = self.layout_of_stable(res_ty); let fields = match &layout.variants { Variants::Single { index } => { - if *index != rustc_internal::internal(variant_index) { + if *index != rustc_internal::internal(self.tcx, variant_index) { // This may occur if all variants except for the one pointed by // index can never be constructed. Generic code might still try // to initialize the non-existing invariant. @@ -565,7 +565,7 @@ impl<'tcx> GotocCtx<'tcx> { &layout.fields } Variants::Multiple { variants, .. } => { - &variants[rustc_internal::internal(variant_index)].fields + &variants[rustc_internal::internal(self.tcx, variant_index)].fields } }; @@ -716,7 +716,10 @@ impl<'tcx> GotocCtx<'tcx> { .offset_of_subfield( self, fields.iter().map(|(var_idx, field_idx)| { - (rustc_internal::internal(var_idx), (*field_idx).into()) + ( + rustc_internal::internal(self.tcx, var_idx), + (*field_idx).into(), + ) }), ) .bytes(), @@ -1180,7 +1183,7 @@ impl<'tcx> GotocCtx<'tcx> { if self.vtable_ctx.emit_vtable_restrictions { // Add to the possible method names for this trait type self.vtable_ctx.add_possible_method( - self.normalized_trait_name(rustc_internal::internal(ty)).into(), + self.normalized_trait_name(rustc_internal::internal(self.tcx, ty)).into(), idx, fn_name.into(), ); @@ -1205,7 +1208,7 @@ impl<'tcx> GotocCtx<'tcx> { /// Generate a function pointer to drop_in_place for entry into the vtable fn codegen_vtable_drop_in_place(&mut self, ty: Ty, trait_ty: Ty) -> Expr { - let trait_ty = rustc_internal::internal(trait_ty); + let trait_ty = rustc_internal::internal(self.tcx, trait_ty); let drop_instance = Instance::resolve_drop_in_place(ty); let drop_sym_name: InternedString = drop_instance.mangled_name().into(); @@ -1296,7 +1299,7 @@ impl<'tcx> GotocCtx<'tcx> { _ => unreachable!("Cannot codegen_vtable for type {:?}", dst_mir_type.kind()), }; - let src_name = self.ty_mangled_name(rustc_internal::internal(src_mir_type)); + let src_name = self.ty_mangled_name(rustc_internal::internal(self.tcx, src_mir_type)); // The name needs to be the same as inserted in typ.rs let vtable_name = self.vtable_name_stable(trait_type).intern(); let vtable_impl_name = format!("{vtable_name}_impl_for_{src_name}"); @@ -1310,7 +1313,7 @@ impl<'tcx> GotocCtx<'tcx> { // 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(trait_ref_binder)) + ctx.tcx.vtable_entries(rustc_internal::internal(ctx.tcx, trait_ref_binder)) } else { TyCtxt::COMMON_VTABLE_ENTRIES }; diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs index c06d64d298c4..d06829822274 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs @@ -31,7 +31,7 @@ impl<'tcx> GotocCtx<'tcx> { } pub fn codegen_caller_span_stable(&self, sp: SpanStable) -> Location { - self.codegen_caller_span(&rustc_internal::internal(sp)) + self.codegen_caller_span(&rustc_internal::internal(self.tcx, sp)) } /// Get the location of the caller. This will attempt to reach the macro caller. diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 3a62a3f91d1a..05eb1bc41dc7 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -220,8 +220,8 @@ impl<'tcx> GotocCtx<'tcx> { location: Location, ) -> Stmt { // this requires place points to an enum type. - let dest_ty_internal = rustc_internal::internal(dest_ty); - let variant_index_internal = rustc_internal::internal(variant_index); + let dest_ty_internal = rustc_internal::internal(self.tcx, dest_ty); + let variant_index_internal = rustc_internal::internal(self.tcx, variant_index); let layout = self.layout_of(dest_ty_internal); match &layout.variants { Variants::Single { .. } => Stmt::skip(location), @@ -533,7 +533,7 @@ impl<'tcx> GotocCtx<'tcx> { let instance = Instance::resolve(def, &subst).unwrap(); // TODO(celina): Move this check to be inside codegen_funcall_args. - if self.ty_needs_untupled_args(rustc_internal::internal(funct)) { + if self.ty_needs_untupled_args(rustc_internal::internal(self.tcx, funct)) { self.codegen_untupled_args(instance, &mut fargs, args.last()); } @@ -595,7 +595,7 @@ impl<'tcx> GotocCtx<'tcx> { fn extract_ptr(&self, arg_expr: Expr, arg_ty: Ty) -> Expr { // Generate an expression that indexes the pointer. let expr = self - .receiver_data_path(rustc_internal::internal(arg_ty)) + .receiver_data_path(rustc_internal::internal(self.tcx, arg_ty)) .fold(arg_expr, |curr_expr, (name, _)| curr_expr.member(name, &self.symbol_table)); trace!(?arg_ty, gotoc_ty=?expr.typ(), gotoc_expr=?expr.value(), "extract_ptr"); diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/ty_stable.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/ty_stable.rs index c4f03e6c2cef..0130beffe84b 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/ty_stable.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/ty_stable.rs @@ -20,11 +20,11 @@ impl<'tcx> GotocCtx<'tcx> { } pub fn codegen_ty_stable(&mut self, ty: Ty) -> Type { - self.codegen_ty(rustc_internal::internal(ty)) + self.codegen_ty(rustc_internal::internal(self.tcx, ty)) } pub fn codegen_ty_ref_stable(&mut self, ty: Ty) -> Type { - self.codegen_ty_ref(rustc_internal::internal(ty)) + self.codegen_ty_ref(rustc_internal::internal(self.tcx, ty)) } pub fn local_ty_stable(&self, local: Local) -> Ty { @@ -36,11 +36,11 @@ impl<'tcx> GotocCtx<'tcx> { } pub fn is_zst_stable(&self, ty: Ty) -> bool { - self.is_zst(rustc_internal::internal(ty)) + self.is_zst(rustc_internal::internal(self.tcx, ty)) } pub fn layout_of_stable(&self, ty: Ty) -> TyAndLayout<'tcx> { - self.layout_of(rustc_internal::internal(ty)) + self.layout_of(rustc_internal::internal(self.tcx, ty)) } pub fn codegen_fndef_type_stable(&mut self, instance: Instance) -> Type { @@ -53,27 +53,27 @@ impl<'tcx> GotocCtx<'tcx> { } pub fn use_fat_pointer_stable(&self, pointer_ty: Ty) -> bool { - self.use_fat_pointer(rustc_internal::internal(pointer_ty)) + self.use_fat_pointer(rustc_internal::internal(self.tcx, pointer_ty)) } pub fn use_thin_pointer_stable(&self, pointer_ty: Ty) -> bool { - self.use_thin_pointer(rustc_internal::internal(pointer_ty)) + self.use_thin_pointer(rustc_internal::internal(self.tcx, pointer_ty)) } pub fn is_fat_pointer_stable(&self, pointer_ty: Ty) -> bool { - self.is_fat_pointer(rustc_internal::internal(pointer_ty)) + self.is_fat_pointer(rustc_internal::internal(self.tcx, pointer_ty)) } pub fn is_vtable_fat_pointer_stable(&self, pointer_ty: Ty) -> bool { - self.is_vtable_fat_pointer(rustc_internal::internal(pointer_ty)) + self.is_vtable_fat_pointer(rustc_internal::internal(self.tcx, pointer_ty)) } pub fn use_vtable_fat_pointer_stable(&self, pointer_ty: Ty) -> bool { - self.use_vtable_fat_pointer(rustc_internal::internal(pointer_ty)) + self.use_vtable_fat_pointer(rustc_internal::internal(self.tcx, pointer_ty)) } pub fn vtable_name_stable(&self, ty: Ty) -> String { - self.vtable_name(rustc_internal::internal(ty)) + self.vtable_name(rustc_internal::internal(self.tcx, ty)) } pub fn rvalue_ty_stable(&self, rvalue: &Rvalue) -> Ty { @@ -81,12 +81,12 @@ impl<'tcx> GotocCtx<'tcx> { } pub fn simd_size_and_type(&self, ty: Ty) -> (u64, Ty) { - let (sz, ty) = rustc_internal::internal(ty).simd_size_and_type(self.tcx); + let (sz, ty) = rustc_internal::internal(self.tcx, ty).simd_size_and_type(self.tcx); (sz, rustc_internal::stable(ty)) } pub fn codegen_enum_discr_typ_stable(&self, ty: Ty) -> Ty { - rustc_internal::stable(self.codegen_enum_discr_typ(rustc_internal::internal(ty))) + rustc_internal::stable(self.codegen_enum_discr_typ(rustc_internal::internal(self.tcx, ty))) } pub fn codegen_function_sig_stable(&mut self, sig: FnSig) -> Type { @@ -107,6 +107,14 @@ impl<'tcx> GotocCtx<'tcx> { Type::code_with_unnamed_parameters(params, self.codegen_ty_stable(sig.output())) } } + + /// Convert a type into a user readable type representation. + /// + /// This should be replaced by StableMIR `pretty_ty()` after + /// is merged. + pub fn pretty_ty(&self, ty: Ty) -> String { + rustc_internal::internal(self.tcx, ty).to_string() + } } /// If given type is a Ref / Raw ref, return the pointee type. pub fn pointee_type(mir_type: Ty) -> Option { @@ -117,14 +125,6 @@ pub fn pointee_type(mir_type: Ty) -> Option { } } -/// Convert a type into a user readable type representation. -/// -/// This should be replaced by StableMIR `pretty_ty()` after -/// is merged. -pub fn pretty_ty(ty: Ty) -> String { - rustc_internal::internal(ty).to_string() -} - pub fn pointee_type_stable(ty: Ty) -> Option { match ty.kind() { TyKind::RigidTy(RigidTy::Ref(_, pointee_ty, _)) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index 83ec6c3374ae..62b5cbedd95a 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -1101,7 +1101,7 @@ impl<'tcx> GotocCtx<'tcx> { .map(|(_, arg_abi)| { let arg_ty_stable = arg_abi.ty; let kind = arg_ty_stable.kind(); - let arg_ty = rustc_internal::internal(arg_ty_stable); + let arg_ty = rustc_internal::internal(self.tcx, arg_ty_stable); if is_first { is_first = false; debug!(self_type=?arg_ty, ?fn_abi, "codegen_dynamic_function_sig"); @@ -1686,7 +1686,7 @@ impl<'tcx> GotocCtx<'tcx> { instance: InstanceStable, fn_abi: &'a FnAbi, ) -> impl Iterator { - let instance_internal = rustc_internal::internal(instance); + let instance_internal = rustc_internal::internal(self.tcx, instance); let requires_caller_location = instance_internal.def.requires_caller_location(self.tcx); let num_args = fn_abi.args.len(); fn_abi.args.iter().enumerate().filter(move |(idx, arg_abi)| { diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index 1cfff307f856..810c5707aad4 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -307,7 +307,7 @@ impl CodegenBackend for GotocCodegenBackend { let main_instance = stable_mir::entry_fn().map(|main_fn| Instance::try_from(main_fn).unwrap()); let local_reachable = filter_crate_items(tcx, |_, instance| { - let def_id = rustc_internal::internal(instance.def.def_id()); + let def_id = rustc_internal::internal(tcx, instance.def.def_id()); Some(instance) == main_instance || tcx.is_reachable_non_generic(def_id) }) .into_iter() diff --git a/kani-compiler/src/codegen_cprover_gotoc/context/current_fn.rs b/kani-compiler/src/codegen_cprover_gotoc/context/current_fn.rs index b8251b153e5a..adac294e30eb 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/current_fn.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/current_fn.rs @@ -4,8 +4,8 @@ use crate::codegen_cprover_gotoc::GotocCtx; use cbmc::goto_program::Stmt; use cbmc::InternedString; -use rustc_middle::mir::Body as InternalBody; -use rustc_middle::ty::Instance as InternalInstance; +use rustc_middle::mir::Body as BodyInternal; +use rustc_middle::ty::Instance as InstanceInternal; use rustc_smir::rustc_internal; use stable_mir::mir::mono::Instance; use stable_mir::mir::{Body, Local, LocalDecl}; @@ -22,7 +22,9 @@ pub struct CurrentFnCtx<'tcx> { /// The crate this function is from krate: String, /// The MIR for the current instance. This is using the internal representation. - mir: &'tcx InternalBody<'tcx>, + mir: &'tcx BodyInternal<'tcx>, + /// The current instance. This is using the internal representation. + instance_internal: InstanceInternal<'tcx>, /// A list of local declarations used to retrieve MIR component types. locals: Vec, /// A list of pretty names for locals that corrspond to user variables. @@ -38,7 +40,7 @@ pub struct CurrentFnCtx<'tcx> { /// Constructor impl<'tcx> CurrentFnCtx<'tcx> { pub fn new(instance: Instance, gcx: &GotocCtx<'tcx>, body: &Body) -> Self { - let internal_instance = rustc_internal::internal(instance); + let instance_internal = rustc_internal::internal(gcx.tcx, instance); let readable_name = instance.name(); let name = if &readable_name == "main" { readable_name.clone() } else { instance.mangled_name() }; @@ -51,7 +53,8 @@ impl<'tcx> CurrentFnCtx<'tcx> { Self { block: vec![], instance, - mir: gcx.tcx.instance_mir(internal_instance.def), + mir: gcx.tcx.instance_mir(instance_internal.def), + instance_internal, krate: instance.def.krate().name, locals, local_names, @@ -83,8 +86,8 @@ impl<'tcx> CurrentFnCtx<'tcx> { /// Getters impl<'tcx> CurrentFnCtx<'tcx> { /// The function we are currently compiling - pub fn instance(&self) -> InternalInstance<'tcx> { - rustc_internal::internal(self.instance) + pub fn instance(&self) -> InstanceInternal<'tcx> { + self.instance_internal } pub fn instance_stable(&self) -> Instance { @@ -92,7 +95,7 @@ impl<'tcx> CurrentFnCtx<'tcx> { } /// The internal MIR for the function we are currently compiling using internal APIs. - pub fn body_internal(&self) -> &'tcx InternalBody<'tcx> { + pub fn body_internal(&self) -> &'tcx BodyInternal<'tcx> { self.mir } diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index 97606688d1ed..0141c2886539 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -38,7 +38,7 @@ pub trait GotocHook { fn matches_function(tcx: TyCtxt, instance: Instance, attr_name: &str) -> bool { let attr_sym = rustc_span::symbol::Symbol::intern(attr_name); if let Some(attr_id) = tcx.all_diagnostic_items(()).name_to_id.get(&attr_sym) { - if rustc_internal::internal(instance.def.def_id()) == *attr_id { + if rustc_internal::internal(tcx, instance.def.def_id()) == *attr_id { debug!("matched: {:?} {:?}", attr_id, attr_sym); return true; } @@ -196,7 +196,7 @@ struct Panic; impl GotocHook for Panic { fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { - let def_id = rustc_internal::internal(instance.def.def_id()); + let def_id = rustc_internal::internal(tcx, instance.def.def_id()); Some(def_id) == tcx.lang_items().panic_fn() || tcx.has_attr(def_id, rustc_span::sym::rustc_const_panic_str) || Some(def_id) == tcx.lang_items().panic_fmt() diff --git a/kani-compiler/src/codegen_cprover_gotoc/utils/utils.rs b/kani-compiler/src/codegen_cprover_gotoc/utils/utils.rs index d81839ea097c..e369f64beda9 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/utils/utils.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/utils/utils.rs @@ -194,5 +194,5 @@ impl<'tcx> GotocCtx<'tcx> { } pub fn span_err(tcx: TyCtxt, span: Span, msg: String) { - tcx.dcx().span_err(rustc_internal::internal(span), msg); + tcx.dcx().span_err(rustc_internal::internal(tcx, span), msg); } diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index e9edbedfcd82..32425204d76a 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -141,7 +141,7 @@ impl<'tcx> KaniAttributes<'tcx> { /// Look up the attributes by a stable MIR DefID pub fn for_def_id(tcx: TyCtxt<'tcx>, def_id: StableDefId) -> Self { - KaniAttributes::for_item(tcx, rustc_internal::internal(def_id)) + KaniAttributes::for_item(tcx, rustc_internal::internal(tcx, def_id)) } pub fn for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> Self { @@ -671,7 +671,7 @@ pub fn is_function_contract_generated(tcx: TyCtxt, def_id: DefId) -> bool { /// Same as [`KaniAttributes::is_harness`] but more efficient because less /// attribute parsing is performed. pub fn is_proof_harness(tcx: TyCtxt, instance: InstanceStable) -> bool { - let def_id = rustc_internal::internal(instance.def.def_id()); + let def_id = rustc_internal::internal(tcx, instance.def.def_id()); has_kani_attribute(tcx, def_id, |a| { matches!(a, KaniAttributeKind::Proof | KaniAttributeKind::ProofForContract) }) @@ -679,14 +679,14 @@ pub fn is_proof_harness(tcx: TyCtxt, instance: InstanceStable) -> bool { /// Does this `def_id` have `#[rustc_test_marker]`? pub fn is_test_harness_description(tcx: TyCtxt, item: impl CrateDef) -> bool { - let def_id = rustc_internal::internal(item.def_id()); + let def_id = rustc_internal::internal(tcx, item.def_id()); let attrs = tcx.get_attrs_unchecked(def_id); attr::contains_name(attrs, rustc_span::symbol::sym::rustc_test_marker) } /// Extract the test harness name from the `#[rustc_test_maker]` pub fn test_harness_name(tcx: TyCtxt, def: &impl CrateDef) -> String { - let def_id = rustc_internal::internal(def.def_id()); + 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() @@ -944,7 +944,7 @@ fn parse_integer(attr: &Attribute) -> Option { if attr_args.len() == 1 { let x = attr_args[0].lit()?; match x.kind { - LitKind::Int(y, ..) => Some(y), + LitKind::Int(y, ..) => Some(y.get()), _ => None, } } diff --git a/kani-compiler/src/kani_middle/coercion.rs b/kani-compiler/src/kani_middle/coercion.rs index 85d59215d661..94879b5b65e6 100644 --- a/kani-compiler/src/kani_middle/coercion.rs +++ b/kani-compiler/src/kani_middle/coercion.rs @@ -66,8 +66,8 @@ pub fn extract_unsize_casting_stable( ) -> CoercionBaseStable { let CoercionBase { src_ty, dst_ty } = extract_unsize_casting( tcx, - rustc_internal::internal(src_ty), - rustc_internal::internal(dst_ty), + rustc_internal::internal(tcx, src_ty), + rustc_internal::internal(tcx, dst_ty), ); CoercionBaseStable { src_ty: rustc_internal::stable(src_ty), @@ -90,11 +90,11 @@ pub fn extract_unsize_casting<'tcx>( .last() .unwrap(); // Extract the pointee type that is being coerced. - let src_pointee_ty = extract_pointee(coerce_info.src_ty).expect(&format!( + 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(coerce_info.dst_ty).expect(&format!( + 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 )); @@ -216,8 +216,8 @@ impl<'tcx> Iterator for CoerceUnsizedIterator<'tcx> { let CustomCoerceUnsized::Struct(coerce_index) = custom_coerce_unsize_info( self.tcx, - rustc_internal::internal(src_ty), - rustc_internal::internal(dst_ty), + rustc_internal::internal(self.tcx, src_ty), + rustc_internal::internal(self.tcx, dst_ty), ); let coerce_index = coerce_index.as_usize(); assert!(coerce_index < src_fields.len()); @@ -229,7 +229,7 @@ impl<'tcx> Iterator for CoerceUnsizedIterator<'tcx> { _ => { // Base case is always a pointer (Box, raw_pointer or reference). assert!( - extract_pointee(src_ty).is_some(), + extract_pointee(self.tcx, src_ty).is_some(), "Expected a pointer, but found {src_ty:?}" ); None @@ -262,6 +262,6 @@ fn custom_coerce_unsize_info<'tcx>( } /// Extract pointee type from builtin pointer types. -fn extract_pointee<'tcx>(typ: TyStable) -> Option> { - rustc_internal::internal(typ).builtin_deref(true).map(|TypeAndMut { ty, .. }| ty) +fn extract_pointee(tcx: TyCtxt<'_>, typ: TyStable) -> Option> { + rustc_internal::internal(tcx, typ).builtin_deref(true).map(|TypeAndMut { ty, .. }| ty) } diff --git a/kani-compiler/src/kani_middle/mod.rs b/kani-compiler/src/kani_middle/mod.rs index 66fb2ca0aa33..e65befc9624e 100644 --- a/kani-compiler/src/kani_middle/mod.rs +++ b/kani-compiler/src/kani_middle/mod.rs @@ -92,7 +92,10 @@ pub fn check_reachable_items(tcx: TyCtxt, queries: &QueryDb, items: &[MonoItem]) // We don't short circuit here since this is a type check and can shake // out differently depending on generic parameters. if let MonoItem::Fn(instance) = item { - if attributes::is_function_contract_generated(tcx, rustc_internal::internal(def_id)) { + if attributes::is_function_contract_generated( + tcx, + rustc_internal::internal(tcx, def_id), + ) { check_is_contract_safe(tcx, *instance); } } @@ -167,7 +170,7 @@ fn check_is_contract_safe(tcx: TyCtxt, instance: Instance) { for var in &bound_fn_sig.bound_vars { if let BoundVariableKind::Ty(t) = var { tcx.dcx().span_err( - rustc_internal::internal(instance.def.span()), + rustc_internal::internal(tcx, instance.def.span()), format!("Found a bound type variable {t:?} after monomorphization"), ); } @@ -211,7 +214,8 @@ pub fn dump_mir_items(tcx: TyCtxt, items: &[MonoItem], output: &Path) { // For each def_id, dump their MIR for (item, def_id) in items.iter().filter_map(visible_item) { writeln!(writer, "// Item: {item:?}").unwrap(); - write_mir_pretty(tcx, Some(rustc_internal::internal(def_id)), &mut writer).unwrap(); + write_mir_pretty(tcx, Some(rustc_internal::internal(tcx, def_id)), &mut writer) + .unwrap(); } } } diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 424077a622f9..63ed41bfd4c1 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -75,7 +75,7 @@ where .filter_map(|item| { // Only collect monomorphic items. // TODO: Remove the def_kind check once https://github.com/rust-lang/rust/pull/119135 has been released. - let def_id = rustc_internal::internal(item.def_id()); + let def_id = rustc_internal::internal(tcx, item.def_id()); (matches!(tcx.def_kind(def_id), rustc_hir::def::DefKind::Ctor(..)) || matches!(item.kind(), ItemKind::Fn)) .then(|| { @@ -237,7 +237,8 @@ impl<'a, 'tcx> MonoItemsFnCollector<'a, 'tcx> { let poly_trait_ref = principal.with_self_ty(concrete_ty); // Walk all methods of the trait, including those of its supertraits - let entries = self.tcx.vtable_entries(rustc_internal::internal(&poly_trait_ref)); + let entries = + self.tcx.vtable_entries(rustc_internal::internal(self.tcx, &poly_trait_ref)); let methods = entries.iter().filter_map(|entry| match entry { VtblEntry::MetadataAlign | VtblEntry::MetadataDropInPlace @@ -395,9 +396,10 @@ impl<'a, 'tcx> MirVisitor for MonoItemsFnCollector<'a, 'tcx> { let caller = CrateItem::try_from(*self.instance).unwrap().name(); let callee = fn_def.name(); // Check if the current function has been stubbed. - if let Some(stub) = - get_stub(self.tcx, rustc_internal::internal(self.instance).def_id()) - { + if let Some(stub) = get_stub( + self.tcx, + rustc_internal::internal(self.tcx, self.instance).def_id(), + ) { // During the MIR stubbing transformation, we do not // force type variables in the stub's signature to // implement the same traits as those in the @@ -413,7 +415,7 @@ impl<'a, 'tcx> MirVisitor for MonoItemsFnCollector<'a, 'tcx> { let sep = callee.rfind("::").unwrap(); let trait_ = &callee[..sep]; self.tcx.dcx().span_err( - rustc_internal::internal(terminator.span), + rustc_internal::internal(self.tcx, terminator.span), format!( "`{}` doesn't implement \ `{}`. The function `{}` \ @@ -465,8 +467,8 @@ impl<'a, 'tcx> MirVisitor for MonoItemsFnCollector<'a, 'tcx> { fn extract_unsize_coercion(tcx: TyCtxt, orig_ty: Ty, dst_trait: Ty) -> (Ty, Ty) { let CoercionBase { src_ty, dst_ty } = coercion::extract_unsize_casting( tcx, - rustc_internal::internal(orig_ty), - rustc_internal::internal(dst_trait), + rustc_internal::internal(tcx, orig_ty), + rustc_internal::internal(tcx, dst_trait), ); (rustc_internal::stable(src_ty), rustc_internal::stable(dst_ty)) } @@ -476,7 +478,7 @@ fn extract_unsize_coercion(tcx: TyCtxt, orig_ty: Ty, dst_trait: Ty) -> (Ty, Ty) fn to_fingerprint(tcx: TyCtxt, item: &MonoItem) -> Fingerprint { tcx.with_stable_hashing_context(|mut hcx| { let mut hasher = StableHasher::new(); - rustc_internal::internal(item).hash_stable(&mut hcx, &mut hasher); + rustc_internal::internal(tcx, item).hash_stable(&mut hcx, &mut hasher); hasher.finish() }) } diff --git a/kani-compiler/src/kani_middle/resolve.rs b/kani-compiler/src/kani_middle/resolve.rs index e88485ebc6d7..53670bfd482d 100644 --- a/kani-compiler/src/kani_middle/resolve.rs +++ b/kani-compiler/src/kani_middle/resolve.rs @@ -399,8 +399,11 @@ fn resolve_in_type<'tcx>( name: &str, ) -> Result> { debug!(?name, ?type_id, "resolve_in_type"); + let missing_item_err = + || ResolveError::MissingItem { tcx, base: type_id, unresolved: name.to_string() }; // Try the inherent `impl` blocks (i.e., non-trait `impl`s). tcx.inherent_impls(type_id) + .map_err(|_| missing_item_err())? .iter() .flat_map(|impl_id| tcx.associated_item_def_ids(impl_id)) .cloned() @@ -409,9 +412,5 @@ fn resolve_in_type<'tcx>( let last = item_path.split("::").last().unwrap(); last == name }) - .ok_or_else(|| ResolveError::MissingItem { - tcx, - base: type_id, - unresolved: name.to_string(), - }) + .ok_or_else(missing_item_err) } diff --git a/kani-compiler/src/kani_middle/stubbing/mod.rs b/kani-compiler/src/kani_middle/stubbing/mod.rs index 9dda0144eda4..0db518ceef9f 100644 --- a/kani-compiler/src/kani_middle/stubbing/mod.rs +++ b/kani-compiler/src/kani_middle/stubbing/mod.rs @@ -27,7 +27,7 @@ pub fn harness_stub_map( harness: Instance, metadata: &HarnessMetadata, ) -> BTreeMap { - let def_id = rustc_internal::internal(harness.def.def_id()); + let def_id = rustc_internal::internal(tcx, harness.def.def_id()); let attrs = &metadata.attributes; let mut stub_pairs = BTreeMap::default(); for stubs in &attrs.stubs { @@ -44,7 +44,7 @@ pub fn harness_stub_map( /// In stable MIR, trying to retrieve an `Instance::body()` will ICE if we cannot evaluate a /// constant as expected. For now, use internal APIs to anticipate this issue. pub fn validate_instance(tcx: TyCtxt, instance: Instance) -> bool { - let internal_instance = rustc_internal::internal(instance); + let internal_instance = rustc_internal::internal(tcx, instance); if get_stub(tcx, internal_instance.def_id()).is_some() { debug!(?instance, "validate_instance"); let item = CrateItem::try_from(instance).unwrap(); @@ -87,7 +87,7 @@ impl<'tcx> StubConstChecker<'tcx> { impl<'tcx> MirVisitor for StubConstChecker<'tcx> { /// Collect constants that are represented as static variables. fn visit_constant(&mut self, constant: &Constant, location: Location) { - let const_ = self.monomorphize(rustc_internal::internal(&constant.literal)); + let const_ = self.monomorphize(rustc_internal::internal(self.tcx, &constant.literal)); debug!(?constant, ?location, ?const_, "visit_constant"); match const_ { Const::Val(..) | Const::Ty(..) => {} @@ -109,7 +109,7 @@ impl<'tcx> MirVisitor for StubConstChecker<'tcx> { tcx.def_path_str(trait_), self.source.name() ); - tcx.dcx().span_err(rustc_internal::internal(location.span()), msg); + tcx.dcx().span_err(rustc_internal::internal(self.tcx, location.span()), msg); self.is_valid = false; } } diff --git a/library/kani_macros/src/sysroot/contracts/bootstrap.rs b/library/kani_macros/src/sysroot/contracts/bootstrap.rs index f02eaa627477..98f52c19d9e7 100644 --- a/library/kani_macros/src/sysroot/contracts/bootstrap.rs +++ b/library/kani_macros/src/sysroot/contracts/bootstrap.rs @@ -11,7 +11,7 @@ use syn::ItemFn; use super::{ helpers::*, shared::{attach_require_kani_any, identifier_for_generated_function}, - ContractConditionsData, ContractConditionsHandler, + ContractConditionsHandler, }; impl<'a> ContractConditionsHandler<'a> { diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 2914c0609c1a..510d3e13b50b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-01-17" +channel = "nightly-2024-01-23" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/kani/Intrinsics/AlignOfVal/align_of_basic.rs b/tests/kani/Intrinsics/AlignOfVal/align_of_basic.rs index 9e76a55a2cb3..ba482248898c 100644 --- a/tests/kani/Intrinsics/AlignOfVal/align_of_basic.rs +++ b/tests/kani/Intrinsics/AlignOfVal/align_of_basic.rs @@ -30,13 +30,13 @@ fn main() { assert!(min_align_of_val(&0i16) == 2); assert!(min_align_of_val(&0i32) == 4); assert!(min_align_of_val(&0i64) == 8); - assert!(min_align_of_val(&0i128) == 8); + assert!(min_align_of_val(&0i128) == 16); assert!(min_align_of_val(&0isize) == 8); assert!(min_align_of_val(&0u8) == 1); assert!(min_align_of_val(&0u16) == 2); assert!(min_align_of_val(&0u32) == 4); assert!(min_align_of_val(&0u64) == 8); - assert!(min_align_of_val(&0u128) == 8); + assert!(min_align_of_val(&0u128) == 16); assert!(min_align_of_val(&0usize) == 8); assert!(min_align_of_val(&0f32) == 4); assert!(min_align_of_val(&0f64) == 8); diff --git a/tests/kani/Intrinsics/AlignOfVal/align_of_fat_ptr.rs b/tests/kani/Intrinsics/AlignOfVal/align_of_fat_ptr.rs index e1b3e1dd4491..426f09de1a59 100644 --- a/tests/kani/Intrinsics/AlignOfVal/align_of_fat_ptr.rs +++ b/tests/kani/Intrinsics/AlignOfVal/align_of_fat_ptr.rs @@ -19,7 +19,7 @@ fn check_align_simple() { let a = A { id: 0 }; let t: &dyn T = &a; #[cfg(target_arch = "x86_64")] - assert_eq!(align_of_val(t), 8); + assert_eq!(align_of_val(t), 16); #[cfg(target_arch = "aarch64")] assert_eq!(align_of_val(t), 16); assert_eq!(align_of_val(&t), 8); diff --git a/tests/kani/Intrinsics/ConstEval/min_align_of.rs b/tests/kani/Intrinsics/ConstEval/min_align_of.rs index eed572b8dc9f..425f27084076 100644 --- a/tests/kani/Intrinsics/ConstEval/min_align_of.rs +++ b/tests/kani/Intrinsics/ConstEval/min_align_of.rs @@ -19,13 +19,13 @@ fn main() { assert!(min_align_of::() == 2); assert!(min_align_of::() == 4); assert!(min_align_of::() == 8); - assert!(min_align_of::() == 8); + assert!(min_align_of::() == 16); assert!(min_align_of::() == 8); assert!(min_align_of::() == 1); assert!(min_align_of::() == 2); assert!(min_align_of::() == 4); assert!(min_align_of::() == 8); - assert!(min_align_of::() == 8); + assert!(min_align_of::() == 16); assert!(min_align_of::() == 8); assert!(min_align_of::() == 4); assert!(min_align_of::() == 8); diff --git a/tests/kani/Intrinsics/ConstEval/pref_align_of.rs b/tests/kani/Intrinsics/ConstEval/pref_align_of.rs index 22ff342a8198..d495bffef5f8 100644 --- a/tests/kani/Intrinsics/ConstEval/pref_align_of.rs +++ b/tests/kani/Intrinsics/ConstEval/pref_align_of.rs @@ -19,13 +19,13 @@ fn main() { assert!(unsafe { pref_align_of::() } == 2); assert!(unsafe { pref_align_of::() } == 4); assert!(unsafe { pref_align_of::() } == 8); - assert!(unsafe { pref_align_of::() } == 8); + assert!(unsafe { pref_align_of::() } == 16); assert!(unsafe { pref_align_of::() } == 8); assert!(unsafe { pref_align_of::() } == 1); assert!(unsafe { pref_align_of::() } == 2); assert!(unsafe { pref_align_of::() } == 4); assert!(unsafe { pref_align_of::() } == 8); - assert!(unsafe { pref_align_of::() } == 8); + assert!(unsafe { pref_align_of::() } == 16); assert!(unsafe { pref_align_of::() } == 8); assert!(unsafe { pref_align_of::() } == 4); assert!(unsafe { pref_align_of::() } == 8); diff --git a/tests/perf/hashset/expected b/tests/perf/hashset/expected index 8ba783d580f9..1fbcbbd13c25 100644 --- a/tests/perf/hashset/expected +++ b/tests/perf/hashset/expected @@ -1 +1 @@ -4 successfully verified harnesses, 0 failures, 4 total +3 successfully verified harnesses, 0 failures, 3 total diff --git a/tests/perf/hashset/src/lib.rs b/tests/perf/hashset/src/lib.rs index fb29db5c7ed6..e2f1fc16666a 100644 --- a/tests/perf/hashset/src/lib.rs +++ b/tests/perf/hashset/src/lib.rs @@ -1,11 +1,12 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: --enable-stubbing --enable-unstable +// kani-flags: -Z stubbing //! Try to verify HashSet basic behavior. use std::collections::{hash_map::RandomState, HashSet}; use std::mem::{size_of, size_of_val, transmute}; +#[allow(dead_code)] fn concrete_state() -> RandomState { let keys: [u64; 2] = [0, 0]; assert_eq!(size_of_val(&keys), size_of::()); @@ -14,7 +15,7 @@ fn concrete_state() -> RandomState { #[kani::proof] #[kani::stub(RandomState::new, concrete_state)] -#[kani::unwind(2)] +#[kani::unwind(5)] #[kani::solver(kissat)] fn check_insert() { let mut set: HashSet = HashSet::default(); @@ -26,41 +27,29 @@ fn check_insert() { #[kani::proof] #[kani::stub(RandomState::new, concrete_state)] -#[kani::unwind(2)] +#[kani::unwind(5)] #[kani::solver(kissat)] fn check_contains() { let first = kani::any(); - let mut set: HashSet = HashSet::from([first]); + let set: HashSet = HashSet::from([first]); assert!(set.contains(&first)); } #[kani::proof] #[kani::stub(RandomState::new, concrete_state)] -#[kani::unwind(2)] +#[kani::unwind(5)] +#[kani::solver(kissat)] fn check_contains_str() { - let mut set = HashSet::from(["s"]); + let set = HashSet::from(["s"]); assert!(set.contains(&"s")); } -#[kani::proof] -#[kani::stub(RandomState::new, concrete_state)] -#[kani::unwind(2)] -#[kani::solver(kissat)] -fn check_insert_two_concrete() { - let mut set: HashSet = HashSet::default(); - let first = 10; - let second = 20; - set.insert(first); - set.insert(second); - assert_eq!(set.len(), 2); -} - // too slow so don't run in the regression for now #[cfg(slow)] mod slow { #[kani::proof] #[kani::stub(RandomState::new, concrete_state)] - #[kani::unwind(3)] + #[kani::unwind(5)] fn check_insert_two_elements() { let mut set: HashSet = HashSet::default(); let first = kani::any(); @@ -71,4 +60,17 @@ mod slow { if first == second { assert_eq!(set.len(), 1) } else { assert_eq!(set.len(), 2) } } + + #[kani::proof] + #[kani::stub(RandomState::new, concrete_state)] + #[kani::unwind(5)] + #[kani::solver(kissat)] + fn check_insert_two_concrete() { + let mut set: HashSet = HashSet::default(); + let first = 10; + let second = 20; + set.insert(first); + set.insert(second); + assert_eq!(set.len(), 2); + } } From 3ceefa768e8583550d6b2240120587dace879b3f Mon Sep 17 00:00:00 2001 From: "Felipe R. Monteiro" Date: Thu, 8 Feb 2024 14:24:00 -0500 Subject: [PATCH 004/225] Upgrade Rust toolchain to nightly-2024-01-24 (#3008) Resolves https://github.com/model-checking/kani/issues/3007. 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 --- rust-toolchain.toml | 2 +- tests/expected/any_vec/exact_length.expected | 4 ++-- tests/expected/any_vec/out_bounds.expected | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 510d3e13b50b..fb0f45b321b4 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-01-23" +channel = "nightly-2024-01-24" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/expected/any_vec/exact_length.expected b/tests/expected/any_vec/exact_length.expected index 082f3fb61570..93feecb214e5 100644 --- a/tests/expected/any_vec/exact_length.expected +++ b/tests/expected/any_vec/exact_length.expected @@ -1,12 +1,12 @@ Checking harness check_access_length_17... Failed Checks: assumption failed\ -in >::get_unchecked +in std::hint::assert_unchecked Checking harness check_access_length_zero... Failed Checks: assumption failed\ -in >::get_unchecked +in std::hint::assert_unchecked Verification failed for - check_access_length_17 Verification failed for - check_access_length_zero diff --git a/tests/expected/any_vec/out_bounds.expected b/tests/expected/any_vec/out_bounds.expected index 24121aee4ee8..71132a64f67d 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 -in >::get_unchecked +in std::hint::assert_unchecked Verification failed for - check_always_out_bounds From 0fb87befa37df284759e6c24deffbb8a02d73229 Mon Sep 17 00:00:00 2001 From: "Felipe R. Monteiro" Date: Thu, 8 Feb 2024 19:49:02 -0500 Subject: [PATCH 005/225] Bump Kani version to 0.46.0 (#3013) ## What's Changed * `modifies` Clauses for Function Contracts by @JustusAdam in https://github.com/model-checking/kani/pull/2800 * Fix ICEs due to mismatched arguments by @celinval in https://github.com/model-checking/kani/pull/2994. Resolves the following issues: * https://github.com/model-checking/kani/issues/2260 * https://github.com/model-checking/kani/issues/2312 * Enable powf*, exp*, log* intrinsics by @tautschnig in https://github.com/model-checking/kani/pull/2996 * Upgrade Rust toolchain to nightly-2024-01-24 by @celinval @feliperodri @qinheping **Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.45.0...kani-0.46.0 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: Celina G. Val --- CHANGELOG.md | 12 ++++++++++++ Cargo.lock | 18 +++++++++--------- 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_macros/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- tools/build-kani/Cargo.toml | 2 +- 11 files changed, 30 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33baeb1ef5af..0d1186c1007f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ 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.46.0] + +## What's Changed +* `modifies` Clauses for Function Contracts by @JustusAdam in https://github.com/model-checking/kani/pull/2800 +* Fix ICEs due to mismatched arguments by @celinval in https://github.com/model-checking/kani/pull/2994. Resolves the following issues: + * https://github.com/model-checking/kani/issues/2260 + * https://github.com/model-checking/kani/issues/2312 +* Enable powf*, exp*, log* intrinsics by @tautschnig in https://github.com/model-checking/kani/pull/2996 +* Upgrade Rust toolchain to nightly-2024-01-24 by @celinval @feliperodri @qinheping + +**Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.45.0...kani-0.46.0 + ## [0.45.0] ## What's Changed diff --git a/Cargo.lock b/Cargo.lock index fb7478a425e0..82e7edf9b7d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,7 +120,7 @@ dependencies = [ [[package]] name = "build-kani" -version = "0.45.0" +version = "0.46.0" dependencies = [ "anyhow", "cargo_metadata", @@ -256,7 +256,7 @@ dependencies = [ [[package]] name = "cprover_bindings" -version = "0.45.0" +version = "0.46.0" dependencies = [ "lazy_static", "linear-map", @@ -433,14 +433,14 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "kani" -version = "0.45.0" +version = "0.46.0" dependencies = [ "kani_macros", ] [[package]] name = "kani-compiler" -version = "0.45.0" +version = "0.46.0" dependencies = [ "clap", "cprover_bindings", @@ -461,7 +461,7 @@ dependencies = [ [[package]] name = "kani-driver" -version = "0.45.0" +version = "0.46.0" dependencies = [ "anyhow", "cargo_metadata", @@ -489,7 +489,7 @@ dependencies = [ [[package]] name = "kani-verifier" -version = "0.45.0" +version = "0.46.0" dependencies = [ "anyhow", "home", @@ -498,7 +498,7 @@ dependencies = [ [[package]] name = "kani_macros" -version = "0.45.0" +version = "0.46.0" dependencies = [ "proc-macro-error", "proc-macro2", @@ -508,7 +508,7 @@ dependencies = [ [[package]] name = "kani_metadata" -version = "0.45.0" +version = "0.46.0" dependencies = [ "clap", "cprover_bindings", @@ -1040,7 +1040,7 @@ checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "std" -version = "0.45.0" +version = "0.46.0" dependencies = [ "kani", ] diff --git a/Cargo.toml b/Cargo.toml index df9559e378b6..435d930c1b21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-verifier" -version = "0.45.0" +version = "0.46.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 5205872f6788..6f66a32952bc 100644 --- a/cprover_bindings/Cargo.toml +++ b/cprover_bindings/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "cprover_bindings" -version = "0.45.0" +version = "0.46.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index f5c1163fe564..c5fd7f8f5855 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-compiler" -version = "0.45.0" +version = "0.46.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index 1e6744824c0c..52e963a8a0ac 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-driver" -version = "0.45.0" +version = "0.46.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 91e22e0224e7..2b34046ebc45 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_metadata" -version = "0.45.0" +version = "0.46.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index d50717ca97c6..0035f329d116 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.45.0" +version = "0.46.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 473d0fc7e5b6..b50652c5d83c 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_macros" -version = "0.45.0" +version = "0.46.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index b37ea4de7d4c..b65246e5d8b6 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.45.0" +version = "0.46.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 636a7dfaea2e..b9972de60276 100644 --- a/tools/build-kani/Cargo.toml +++ b/tools/build-kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "build-kani" -version = "0.45.0" +version = "0.46.0" edition = "2021" description = "Builds Kani, Sysroot and release bundle." license = "MIT OR Apache-2.0" From 889d09e394eb28c769794df5ff040831fa44e8d4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:02:03 +0100 Subject: [PATCH 006/225] Automatic toolchain upgrade to nightly-2024-01-25 (#3011) Update Rust toolchain from nightly-2024-01-24 to nightly-2024-01-25 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/5d3d3479d774754856db2db3e439dfb88ef3c52f up to https://github.com/rust-lang/rust/commit/7ffc697ce10f19447c0ce338428ae4b9bc0c041c. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index fb0f45b321b4..b316f181bdac 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-01-24" +channel = "nightly-2024-01-25" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From d861442704c2791aa6ad92d7881a760e4a7deaa0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 11 Feb 2024 23:23:48 -0800 Subject: [PATCH 007/225] Automatic cargo update to 2024-02-12 (#3024) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 57 +++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 82e7edf9b7d9..b00367482588 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", @@ -139,9 +139,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" +checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" dependencies = [ "serde", ] @@ -168,9 +168,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.18" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" dependencies = [ "clap_builder", "clap_derive", @@ -178,9 +178,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.18" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" dependencies = [ "anstream", "anstyle", @@ -190,9 +190,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck", "proc-macro2", @@ -202,9 +202,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" @@ -318,9 +318,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "encode_unicode" @@ -408,9 +408,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -628,19 +628,18 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -661,9 +660,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] @@ -1058,9 +1057,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "strum" @@ -1135,18 +1134,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", From 42fcf40af42a647f8ab74d3a7b41185be64690c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 16:43:04 +0000 Subject: [PATCH 008/225] Bump ncipollo/release-action from 1.13.0 to 1.14.0 (#3025) Bumps [ncipollo/release-action](https://github.com/ncipollo/release-action) from 1.13.0 to 1.14.0. --- .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 56cb99bac960..bd3e145e1bce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -235,7 +235,7 @@ jobs: - name: Create release id: create_release - uses: ncipollo/release-action@v1.13.0 + uses: ncipollo/release-action@v1.14.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 17ff02cdfd9b5cf63ae4ee72a54a5b2188cae54d Mon Sep 17 00:00:00 2001 From: "Felipe R. Monteiro" Date: Tue, 13 Feb 2024 14:06:28 -0500 Subject: [PATCH 009/225] Upgrade Rust toolchain to nightly-2024-02-09 (#3015) Attempt to update Rust toolchain from nightly-2024-01-25 to nightly-2024-02-09. Resolves https://github.com/model-checking/kani/issues/3014. 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 --- Cargo.lock | 45 ++++++++++++------ cprover_bindings/Cargo.toml | 2 +- cprover_bindings/src/cbmc_string.rs | 4 +- .../src/codegen_cprover_gotoc/codegen/typ.rs | 46 +++---------------- kani-compiler/src/kani_middle/coercion.rs | 2 +- kani-compiler/src/session.rs | 2 +- kani-driver/src/args/common.rs | 1 + kani-driver/src/main.rs | 1 - rust-toolchain.toml | 2 +- .../non_arbitrary_param/expected | 7 ++- tools/bookrunner/librustdoc/lib.rs | 1 - 11 files changed, 47 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b00367482588..ec36c820d13c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,13 +14,14 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.8" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ - "getrandom", + "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -378,19 +379,13 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", ] -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" - [[package]] name = "heck" version = "0.4.1" @@ -413,7 +408,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown", ] [[package]] @@ -1046,12 +1041,12 @@ dependencies = [ [[package]] name = "string-interner" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e2531d8525b29b514d25e275a43581320d587b86db302b9a7e464bac579648" +checksum = "07f9fdfdd31a0ff38b59deb401be81b73913d76c9cc5b1aed4e1330a223420b9" dependencies = [ "cfg-if", - "hashbrown 0.11.2", + "hashbrown", "serde", ] @@ -1516,3 +1511,23 @@ checksum = "5389a154b01683d28c77f8f68f49dea75f0a4da32557a58f68ee51ebba472d29" dependencies = [ "memchr", ] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] diff --git a/cprover_bindings/Cargo.toml b/cprover_bindings/Cargo.toml index 6f66a32952bc..c2535d20d2fe 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.14.0" +string-interner = "0.15.0" tracing = "0.1" linear-map = {version = "1.2", features = ["serde_impl"]} diff --git a/cprover_bindings/src/cbmc_string.rs b/cprover_bindings/src/cbmc_string.rs index b487b7615122..4c392f647759 100644 --- a/cprover_bindings/src/cbmc_string.rs +++ b/cprover_bindings/src/cbmc_string.rs @@ -3,6 +3,7 @@ use lazy_static::lazy_static; use std::sync::Mutex; +use string_interner::backend::StringBackend; use string_interner::symbol::SymbolU32; use string_interner::StringInterner; @@ -24,7 +25,8 @@ pub struct InternedString(SymbolU32); // Use a `Mutex` to make this thread safe. lazy_static! { - static ref INTERNER: Mutex = Mutex::new(StringInterner::default()); + static ref INTERNER: Mutex> = + Mutex::new(StringInterner::default()); } impl InternedString { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index 62b5cbedd95a..c988636a390f 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -1,7 +1,6 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT use crate::codegen_cprover_gotoc::GotocCtx; -use cbmc::btree_map; use cbmc::goto_program::{DatatypeComponent, Expr, Location, Parameter, Symbol, SymbolTable, Type}; use cbmc::utils::aggr_tag; use cbmc::{InternString, InternedString}; @@ -51,8 +50,6 @@ pub trait TypeExt { fn is_rust_fat_ptr(&self, st: &SymbolTable) -> bool; fn is_rust_slice_fat_ptr(&self, st: &SymbolTable) -> bool; fn is_rust_trait_fat_ptr(&self, st: &SymbolTable) -> bool; - fn is_unit(&self) -> bool; - fn is_unit_pointer(&self) -> bool; fn unit() -> Self; } @@ -92,42 +89,6 @@ impl TypeExt for Type { // We don't have access to the symbol table here to do it ourselves. Type::struct_tag(UNIT_TYPE_EMPTY_STRUCT_NAME) } - - fn is_unit(&self) -> bool { - match self { - Type::StructTag(name) => *name == aggr_tag(UNIT_TYPE_EMPTY_STRUCT_NAME), - _ => false, - } - } - - fn is_unit_pointer(&self) -> bool { - match self { - Type::Pointer { typ } => typ.is_unit(), - _ => false, - } - } -} - -trait ExprExt { - fn unit(symbol_table: &SymbolTable) -> Self; - - fn is_unit(&self) -> bool; - - fn is_unit_pointer(&self) -> bool; -} - -impl ExprExt for Expr { - fn unit(symbol_table: &SymbolTable) -> Self { - Expr::struct_expr(Type::unit(), btree_map![], symbol_table) - } - - fn is_unit(&self) -> bool { - self.typ().is_unit() - } - - fn is_unit_pointer(&self) -> bool { - self.typ().is_unit_pointer() - } } /// Function signatures @@ -640,7 +601,11 @@ impl<'tcx> GotocCtx<'tcx> { ty::Bound(_, _) | ty::Param(_) => unreachable!("monomorphization bug"), // type checking remnants which shouldn't be reachable - ty::CoroutineWitness(_, _) | ty::Infer(_) | ty::Placeholder(_) | ty::Error(_) => { + ty::CoroutineWitness(_, _) + | ty::CoroutineClosure(_, _) + | ty::Infer(_) + | ty::Placeholder(_) + | ty::Error(_) => { unreachable!("remnants of type checking") } } @@ -1078,6 +1043,7 @@ impl<'tcx> GotocCtx<'tcx> { ty::Bound(_, _) => todo!("{:?} {:?}", pointee_type, pointee_type.kind()), ty::Error(_) => todo!("{:?} {:?}", pointee_type, pointee_type.kind()), ty::CoroutineWitness(_, _) => todo!("{:?} {:?}", pointee_type, pointee_type.kind()), + ty::CoroutineClosure(_, _) => todo!("{:?} {:?}", pointee_type, pointee_type.kind()), ty::Infer(_) => todo!("{:?} {:?}", pointee_type, pointee_type.kind()), ty::Param(_) => todo!("{:?} {:?}", pointee_type, pointee_type.kind()), ty::Placeholder(_) => todo!("{:?} {:?}", pointee_type, pointee_type.kind()), diff --git a/kani-compiler/src/kani_middle/coercion.rs b/kani-compiler/src/kani_middle/coercion.rs index 94879b5b65e6..10b8c1275da6 100644 --- a/kani-compiler/src/kani_middle/coercion.rs +++ b/kani-compiler/src/kani_middle/coercion.rs @@ -253,7 +253,7 @@ fn custom_coerce_unsize_info<'tcx>( match tcx.codegen_select_candidate((ParamEnv::reveal_all(), trait_ref)) { Ok(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, .. })) => { - tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap() + tcx.coerce_unsized_info(impl_def_id).unwrap().custom_kind.unwrap() } impl_source => { unreachable!("invalid `CoerceUnsized` impl_source: {:?}", impl_source); diff --git a/kani-compiler/src/session.rs b/kani-compiler/src/session.rs index a1c3af20bb06..5b5416a5f142 100644 --- a/kani-compiler/src/session.rs +++ b/kani-compiler/src/session.rs @@ -60,7 +60,7 @@ static JSON_PANIC_HOOK: LazyLock) + Sync + Send TerminalUrl::No, ); let diagnostic = Diagnostic::new(rustc_errors::Level::Bug, msg); - emitter.emit_diagnostic(&diagnostic); + emitter.emit_diagnostic(diagnostic); (*JSON_PANIC_HOOK)(info); })); hook diff --git a/kani-driver/src/args/common.rs b/kani-driver/src/args/common.rs index d4ac0a61bd17..3333ee67badf 100644 --- a/kani-driver/src/args/common.rs +++ b/kani-driver/src/args/common.rs @@ -51,6 +51,7 @@ pub trait Verbosity { /// Note that `debug() == true` must imply `verbose() == true`. fn verbose(&self) -> bool; /// Whether we should emit debug messages. + #[allow(unused)] fn debug(&self) -> bool; /// Whether any verbosity was selected. fn is_set(&self) -> bool; diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 1d2afa177ca9..009f25b37b5a 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -1,7 +1,6 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT #![feature(let_chains)] -#![feature(array_methods)] use std::ffi::OsString; use std::process::ExitCode; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index b316f181bdac..9d93e7382952 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-01-25" +channel = "nightly-2024-02-09" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/ui/derive-arbitrary/non_arbitrary_param/expected b/tests/ui/derive-arbitrary/non_arbitrary_param/expected index e74643f3bdb6..55f12678cf9a 100644 --- a/tests/ui/derive-arbitrary/non_arbitrary_param/expected +++ b/tests/ui/derive-arbitrary/non_arbitrary_param/expected @@ -1,5 +1,4 @@ error[E0277]: the trait bound `Void: kani::Arbitrary` is not satisfied - -|\ -| let _wrapper: Wrapper = kani::any();\ -| ^^^^^^^^^ the trait `kani::Arbitrary` is not implemented for `Void`\ + |\ +14 | let _wrapper: Wrapper = kani::any();\ + | ^^^^^^^^^^^ the trait `kani::Arbitrary` is not implemented for `Void`, which is required by `Wrapper: kani::Arbitrary`\ diff --git a/tools/bookrunner/librustdoc/lib.rs b/tools/bookrunner/librustdoc/lib.rs index 68bc77fa14e8..cddc369b4dc1 100644 --- a/tools/bookrunner/librustdoc/lib.rs +++ b/tools/bookrunner/librustdoc/lib.rs @@ -7,7 +7,6 @@ html_playground_url = "https://play.rust-lang.org/" )] #![feature(rustc_private)] -#![feature(array_methods)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(control_flow_enum)] From 6bea13110053ee668388f1100d286354679085be Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Thu, 15 Feb 2024 15:20:41 -0800 Subject: [PATCH 010/225] Update s2n-quic submodule to latest (#3031) Update the s2n-quic submodule to bring in the latest proofs. This is to ensure we don't break any of the existing proofs. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- tests/perf/overlays/s2n-quic/tools/xdp/s2n-quic-xdp/expected | 1 - tests/perf/s2n-quic | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 tests/perf/overlays/s2n-quic/tools/xdp/s2n-quic-xdp/expected diff --git a/tests/perf/overlays/s2n-quic/tools/xdp/s2n-quic-xdp/expected b/tests/perf/overlays/s2n-quic/tools/xdp/s2n-quic-xdp/expected deleted file mode 100644 index 34c886c358cb..000000000000 --- a/tests/perf/overlays/s2n-quic/tools/xdp/s2n-quic-xdp/expected +++ /dev/null @@ -1 +0,0 @@ -VERIFICATION:- SUCCESSFUL diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index f1df2d64083e..311ece35722d 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit f1df2d64083e4d1b0f56dc0a298066fdef062bcb +Subproject commit 311ece35722d9bebb8b95fdd6f16c2b6887aa46b From 9fae67a9eed66ab0840e5e682c4f941c17705a2d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 13:32:18 +0100 Subject: [PATCH 011/225] Automatic cargo update to 2024-02-19 (#3033) Dependency upgrade resulting from `cargo update`. --- Cargo.lock | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec36c820d13c..a5a347c7b518 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" dependencies = [ "cfg-if", "once_cell", @@ -35,9 +35,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" dependencies = [ "anstyle", "anstyle-parse", @@ -169,9 +169,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ "clap_builder", "clap_derive", @@ -179,9 +179,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ "anstream", "anstyle", @@ -198,7 +198,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -498,7 +498,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -966,7 +966,7 @@ checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -1078,7 +1078,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -1091,7 +1091,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -1106,9 +1106,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" dependencies = [ "proc-macro2", "quote", @@ -1144,7 +1144,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -1180,9 +1180,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.4" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951" +checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" dependencies = [ "indexmap", "serde", @@ -1210,7 +1210,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -1505,9 +1505,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.39" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5389a154b01683d28c77f8f68f49dea75f0a4da32557a58f68ee51ebba472d29" +checksum = "d90f4e0f530c4c69f62b80d839e9ef3855edc9cba471a160c4d692deed62b401" dependencies = [ "memchr", ] @@ -1529,5 +1529,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] From 72fd9f4fb541bda217ac42c0c8d639d63f640b46 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Tue, 20 Feb 2024 11:03:06 -0800 Subject: [PATCH 012/225] Upgrade toolchain to 2024-02-14 (#3036) Upgrade toolchain to 2024-02-17. Relevant PRs: https://github.com/rust-lang/rust/pull/120872 https://github.com/rust-lang/rust/pull/120594 Resolves #3028 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 | 12 ++++++----- cprover_bindings/src/goto_program/symbol.rs | 15 +++++++------- cprover_bindings/src/irep/irep_id.rs | 20 ++++++++++++------- .../codegen_cprover_gotoc/codegen/rvalue.rs | 1 + .../src/codegen_cprover_gotoc/codegen/typ.rs | 10 ++++++---- kani-compiler/src/kani_middle/attributes.rs | 2 +- rust-toolchain.toml | 2 +- .../playback_opts.expected | 2 +- 8 files changed, 38 insertions(+), 26 deletions(-) diff --git a/cprover_bindings/src/goto_program/builtin.rs b/cprover_bindings/src/goto_program/builtin.rs index 438bc2eea1e9..a0c1f211b5e2 100644 --- a/cprover_bindings/src/goto_program/builtin.rs +++ b/cprover_bindings/src/goto_program/builtin.rs @@ -4,6 +4,8 @@ use self::BuiltinFn::*; use super::{Expr, Location, Symbol, Type}; +use std::fmt::Display; + #[derive(Debug, Clone, Copy)] pub enum BuiltinFn { Abort, @@ -67,9 +69,9 @@ pub enum BuiltinFn { Unlink, } -impl ToString for BuiltinFn { - fn to_string(&self) -> String { - match self { +impl Display for BuiltinFn { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let func = match self { Abort => "abort", Assert => "assert", CProverAssume => "__CPROVER_assume", @@ -129,8 +131,8 @@ impl ToString for BuiltinFn { Trunc => "trunc", Truncf => "truncf", Unlink => "unlink", - } - .to_string() + }; + write!(f, "{func}") } } diff --git a/cprover_bindings/src/goto_program/symbol.rs b/cprover_bindings/src/goto_program/symbol.rs index b1082a8f1f80..ad71b0f84346 100644 --- a/cprover_bindings/src/goto_program/symbol.rs +++ b/cprover_bindings/src/goto_program/symbol.rs @@ -4,6 +4,8 @@ use super::super::utils::aggr_tag; use super::{DatatypeComponent, Expr, Location, Parameter, Stmt, Type}; use crate::{InternStringOption, InternedString}; +use std::fmt::Display; + /// Based off the CBMC symbol implementation here: /// #[derive(Clone, Debug)] @@ -452,14 +454,13 @@ impl SymbolValues { } } -/// ToString - -impl ToString for SymbolModes { - fn to_string(&self) -> String { - match self { +/// Display +impl Display for SymbolModes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mode = match self { SymbolModes::C => "C", SymbolModes::Rust => "Rust", - } - .to_string() + }; + write!(f, "{mode}") } } diff --git a/cprover_bindings/src/irep/irep_id.rs b/cprover_bindings/src/irep/irep_id.rs index cad6eb563bf4..119aecb8887c 100644 --- a/cprover_bindings/src/irep/irep_id.rs +++ b/cprover_bindings/src/irep/irep_id.rs @@ -8,6 +8,8 @@ use crate::cbmc_string::InternedString; use crate::utils::NumUtils; use num::bigint::{BigInt, BigUint, Sign}; +use std::fmt::Display; + #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] pub enum IrepId { /// In addition to the standard enums defined below, CBMC also allows ids to be strings. @@ -872,15 +874,19 @@ impl IrepId { } } -impl ToString for IrepId { - fn to_string(&self) -> String { +impl Display for IrepId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - IrepId::FreeformString(s) => return s.to_string(), - IrepId::FreeformInteger(i) => return i.to_string(), + IrepId::FreeformString(s) => { + return write!(f, "{s}"); + } + IrepId::FreeformInteger(i) => { + return write!(f, "{i}"); + } IrepId::FreeformBitPattern(i) => { - return format!("{i:X}"); + return write!(f, "{i:X}"); } - _ => (), + _ => {} } let s = match self { @@ -1708,7 +1714,7 @@ impl ToString for IrepId { IrepId::VectorGt => "vector->", IrepId::VectorLt => "vector-<", }; - s.to_string() + write!(f, "{s}") } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index e7501df3aac4..3df6d807aaea 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -725,6 +725,7 @@ impl<'tcx> GotocCtx<'tcx> { .bytes(), Type::size_t(), ), + NullOp::DebugAssertions => Expr::c_false(), } } Rvalue::ShallowInitBox(ref operand, content_ty) => { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index c988636a390f..b0e3c115d597 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -1714,8 +1714,10 @@ impl<'tcx> GotocCtx<'tcx> { /// metadata associated with it. pub fn use_thin_pointer(&self, mir_type: Ty<'tcx>) -> bool { // ptr_metadata_ty is not defined on all types, the projection of an associated type - let (metadata, _check_is_sized) = mir_type.ptr_metadata_ty(self.tcx, normalize_type); - !self.is_unsized(mir_type) || metadata == self.tcx.types.unit + let metadata = mir_type.ptr_metadata_ty_or_tail(self.tcx, normalize_type); + !self.is_unsized(mir_type) + || metadata.is_err() + || (metadata.unwrap() == self.tcx.types.unit) } /// We use fat pointer if not thin pointer. @@ -1726,14 +1728,14 @@ impl<'tcx> GotocCtx<'tcx> { /// A pointer to the mir type should be a slice fat pointer. /// We use a slice fat pointer if the metadata is the slice length (type usize). pub fn use_slice_fat_pointer(&self, mir_type: Ty<'tcx>) -> bool { - let (metadata, _check_is_sized) = mir_type.ptr_metadata_ty(self.tcx, normalize_type); + let metadata = mir_type.ptr_metadata_ty(self.tcx, normalize_type); metadata == self.tcx.types.usize } /// A pointer to the mir type should be a vtable fat pointer. /// We use a vtable fat pointer if this is a fat pointer to anything that is not a slice ptr. /// I.e.: The metadata is not length (type usize). pub fn use_vtable_fat_pointer(&self, mir_type: Ty<'tcx>) -> bool { - let (metadata, _check_is_sized) = mir_type.ptr_metadata_ty(self.tcx, normalize_type); + let metadata = mir_type.ptr_metadata_ty(self.tcx, normalize_type); metadata != self.tcx.types.unit && metadata != self.tcx.types.usize } diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index 32425204d76a..621107acd13e 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -268,7 +268,7 @@ impl<'tcx> KaniAttributes<'tcx> { .hir_id() }; - let result = match hir_map.get_parent(hir_id) { + let result = match self.tcx.parent_hir_node(hir_id) { Node::Item(Item { kind, .. }) => match kind { ItemKind::Mod(m) => find_in_mod(m), ItemKind::Impl(imp) => { diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 9d93e7382952..42bd4779102f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-02-09" +channel = "nightly-2024-02-14" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/script-based-pre/cargo_playback_opts/playback_opts.expected b/tests/script-based-pre/cargo_playback_opts/playback_opts.expected index 68b7daa3ee03..b68f0b355029 100644 --- a/tests/script-based-pre/cargo_playback_opts/playback_opts.expected +++ b/tests/script-based-pre/cargo_playback_opts/playback_opts.expected @@ -1,6 +1,6 @@ [TEST] Only codegen test... Executable unittests src/lib.rs [TEST] Only codegen test... - Finished test + Finished `test` [TEST] Executable debug/deps/sample_crate- From 56c22b056445ecc0d06ba067d68f56bd4480940d Mon Sep 17 00:00:00 2001 From: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> Date: Wed, 21 Feb 2024 13:32:39 -0500 Subject: [PATCH 013/225] Update dependencies from `cargo update` (#3038) Updates dependencies to their latest versions after running `cargo update` 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 | 60 +++++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5a347c7b518..eafca6ab0f76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" +checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" dependencies = [ "cfg-if", "once_cell", @@ -83,9 +83,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" [[package]] name = "autocfg" @@ -198,7 +198,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -498,7 +498,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -921,9 +921,9 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -942,38 +942,38 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -1000,9 +1000,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.31" +version = "0.9.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adf8a49373e98a4c5f0ceb5d05aa7c648d75f63774981ed95b7c7443bbd50c6e" +checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" dependencies = [ "indexmap", "itoa", @@ -1078,7 +1078,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -1091,7 +1091,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -1106,9 +1106,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.49" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ "proc-macro2", "quote", @@ -1144,14 +1144,14 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -1210,7 +1210,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] [[package]] @@ -1505,9 +1505,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d90f4e0f530c4c69f62b80d839e9ef3855edc9cba471a160c4d692deed62b401" +checksum = "7a4191c47f15cc3ec71fcb4913cb83d58def65dd3787610213c649283b5ce178" dependencies = [ "memchr", ] @@ -1529,5 +1529,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.50", ] From 6c97820be29798fb9802547715f1a32c047f0455 Mon Sep 17 00:00:00 2001 From: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> Date: Wed, 21 Feb 2024 15:33:08 -0500 Subject: [PATCH 014/225] Bump Kani version to 0.47.0 (#3039) ## What's Changed * Upgrade toolchain to 2024-02-14 by @zhassan-aws in https://github.com/model-checking/kani/pull/3036 **Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.46.0...kani-0.47.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 | 7 +++++++ Cargo.lock | 18 +++++++++--------- 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_macros/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- tools/build-kani/Cargo.toml | 2 +- 11 files changed, 25 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d1186c1007f..968906b6f4b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +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. +## [0.47.0] + +### What's Changed +* Upgrade toolchain to 2024-02-14 by @zhassan-aws in https://github.com/model-checking/kani/pull/3036 + +**Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.46.0...kani-0.47.0 + ## [0.46.0] ## What's Changed diff --git a/Cargo.lock b/Cargo.lock index eafca6ab0f76..186e554c9f1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,7 +121,7 @@ dependencies = [ [[package]] name = "build-kani" -version = "0.46.0" +version = "0.47.0" dependencies = [ "anyhow", "cargo_metadata", @@ -257,7 +257,7 @@ dependencies = [ [[package]] name = "cprover_bindings" -version = "0.46.0" +version = "0.47.0" dependencies = [ "lazy_static", "linear-map", @@ -428,14 +428,14 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "kani" -version = "0.46.0" +version = "0.47.0" dependencies = [ "kani_macros", ] [[package]] name = "kani-compiler" -version = "0.46.0" +version = "0.47.0" dependencies = [ "clap", "cprover_bindings", @@ -456,7 +456,7 @@ dependencies = [ [[package]] name = "kani-driver" -version = "0.46.0" +version = "0.47.0" dependencies = [ "anyhow", "cargo_metadata", @@ -484,7 +484,7 @@ dependencies = [ [[package]] name = "kani-verifier" -version = "0.46.0" +version = "0.47.0" dependencies = [ "anyhow", "home", @@ -493,7 +493,7 @@ dependencies = [ [[package]] name = "kani_macros" -version = "0.46.0" +version = "0.47.0" dependencies = [ "proc-macro-error", "proc-macro2", @@ -503,7 +503,7 @@ dependencies = [ [[package]] name = "kani_metadata" -version = "0.46.0" +version = "0.47.0" dependencies = [ "clap", "cprover_bindings", @@ -1034,7 +1034,7 @@ checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "std" -version = "0.46.0" +version = "0.47.0" dependencies = [ "kani", ] diff --git a/Cargo.toml b/Cargo.toml index 435d930c1b21..b842bf8fbf0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-verifier" -version = "0.46.0" +version = "0.47.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 c2535d20d2fe..3882e5ad300c 100644 --- a/cprover_bindings/Cargo.toml +++ b/cprover_bindings/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "cprover_bindings" -version = "0.46.0" +version = "0.47.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index c5fd7f8f5855..c5ac44d0be1e 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-compiler" -version = "0.46.0" +version = "0.47.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index 52e963a8a0ac..a009f444840b 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-driver" -version = "0.46.0" +version = "0.47.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 2b34046ebc45..92cba41e27f8 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_metadata" -version = "0.46.0" +version = "0.47.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index 0035f329d116..27fc6fc9ad6d 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.46.0" +version = "0.47.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 b50652c5d83c..46b6cf3daadd 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_macros" -version = "0.46.0" +version = "0.47.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index b65246e5d8b6..bdd10673f640 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.46.0" +version = "0.47.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 b9972de60276..8c80063d7ff5 100644 --- a/tools/build-kani/Cargo.toml +++ b/tools/build-kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "build-kani" -version = "0.46.0" +version = "0.47.0" edition = "2021" description = "Builds Kani, Sysroot and release bundle." license = "MIT OR Apache-2.0" From b3110eed3ca32e7090d93d57e64a98880f4d20a5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 05:09:55 -0800 Subject: [PATCH 015/225] Automatic cargo update to 2024-02-26 (#3043) 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 186e554c9f1e..fe8465a3b295 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1386,7 +1386,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -1406,17 +1406,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.3", + "windows_aarch64_msvc 0.52.3", + "windows_i686_gnu 0.52.3", + "windows_i686_msvc 0.52.3", + "windows_x86_64_gnu 0.52.3", + "windows_x86_64_gnullvm 0.52.3", + "windows_x86_64_msvc 0.52.3", ] [[package]] @@ -1427,9 +1427,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" [[package]] name = "windows_aarch64_msvc" @@ -1439,9 +1439,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" [[package]] name = "windows_i686_gnu" @@ -1451,9 +1451,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" [[package]] name = "windows_i686_msvc" @@ -1463,9 +1463,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" [[package]] name = "windows_x86_64_gnu" @@ -1475,9 +1475,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" [[package]] name = "windows_x86_64_gnullvm" @@ -1487,9 +1487,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" [[package]] name = "windows_x86_64_msvc" @@ -1499,9 +1499,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" [[package]] name = "winnow" From c2dc48939ce2863dbb223cd68e671f7d8d3415f9 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Mon, 26 Feb 2024 08:34:07 -0800 Subject: [PATCH 016/225] Upgrade rust toolchain to 2024-02-17 (#3040) Upgrade toolchain to 2024-02-17. Relevant PR: - https://github.com/rust-lang/rust/pull/120500 - https://github.com/rust-lang/rust/pull/100603 Fixes https://github.com/model-checking/kani/issues/87 Fixes https://github.com/model-checking/kani/issues/3034 Fixes https://github.com/model-checking/kani/issues/3037 --- .../codegen/intrinsic.rs | 25 +--- .../codegen_cprover_gotoc/codegen/operand.rs | 11 ++ .../codegen/statement.rs | 110 ++++++++++-------- .../codegen/ty_stable.rs | 5 + .../src/codegen_cprover_gotoc/codegen/typ.rs | 39 +------ kani-compiler/src/kani_middle/intrinsics.rs | 4 +- kani-compiler/src/main.rs | 1 + rust-toolchain.toml | 2 +- tests/kani/Closure/zst_unwrap.rs | 16 +++ tests/kani/SizeAndAlignOfDst/main_assert.rs | 58 +++++++++ .../SizeAndAlignOfDst/main_assert_fixme.rs | 85 ++++++++------ tests/kani/Tuple/tuple_trait.rs | 19 +++ 12 files changed, 227 insertions(+), 148 deletions(-) create mode 100644 tests/kani/Closure/zst_unwrap.rs create mode 100644 tests/kani/SizeAndAlignOfDst/main_assert.rs create mode 100644 tests/kani/Tuple/tuple_trait.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 6c711e3d20e4..e497cfc6b47c 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -12,7 +12,7 @@ use cbmc::goto_program::{ use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::ParamEnv; use rustc_smir::rustc_internal; -use stable_mir::mir::mono::{Instance, InstanceKind}; +use stable_mir::mir::mono::Instance; use stable_mir::mir::{BasicBlockIdx, Operand, Place}; use stable_mir::ty::{GenericArgs, RigidTy, Span, Ty, TyKind, UintTy}; use tracing::debug; @@ -45,17 +45,15 @@ impl<'tcx> GotocCtx<'tcx> { /// there is no terminator. pub fn codegen_funcall_of_intrinsic( &mut self, - func: &Operand, + instance: Instance, args: &[Operand], destination: &Place, target: Option, span: Span, ) -> Stmt { - let instance = self.get_intrinsic_instance(func).unwrap(); - if let Some(target) = target { let loc = self.codegen_span_stable(span); - let fargs = self.codegen_funcall_args(args, false); + let fargs = args.iter().map(|arg| self.codegen_operand_stable(arg)).collect::>(); Stmt::block( vec![ self.codegen_intrinsic(instance, fargs, destination, span), @@ -68,23 +66,6 @@ impl<'tcx> GotocCtx<'tcx> { } } - /// Returns `Some(instance)` if the function is an intrinsic; `None` otherwise - fn get_intrinsic_instance(&self, func: &Operand) -> Option { - let funct = self.operand_ty_stable(func); - match funct.kind() { - TyKind::RigidTy(RigidTy::FnDef(def, args)) => { - let instance = Instance::resolve(def, &args).unwrap(); - if matches!(instance.kind, InstanceKind::Intrinsic) { Some(instance) } else { None } - } - _ => None, - } - } - - /// Returns true if the `func` is a call to a compiler intrinsic; false otherwise. - pub fn is_intrinsic(&self, func: &Operand) -> bool { - self.get_intrinsic_instance(func).is_some() - } - /// Handles codegen for non returning intrinsics /// Non returning intrinsics are not associated with a destination pub fn codegen_never_return_intrinsic(&mut self, instance: Instance, span: Span) -> Stmt { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs index 517609bb0b4c..6c75c6a5ad5a 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs @@ -557,6 +557,17 @@ impl<'tcx> GotocCtx<'tcx> { alloc_vals } + /// Returns `Some(instance)` if the function is an intrinsic; `None` otherwise + pub fn get_instance(&self, func: &Operand) -> Option { + let funct = self.operand_ty_stable(func); + match funct.kind() { + TyKind::RigidTy(RigidTy::FnDef(def, args)) => { + Some(Instance::resolve(def, &args).unwrap()) + } + _ => None, + } + } + /// Generate a goto expression for a MIR "function item" reference. /// /// A "function item" is a ZST that corresponds to a specific single function. diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 05eb1bc41dc7..05b153f9583f 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -7,14 +7,16 @@ 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_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{List, ParamEnv}; 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::{ AssertMessage, BasicBlockIdx, CopyNonOverlapping, NonDivergingIntrinsic, Operand, Place, Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, RETURN_LOCAL, }; -use stable_mir::ty::{RigidTy, Span, Ty, TyKind, VariantIdx}; +use stable_mir::ty::{Abi, RigidTy, Span, Ty, TyKind, VariantIdx}; use tracing::{debug, debug_span, trace}; impl<'tcx> GotocCtx<'tcx> { @@ -432,31 +434,21 @@ impl<'tcx> GotocCtx<'tcx> { /// as subsequent parameters. /// /// See [GotocCtx::ty_needs_untupled_args] for more details. - fn codegen_untupled_args( - &mut self, - instance: Instance, - fargs: &mut Vec, - last_mir_arg: Option<&Operand>, - ) { - debug!("codegen_untuple_closure_args instance: {:?}, fargs {:?}", instance.name(), fargs); - if !fargs.is_empty() { - let tuple_ty = self.operand_ty_stable(last_mir_arg.unwrap()); - if self.is_zst_stable(tuple_ty) { - // Don't pass anything if all tuple elements are ZST. - // ZST arguments are ignored. - return; - } - let tupe = fargs.remove(fargs.len() - 1); - if let TyKind::RigidTy(RigidTy::Tuple(tupled_args)) = tuple_ty.kind() { - for (idx, arg_ty) in tupled_args.iter().enumerate() { - if !self.is_zst_stable(*arg_ty) { - // Access the tupled parameters through the `member` operation - let idx_expr = tupe.clone().member(&idx.to_string(), &self.symbol_table); - fargs.push(idx_expr); - } - } - } - } + fn codegen_untupled_args(&mut self, op: &Operand, args_abi: &[ArgAbi]) -> Vec { + let tuple_ty = self.operand_ty_stable(op); + let tuple_expr = self.codegen_operand_stable(op); + let TyKind::RigidTy(RigidTy::Tuple(tupled_args)) = tuple_ty.kind() else { unreachable!() }; + tupled_args + .iter() + .enumerate() + .filter_map(|(idx, _)| { + let arg_abi = &args_abi[idx]; + (arg_abi.mode != PassMode::Ignore).then(|| { + // Access the tupled parameters through the `member` operation + tuple_expr.clone().member(idx.to_string(), &self.symbol_table) + }) + }) + .collect() } /// Because function calls terminate basic blocks, to "end" a function call, we @@ -472,25 +464,24 @@ impl<'tcx> GotocCtx<'tcx> { /// Generate Goto-C for each argument to a function call. /// /// N.B. public only because instrinsics use this directly, too. - /// When `skip_zst` is set to `true`, the return value will not include any argument that is ZST. - /// This is used because we ignore ZST arguments, except for intrinsics. - pub(crate) fn codegen_funcall_args(&mut self, args: &[Operand], skip_zst: bool) -> Vec { - let fargs = args + pub(crate) fn codegen_funcall_args(&mut self, fn_abi: &FnAbi, args: &[Operand]) -> Vec { + let fargs: Vec = args .iter() - .filter_map(|op| { - let op_ty = self.operand_ty_stable(op); - if op_ty.kind().is_bool() { + .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 !self.is_zst_stable(op_ty) || !skip_zst { + } else if arg_abi.map_or(true, |abi| abi.mode != PassMode::Ignore) { Some(self.codegen_operand_stable(op)) } else { - // We ignore ZST types. - debug!(arg=?op, "codegen_funcall_args ignore"); None } }) .collect(); - debug!(?fargs, "codegen_funcall_args"); + debug!(?fargs, args_abi=?fn_abi.args, "codegen_funcall_args"); fargs } @@ -515,9 +506,12 @@ impl<'tcx> GotocCtx<'tcx> { span: Span, ) -> Stmt { debug!(?func, ?args, ?destination, ?span, "codegen_funcall"); - if self.is_intrinsic(&func) { + let instance_opt = self.get_instance(func); + if let Some(instance) = instance_opt + && matches!(instance.kind, InstanceKind::Intrinsic) + { return self.codegen_funcall_of_intrinsic( - &func, + instance, &args, &destination, target.map(|bb| bb), @@ -526,16 +520,23 @@ impl<'tcx> GotocCtx<'tcx> { } let loc = self.codegen_span_stable(span); - let funct = self.operand_ty_stable(func); - let mut fargs = self.codegen_funcall_args(&args, true); - match funct.kind() { - TyKind::RigidTy(RigidTy::FnDef(def, subst)) => { - let instance = Instance::resolve(def, &subst).unwrap(); - - // TODO(celina): Move this check to be inside codegen_funcall_args. - if self.ty_needs_untupled_args(rustc_internal::internal(self.tcx, funct)) { - self.codegen_untupled_args(instance, &mut fargs, args.last()); - } + let fn_ty = self.operand_ty_stable(func); + match fn_ty.kind() { + fn_def @ TyKind::RigidTy(RigidTy::FnDef(..)) => { + let instance = instance_opt.unwrap(); + let fn_abi = instance.fn_abi().unwrap(); + let mut fargs = if args.is_empty() + || fn_def.fn_sig().unwrap().value.abi != Abi::RustCall + { + 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); + fargs.append( + &mut self.codegen_untupled_args(untupled, &fn_abi.args[first_args.len()..]), + ); + fargs + }; if let Some(hk) = self.hooks.hook_applies(self.tcx, instance) { return hk.handle(self, instance, fargs, destination, *target, span); @@ -573,7 +574,16 @@ impl<'tcx> GotocCtx<'tcx> { Stmt::block(stmts, loc) } // Function call through a pointer - TyKind::RigidTy(RigidTy::FnPtr(_)) => { + TyKind::RigidTy(RigidTy::FnPtr(fn_sig)) => { + let fn_sig_internal = rustc_internal::internal(self.tcx, fn_sig); + let fn_ptr_abi = rustc_internal::stable( + self.tcx + .fn_abi_of_fn_ptr( + ParamEnv::reveal_all().and((fn_sig_internal, &List::empty())), + ) + .unwrap(), + ); + 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/codegen/ty_stable.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/ty_stable.rs index 0130beffe84b..7ba2ff9c53c8 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/ty_stable.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/ty_stable.rs @@ -115,6 +115,11 @@ impl<'tcx> GotocCtx<'tcx> { pub fn pretty_ty(&self, ty: Ty) -> String { rustc_internal::internal(self.tcx, ty).to_string() } + + pub fn requires_caller_location(&self, instance: Instance) -> bool { + let instance_internal = rustc_internal::internal(self.tcx, instance); + instance_internal.def.requires_caller_location(self.tcx) + } } /// If given type is a Ref / Raw ref, return the pointee type. pub fn pointee_type(mir_type: Ty) -> Option { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index b0e3c115d597..57af958abf41 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -21,7 +21,6 @@ use rustc_target::abi::{ Abi::Vector, FieldIdx, FieldsShape, Integer, LayoutS, Primitive, Size, TagEncoding, TyAndLayout, VariantIdx, Variants, }; -use rustc_target::spec::abi::Abi; use stable_mir::abi::{ArgAbi, FnAbi, PassMode}; use stable_mir::mir::mono::Instance as InstanceStable; use stable_mir::mir::Body; @@ -731,39 +730,6 @@ impl<'tcx> GotocCtx<'tcx> { self.codegen_struct_fields(flds, &layout.layout.0, Size::ZERO) } - /// A closure / some shims in Rust MIR takes two arguments: - /// - /// 0. a struct representing the environment - /// 1. a tuple containing the parameters - /// - /// However, during codegen/lowering from MIR, the 2nd tuple of parameters - /// is flattened into subsequent parameters. - /// - /// Checking whether the type's kind is a closure is insufficient, because - /// a virtual method call through a vtable can have the trait's non-closure - /// type. For example: - /// - /// ``` - /// let p: &dyn Fn(i32) = &|x| assert!(x == 1); - /// p(1); - /// ``` - /// - /// Here, the call `p(1)` desugars to an MIR trait call `Fn::call(&p, (1,))`, - /// where the second argument is a tuple. The instance type kind for - /// `Fn::call` is not a closure, because dynamically, the pointer may be to - /// a function definition instead. We still need to untuple in this case, - /// so we follow the example elsewhere in Rust to use the ABI call type. - /// - /// See `make_call_args` in `rustc_mir_transform/src/inline.rs` - pub fn ty_needs_untupled_args(&self, ty: Ty<'tcx>) -> bool { - // Note that [Abi::RustCall] is not [Abi::Rust]. - // Documentation is sparse, but it does seem to correspond to the need for untupling. - match ty.kind() { - ty::FnDef(..) | ty::FnPtr(..) => ty.fn_sig(self.tcx).abi() == Abi::RustCall, - _ => unreachable!("Can't treat type as a function: {:?}", ty), - } - } - /// A closure is a struct of all its environments. That is, a closure is /// just a tuple with a unique type identifier, so that Fn related traits /// can find its impl. @@ -1647,13 +1613,12 @@ impl<'tcx> GotocCtx<'tcx> { /// 1. In some cases, an argument can be ignored (e.g.: ZST arguments in regular Rust calls). /// 2. We currently don't support `track_caller`, so we ignore the extra argument that is added to support that. /// Tracked here: - fn codegen_args<'a>( + pub fn codegen_args<'a>( &self, instance: InstanceStable, fn_abi: &'a FnAbi, ) -> impl Iterator { - let instance_internal = rustc_internal::internal(self.tcx, instance); - let requires_caller_location = instance_internal.def.requires_caller_location(self.tcx); + let requires_caller_location = self.requires_caller_location(instance); let num_args = fn_abi.args.len(); fn_abi.args.iter().enumerate().filter(move |(idx, arg_abi)| { arg_abi.mode != PassMode::Ignore && !(requires_caller_location && idx + 1 == num_args) diff --git a/kani-compiler/src/kani_middle/intrinsics.rs b/kani-compiler/src/kani_middle/intrinsics.rs index 8a7fc16d8e9f..404cb0f277c3 100644 --- a/kani-compiler/src/kani_middle/intrinsics.rs +++ b/kani-compiler/src/kani_middle/intrinsics.rs @@ -101,8 +101,8 @@ fn resolve_rust_intrinsic<'tcx>( func_ty: Ty<'tcx>, ) -> Option<(Symbol, GenericArgsRef<'tcx>)> { if let ty::FnDef(def_id, args) = *func_ty.kind() { - if tcx.is_intrinsic(def_id) { - return Some((tcx.item_name(def_id), args)); + if let Some(symbol) = tcx.intrinsic(def_id) { + return Some((symbol, args)); } } None diff --git a/kani-compiler/src/main.rs b/kani-compiler/src/main.rs index 4316d188c02d..fef0fba66489 100644 --- a/kani-compiler/src/main.rs +++ b/kani-compiler/src/main.rs @@ -13,6 +13,7 @@ #![feature(lazy_cell)] #![feature(more_qualified_paths)] #![feature(iter_intersperse)] +#![feature(let_chains)] extern crate rustc_abi; extern crate rustc_ast; extern crate rustc_ast_pretty; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 42bd4779102f..42f6af899b43 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-02-14" +channel = "nightly-2024-02-17" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/kani/Closure/zst_unwrap.rs b/tests/kani/Closure/zst_unwrap.rs new file mode 100644 index 000000000000..4c755e08abc7 --- /dev/null +++ b/tests/kani/Closure/zst_unwrap.rs @@ -0,0 +1,16 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +//! Test that Kani can properly handle closure to fn ptr when an argument type is Never (`!`). +//! See for more details. +#![feature(never_type)] + +pub struct Foo { + _x: i32, + _never: !, +} + +#[kani::proof] +fn check_unwrap_never() { + let res = Result::::Ok(3); + let _x = res.unwrap_or_else(|_f| 5); +} diff --git a/tests/kani/SizeAndAlignOfDst/main_assert.rs b/tests/kani/SizeAndAlignOfDst/main_assert.rs new file mode 100644 index 000000000000..09f7b1b0c774 --- /dev/null +++ b/tests/kani/SizeAndAlignOfDst/main_assert.rs @@ -0,0 +1,58 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This is a regression test for size_and_align_of_dst computing the +//! size and alignment of a dynamically-sized type like +//! Arc>. +//! + +/// This test fails on macos but not in other platforms. +/// Thus only enable it for platforms where this shall succeed. +#[cfg(not(target_os = "macos"))] +mod not_macos { + use std::sync::Arc; + use std::sync::Mutex; + + pub trait Subscriber { + fn process(&self); + fn increment(&mut self); + fn get(&self) -> u32; + } + + struct DummySubscriber { + val: u32, + } + + impl DummySubscriber { + fn new() -> Self { + DummySubscriber { val: 0 } + } + } + + impl Subscriber for DummySubscriber { + fn process(&self) {} + fn increment(&mut self) { + self.val = self.val + 1; + } + fn get(&self) -> u32 { + self.val + } + } + + #[kani::proof] + #[kani::unwind(2)] + fn simplified() { + let s: Arc> = Arc::new(Mutex::new(DummySubscriber::new())); + let data = s.lock().unwrap(); + assert!(data.get() == 0); + } + + #[kani::proof] + #[kani::unwind(1)] + fn original() { + let s: Arc> = Arc::new(Mutex::new(DummySubscriber::new())); + let mut data = s.lock().unwrap(); + data.increment(); + assert!(data.get() == 1); + } +} diff --git a/tests/kani/SizeAndAlignOfDst/main_assert_fixme.rs b/tests/kani/SizeAndAlignOfDst/main_assert_fixme.rs index 9173eca7db3c..25075bc910d9 100644 --- a/tests/kani/SizeAndAlignOfDst/main_assert_fixme.rs +++ b/tests/kani/SizeAndAlignOfDst/main_assert_fixme.rs @@ -8,51 +8,64 @@ //! Arc>. //! We added a simplified version of the original harness from: //! -//! This currently fails due to -//! +//! This currently fails on MacOS instances due to unsupported foreign function: +//! `pthread_mutexattr_init`. -use std::sync::Arc; -use std::sync::Mutex; +#[cfg(target_os = "macos")] +mod macos { + use std::sync::Arc; + use std::sync::Mutex; -pub trait Subscriber { - fn process(&self); - fn increment(&mut self); - fn get(&self) -> u32; -} + pub trait Subscriber { + fn process(&self); + fn increment(&mut self); + fn get(&self) -> u32; + } -struct DummySubscriber { - val: u32, -} + struct DummySubscriber { + val: u32, + } -impl DummySubscriber { - fn new() -> Self { - DummySubscriber { val: 0 } + impl DummySubscriber { + fn new() -> Self { + DummySubscriber { val: 0 } + } } -} -impl Subscriber for DummySubscriber { - fn process(&self) {} - fn increment(&mut self) { - self.val = self.val + 1; + impl Subscriber for DummySubscriber { + fn process(&self) {} + fn increment(&mut self) { + self.val = self.val + 1; + } + fn get(&self) -> u32 { + self.val + } } - fn get(&self) -> u32 { - self.val + + #[kani::proof] + #[kani::unwind(2)] + fn simplified() { + let s: Arc> = Arc::new(Mutex::new(DummySubscriber::new())); + let data = s.lock().unwrap(); + assert!(data.get() == 0); } -} -#[kani::proof] -#[kani::unwind(2)] -fn simplified() { - let s: Arc> = Arc::new(Mutex::new(DummySubscriber::new())); - let data = s.lock().unwrap(); - assert!(data.get() == 0); + #[kani::proof] + #[kani::unwind(1)] + fn original() { + let s: Arc> = Arc::new(Mutex::new(DummySubscriber::new())); + let mut data = s.lock().unwrap(); + data.increment(); + assert!(data.get() == 1); + } } -#[kani::proof] -#[kani::unwind(1)] -fn original() { - let s: Arc> = Arc::new(Mutex::new(DummySubscriber::new())); - let mut data = s.lock().unwrap(); - data.increment(); - assert!(data.get() == 1); +#[cfg(not(target_os = "macos"))] +mod not_macos { + /// Since this is a fixme test, it must also fail in other platforms. + /// Remove this once we fix the issue above. + #[kani::proof] + fn fail() { + assert!(false); + } } diff --git a/tests/kani/Tuple/tuple_trait.rs b/tests/kani/Tuple/tuple_trait.rs new file mode 100644 index 000000000000..d775b393b0b8 --- /dev/null +++ b/tests/kani/Tuple/tuple_trait.rs @@ -0,0 +1,19 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Tests support for functions declared with "rust-call" ABI and an empty set of arguments. +#![feature(unboxed_closures, tuple_trait)] + +extern "rust-call" fn foo(_: T) -> usize { + static mut COUNTER: usize = 0; + unsafe { + COUNTER += 1; + COUNTER + } +} + +#[kani::proof] +fn main() { + assert_eq!(foo(()), 1); + assert_eq!(foo(()), 2); +} From 9b9094ddfbec1101cfc244b623dd14a2d538869f Mon Sep 17 00:00:00 2001 From: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Date: Thu, 29 Feb 2024 10:45:09 -0500 Subject: [PATCH 017/225] Upgrade `windows-targets` crate to version 0.52.4 (#3049) Upgrades the `windows-targets` dependency to version 0.52.4. This is needed because versions 0.52.1 to 0.52.3 of `windows-targets` have been yanked (see it in [crates.io](https://crates.io/crates/windows-targets/versions) or [this CI run](https://github.com/model-checking/kani/actions/runs/8087150315/job/22098459275?pr=3047#step:4:609)). --- Cargo.lock | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe8465a3b295..434c0e7bb330 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1386,7 +1386,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.3", + "windows-targets 0.52.4", ] [[package]] @@ -1406,17 +1406,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.52.3", - "windows_aarch64_msvc 0.52.3", - "windows_i686_gnu 0.52.3", - "windows_i686_msvc 0.52.3", - "windows_x86_64_gnu 0.52.3", - "windows_x86_64_gnullvm 0.52.3", - "windows_x86_64_msvc 0.52.3", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -1427,9 +1427,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -1439,9 +1439,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -1451,9 +1451,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -1463,9 +1463,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -1475,9 +1475,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -1487,9 +1487,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -1499,9 +1499,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" From 73b4c47a2192973fd066773c2030fbcb07f3cb10 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Thu, 29 Feb 2024 14:57:12 -0600 Subject: [PATCH 018/225] Fix `codegen_atomic_binop` for `atomic_ptr` (#3047) Fetch functions of `atomic_ptr` calls atomic intrinsic functions with pointer-type arguments (`invalid_mut`), which will cause typecheck failures. The change in this commit add support of pointer-type arguments into `codegen_atomic_binop` to fix the issue. The new `codegen_atomic_binop` will cast pointer arguments to `size_t`, apply op on them, and then cast the op result back to the pointer type. The new test include all fetch functions of `atomic_ptr` except for `fetch_ptr_add` and `fetch_ptr_sub`, which do not call intrinsic functions. Resolves #3042. --------- Co-authored-by: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Co-authored-by: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> --- .../codegen/intrinsic.rs | 22 +++++- .../Atomic/Stable/AtomicPtr/main.rs | 72 +++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 tests/kani/Intrinsics/Atomic/Stable/AtomicPtr/main.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index e497cfc6b47c..badf07d4e132 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -239,6 +239,19 @@ impl<'tcx> GotocCtx<'tcx> { // *var1 = op(*var1, var2); // var = tmp; // ------------------------- + // + // In fetch functions of atomic_ptr such as https://doc.rust-lang.org/std/sync/atomic/struct.AtomicPtr.html#method.fetch_byte_add, + // the type of var2 can be pointer (invalid_mut). + // In such case, atomic binops are transformed as follows to avoid typecheck failure. + // ------------------------- + // var = atomic_op(var1, var2) + // ------------------------- + // unsigned char tmp; + // tmp = *var1; + // *var1 = (typeof var1)op((size_t)*var1, (size_t)var2); + // var = tmp; + // ------------------------- + // // Note: Atomic arithmetic operations wrap around on overflow. macro_rules! codegen_atomic_binop { ($op: ident) => {{ @@ -249,7 +262,14 @@ impl<'tcx> GotocCtx<'tcx> { let (tmp, decl_stmt) = self.decl_temp_variable(var1.typ().clone(), Some(var1.to_owned()), loc); let var2 = fargs.remove(0); - let op_expr = (var1.clone()).$op(var2).with_location(loc); + let op_expr = if var2.typ().is_pointer() { + (var1.clone().cast_to(Type::c_size_t())) + .$op(var2.cast_to(Type::c_size_t())) + .with_location(loc) + .cast_to(var1.typ().clone()) + } else { + (var1.clone()).$op(var2).with_location(loc) + }; let assign_stmt = (var1.clone()).assign(op_expr, loc); let res_stmt = self.codegen_expr_to_place_stable(place, tmp.clone()); Stmt::atomic_block(vec![decl_stmt, assign_stmt, res_stmt], loc) diff --git a/tests/kani/Intrinsics/Atomic/Stable/AtomicPtr/main.rs b/tests/kani/Intrinsics/Atomic/Stable/AtomicPtr/main.rs new file mode 100644 index 000000000000..c703c7c7125f --- /dev/null +++ b/tests/kani/Intrinsics/Atomic/Stable/AtomicPtr/main.rs @@ -0,0 +1,72 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Test atomic intrinsics through the stable interface of atomic_ptr. +// Specifically, it checks that Kani correctly handles atomic_ptr's fetch methods, in which the second argument is a pointer type. +// These methods were not correctly handled as explained in https://github.com/model-checking/kani/issues/3042. + +#![feature(strict_provenance_atomic_ptr, strict_provenance)] +use std::sync::atomic::{AtomicPtr, Ordering}; + +#[kani::proof] +fn check_fetch_byte_add() { + let atom = AtomicPtr::::new(core::ptr::null_mut()); + assert_eq!(atom.fetch_byte_add(1, Ordering::Relaxed).addr(), 0); + // Note: in units of bytes, not `size_of::()`. + assert_eq!(atom.load(Ordering::Relaxed).addr(), 1); +} + +#[kani::proof] +fn check_fetch_byte_sub() { + let atom = AtomicPtr::::new(core::ptr::invalid_mut(1)); + assert_eq!(atom.fetch_byte_sub(1, Ordering::Relaxed).addr(), 1); + assert_eq!(atom.load(Ordering::Relaxed).addr(), 0); +} + +#[kani::proof] +fn check_fetch_and() { + let pointer = &mut 3i64 as *mut i64; + // A tagged pointer + let atom = AtomicPtr::::new(pointer.map_addr(|a| a | 1)); + assert_eq!(atom.fetch_or(1, Ordering::Relaxed).addr() & 1, 1); + // Untag, and extract the previously tagged pointer. + let untagged = atom.fetch_and(!1, Ordering::Relaxed).map_addr(|a| a & !1); + assert_eq!(untagged, pointer); +} + +#[kani::proof] +fn check_fetch_or() { + let pointer = &mut 3i64 as *mut i64; + + let atom = AtomicPtr::::new(pointer); + // Tag the bottom bit of the pointer. + assert_eq!(atom.fetch_or(1, Ordering::Relaxed).addr() & 1, 0); + // Extract and untag. + let tagged = atom.load(Ordering::Relaxed); + assert_eq!(tagged.addr() & 1, 1); + assert_eq!(tagged.map_addr(|p| p & !1), pointer); +} + +#[kani::proof] +fn check_fetch_update() { + let ptr: *mut _ = &mut 5; + let some_ptr = AtomicPtr::new(ptr); + + let new: *mut _ = &mut 10; + assert_eq!(some_ptr.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(ptr)); + let result = some_ptr.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| { + if x == ptr { Some(new) } else { None } + }); + assert_eq!(result, Ok(ptr)); + assert_eq!(some_ptr.load(Ordering::SeqCst), new); +} + +#[kani::proof] +fn check_fetch_xor() { + let pointer = &mut 3i64 as *mut i64; + let atom = AtomicPtr::::new(pointer); + + // Toggle a tag bit on the pointer. + atom.fetch_xor(1, Ordering::Relaxed); + assert_eq!(atom.load(Ordering::Relaxed).addr() & 1, 1); +} From 46bdb75d6ea887ddd21b2e90c6bf32c110978751 Mon Sep 17 00:00:00 2001 From: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Date: Thu, 29 Feb 2024 17:07:43 -0500 Subject: [PATCH 019/225] Upgrade Rust toolchain to `nightly-2024-02-25` (#3048) Upgrades the Rust toolchain to `nightly-2024-02-25`. The Rust compiler PRs that triggered changes in this upgrades are: * https://github.com/rust-lang/rust/pull/121209 * https://github.com/rust-lang/rust/pull/121309 * https://github.com/rust-lang/rust/pull/120863 * https://github.com/rust-lang/rust/pull/117772 * https://github.com/rust-lang/rust/pull/117658 With https://github.com/rust-lang/rust/pull/121309 some intrinsics became inlineable so their names became qualified. This made our `match` on the intrinsic name to fail in those cases, leaving them as unsupported constructs as in this example: ``` warning: Found the following unsupported constructs: - _RNvNtCscyGW2MM2t5j_4core10intrinsics8unlikelyCs1eohKeNmpdS_5arith (3) - caller_location (1) - foreign function (1) Verification will fail if one or more of these constructs is reachable. See https://model-checking.github.io/kani/rust-feature-support.html for more details. [...] Failed Checks: _RNvNtCscyGW2MM2t5j_4core10intrinsics8unlikelyCs1eohKeNmpdS_5arith is not currently supported by Kani. Please post your example at https://github.com/model-checking/kani/issues/new/choose File: "/home/ubuntu/.rustup/toolchains/nightly-2024-02-18-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/num/mod.rs", line 25, in core::num::::checked_add ``` We use `trimmed_name()` to work around this, but that may include type arguments if the intrinsic is defined on generics. So in those cases, we just take the first part of the name so we can keep the rest as before. Resolves #3044 --- cprover_bindings/src/goto_program/location.rs | 1 - cprover_bindings/src/goto_program/typ.rs | 1 - .../codegen_cprover_gotoc/codegen/assert.rs | 1 - .../codegen_cprover_gotoc/codegen/function.rs | 1 - .../codegen/intrinsic.rs | 19 ++++++++++++++++++- .../compiler_interface.rs | 5 ++--- kani-compiler/src/kani_compiler.rs | 4 +--- kani-compiler/src/kani_middle/metadata.rs | 1 - kani-compiler/src/kani_middle/provide.rs | 1 - kani-compiler/src/kani_middle/reachability.rs | 2 +- kani-driver/src/args/playback_args.rs | 2 -- rust-toolchain.toml | 2 +- tests/expected/any_vec/exact_length.expected | 4 ++-- tests/expected/any_vec/out_bounds.expected | 2 +- .../Atomic/Stable/AtomicPtr/main.rs | 2 +- tests/kani/Intrinsics/Likely/main.rs | 8 +++++++- tools/bookrunner/librustdoc/html/markdown.rs | 1 - tools/compiletest/src/runtest.rs | 1 - 18 files changed, 34 insertions(+), 24 deletions(-) diff --git a/cprover_bindings/src/goto_program/location.rs b/cprover_bindings/src/goto_program/location.rs index 79b123ad8b0e..4097d075276d 100644 --- a/cprover_bindings/src/goto_program/location.rs +++ b/cprover_bindings/src/goto_program/location.rs @@ -1,7 +1,6 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT use crate::cbmc_string::{InternStringOption, InternedString}; -use std::convert::TryInto; use std::fmt::Debug; /// A `Location` represents a source location. diff --git a/cprover_bindings/src/goto_program/typ.rs b/cprover_bindings/src/goto_program/typ.rs index dd07c150bb3f..da943b26ab19 100644 --- a/cprover_bindings/src/goto_program/typ.rs +++ b/cprover_bindings/src/goto_program/typ.rs @@ -7,7 +7,6 @@ use super::super::MachineModel; use super::{Expr, SymbolTable}; use crate::cbmc_string::InternedString; use std::collections::BTreeMap; -use std::convert::TryInto; use std::fmt::Debug; /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs index 68573cd2a1cd..cad3595bca50 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs @@ -22,7 +22,6 @@ use crate::codegen_cprover_gotoc::GotocCtx; use cbmc::goto_program::{Expr, Location, Stmt, Type}; use cbmc::InternedString; use stable_mir::ty::Span as SpanStable; -use std::convert::AsRef; use strum_macros::{AsRefStr, EnumString}; use tracing::debug; diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs index d4061d4271db..78c7e2b6cbd6 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs @@ -12,7 +12,6 @@ use stable_mir::mir::{Body, Local}; use stable_mir::ty::{RigidTy, TyKind}; use stable_mir::CrateDef; use std::collections::BTreeMap; -use std::iter::FromIterator; use tracing::{debug, debug_span}; /// Codegen MIR functions into gotoc diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index badf07d4e132..7996d434c351 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -112,7 +112,7 @@ impl<'tcx> GotocCtx<'tcx> { place: &Place, span: Span, ) -> Stmt { - let intrinsic_sym = instance.mangled_name(); + let intrinsic_sym = instance.trimmed_name(); let intrinsic = intrinsic_sym.as_str(); let loc = self.codegen_span_stable(span); debug!(?instance, "codegen_intrinsic"); @@ -288,6 +288,23 @@ impl<'tcx> GotocCtx<'tcx> { }}; } + /// Gets the basename of an intrinsic given its trimmed name. + /// + /// For example, given `arith_offset::` this returns `arith_offset`. + fn intrinsic_basename(name: &str) -> &str { + let scope_sep_count = name.matches("::").count(); + // We expect at most one `::` separator from trimmed intrinsic names + debug_assert!( + scope_sep_count < 2, + "expected at most one `::` in intrinsic name, but found {scope_sep_count} in `{name}`" + ); + let name_split = name.split_once("::"); + if let Some((base_name, _type_args)) = name_split { base_name } else { name } + } + // The trimmed name includes type arguments if the intrinsic was defined + // on generic types, but we only need the basename for the match below. + let intrinsic = intrinsic_basename(intrinsic); + if let Some(stripped) = intrinsic.strip_prefix("simd_shuffle") { assert!(fargs.len() == 3, "`simd_shuffle` had unexpected arguments {fargs:?}"); let n: u64 = self.simd_shuffle_length(stripped, farg_types, span); diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index 810c5707aad4..91e6c2538195 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -53,7 +53,6 @@ use std::ffi::OsString; use std::fmt::Write; use std::fs::File; use std::io::BufWriter; -use std::iter::FromIterator; use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::{Arc, Mutex}; @@ -353,10 +352,10 @@ impl CodegenBackend for GotocCodegenBackend { ongoing_codegen: Box, _sess: &Session, _filenames: &OutputFilenames, - ) -> Result<(CodegenResults, FxIndexMap), ErrorGuaranteed> { + ) -> (CodegenResults, FxIndexMap) { match ongoing_codegen.downcast::<(CodegenResults, FxIndexMap)>() { - Ok(val) => Ok(*val), + Ok(val) => *val, Err(val) => panic!("unexpected error: {:?}", (*val).type_id()), } } diff --git a/kani-compiler/src/kani_compiler.rs b/kani-compiler/src/kani_compiler.rs index 118821f5995f..48b4318db5bf 100644 --- a/kani-compiler/src/kani_compiler.rs +++ b/kani-compiler/src/kani_compiler.rs @@ -465,10 +465,8 @@ fn metadata_output_path(tcx: TyCtxt) -> PathBuf { #[cfg(test)] mod tests { use super::*; - use kani_metadata::{HarnessAttributes, HarnessMetadata}; + use kani_metadata::HarnessAttributes; use rustc_data_structures::fingerprint::Fingerprint; - use rustc_hir::definitions::DefPathHash; - use std::collections::HashMap; fn mock_next_harness_id() -> HarnessId { static mut COUNTER: u64 = 0; diff --git a/kani-compiler/src/kani_middle/metadata.rs b/kani-compiler/src/kani_middle/metadata.rs index 02f5da107556..9236de48308f 100644 --- a/kani-compiler/src/kani_middle/metadata.rs +++ b/kani-compiler/src/kani_middle/metadata.rs @@ -3,7 +3,6 @@ //! This module handles Kani metadata generation. For example, generating HarnessMetadata for a //! given function. -use std::default::Default; use std::path::Path; use crate::kani_middle::attributes::test_harness_name; diff --git a/kani-compiler/src/kani_middle/provide.rs b/kani-compiler/src/kani_middle/provide.rs index 70e046d6f9d6..d5495acb67a7 100644 --- a/kani-compiler/src/kani_middle/provide.rs +++ b/kani-compiler/src/kani_middle/provide.rs @@ -10,7 +10,6 @@ use crate::kani_middle::reachability::{collect_reachable_items, filter_crate_ite use crate::kani_middle::stubbing; use crate::kani_queries::QueryDb; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_interface; use rustc_middle::util::Providers; use rustc_middle::{mir::Body, query::queries, ty::TyCtxt}; use stable_mir::mir::mono::MonoItem; diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 63ed41bfd4c1..11ba57cb1cff 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -31,7 +31,7 @@ use stable_mir::mir::{ TerminatorKind, }; use stable_mir::ty::{Allocation, ClosureKind, ConstantKind, RigidTy, Ty, TyKind}; -use stable_mir::{self, CrateItem}; +use stable_mir::CrateItem; use stable_mir::{CrateDef, ItemKind}; use crate::kani_middle::coercion; diff --git a/kani-driver/src/args/playback_args.rs b/kani-driver/src/args/playback_args.rs index bdad446a1158..ad82d9632a7a 100644 --- a/kani-driver/src/args/playback_args.rs +++ b/kani-driver/src/args/playback_args.rs @@ -100,8 +100,6 @@ impl ValidateArgs for PlaybackArgs { #[cfg(test)] mod tests { - use clap::Parser; - use super::*; #[test] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 42f6af899b43..408b2e859604 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-02-17" +channel = "nightly-2024-02-25" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/expected/any_vec/exact_length.expected b/tests/expected/any_vec/exact_length.expected index 93feecb214e5..f64d2830a7b8 100644 --- a/tests/expected/any_vec/exact_length.expected +++ b/tests/expected/any_vec/exact_length.expected @@ -1,12 +1,12 @@ Checking harness check_access_length_17... Failed Checks: assumption failed\ -in std::hint::assert_unchecked +in >::get_unchecked Checking harness check_access_length_zero... Failed Checks: assumption failed\ -in std::hint::assert_unchecked +in >::get_unchecked Verification failed for - check_access_length_17 Verification failed for - check_access_length_zero diff --git a/tests/expected/any_vec/out_bounds.expected b/tests/expected/any_vec/out_bounds.expected index 71132a64f67d..24121aee4ee8 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 -in std::hint::assert_unchecked +in >::get_unchecked Verification failed for - check_always_out_bounds diff --git a/tests/kani/Intrinsics/Atomic/Stable/AtomicPtr/main.rs b/tests/kani/Intrinsics/Atomic/Stable/AtomicPtr/main.rs index c703c7c7125f..4e9d68619fd7 100644 --- a/tests/kani/Intrinsics/Atomic/Stable/AtomicPtr/main.rs +++ b/tests/kani/Intrinsics/Atomic/Stable/AtomicPtr/main.rs @@ -18,7 +18,7 @@ fn check_fetch_byte_add() { #[kani::proof] fn check_fetch_byte_sub() { - let atom = AtomicPtr::::new(core::ptr::invalid_mut(1)); + let atom = AtomicPtr::::new(core::ptr::without_provenance_mut(1)); assert_eq!(atom.fetch_byte_sub(1, Ordering::Relaxed).addr(), 1); assert_eq!(atom.load(Ordering::Relaxed).addr(), 0); } diff --git a/tests/kani/Intrinsics/Likely/main.rs b/tests/kani/Intrinsics/Likely/main.rs index 524fbd18ddc8..14643241ef90 100644 --- a/tests/kani/Intrinsics/Likely/main.rs +++ b/tests/kani/Intrinsics/Likely/main.rs @@ -28,9 +28,15 @@ fn check_unlikely(x: i32, y: i32) { } #[kani::proof] -fn main() { +fn check_likely_main() { let x = kani::any(); let y = kani::any(); check_likely(x, y); +} + +#[kani::proof] +fn check_unlikely_main() { + let x = kani::any(); + let y = kani::any(); check_unlikely(x, y); } diff --git a/tools/bookrunner/librustdoc/html/markdown.rs b/tools/bookrunner/librustdoc/html/markdown.rs index 1bcd5dad7532..3f827b77ff2b 100644 --- a/tools/bookrunner/librustdoc/html/markdown.rs +++ b/tools/bookrunner/librustdoc/html/markdown.rs @@ -7,7 +7,6 @@ use rustc_span::edition::Edition; -use std::default::Default; use std::str; use std::{borrow::Cow, marker::PhantomData}; diff --git a/tools/compiletest/src/runtest.rs b/tools/compiletest/src/runtest.rs index ee89c252dc4f..7925ed83e6e5 100644 --- a/tools/compiletest/src/runtest.rs +++ b/tools/compiletest/src/runtest.rs @@ -23,7 +23,6 @@ use std::process::{Command, ExitStatus, Output, Stdio}; use std::str; use serde::{Deserialize, Serialize}; -use serde_yaml; use tracing::*; use wait_timeout::ChildExt; From aaef7c7eb575a0f31781d2fe0dcdfe504861e215 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Thu, 29 Feb 2024 21:25:13 -0800 Subject: [PATCH 020/225] Update s2n-quic submodule (#3050) Update the s2n-quic submodule to the latest commit. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- tests/perf/overlays/s2n-quic/common/s2n-codec/expected | 1 + tests/perf/s2n-quic | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 tests/perf/overlays/s2n-quic/common/s2n-codec/expected diff --git a/tests/perf/overlays/s2n-quic/common/s2n-codec/expected b/tests/perf/overlays/s2n-quic/common/s2n-codec/expected new file mode 100644 index 000000000000..610b05dc4e67 --- /dev/null +++ b/tests/perf/overlays/s2n-quic/common/s2n-codec/expected @@ -0,0 +1 @@ +successfully verified harnesses, 0 failures diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 311ece35722d..d103836ce086 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 311ece35722d9bebb8b95fdd6f16c2b6887aa46b +Subproject commit d103836ce086534e63c75a0b497079ed74e58c18 From fa9f61df0b6a680274c9e63cd6c5a9f323e961be Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Fri, 1 Mar 2024 14:03:54 -0800 Subject: [PATCH 021/225] Update s2n-quic submodule weekly through dependabot (#3053) Use dependabot to update the `s2n-quic` submodule weekly (every Monday). This is to avoid having to do this effort manually, and to guarantee that none of the `s2n-quic` proofs break due to changes in Kani. I've tested this in my own fork, and it seemed to work correctly. --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7647990dd685..7f78dab1ece0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,3 +9,10 @@ updates: directory: "/" schedule: interval: "weekly" + + - package-ecosystem: "gitsubmodule" + directory: "/" + allow: + - dependency-name: "tests/perf/s2n-quic" + schedule: + interval: "weekly" From b4480acdfddf0e369f8584f7688c4ba2fa5413cd Mon Sep 17 00:00:00 2001 From: "Felipe R. Monteiro" Date: Sat, 2 Mar 2024 00:43:34 -0500 Subject: [PATCH 022/225] Retrieve info for recursion tracker reliably (#3045) Fixes https://github.com/model-checking/kani/issues/3035. 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 --- .../codegen_cprover_gotoc/codegen/contract.rs | 59 ++++++++++++++----- .../codegen/static_var.rs | 4 ++ .../generic_infinity_recursion.expected | 1 + .../generic_infinity_recursion.rs | 17 ++++++ 4 files changed, 65 insertions(+), 16 deletions(-) create mode 100644 tests/expected/function-contract/generic_infinity_recursion.expected create mode 100644 tests/expected/function-contract/generic_infinity_recursion.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs index 2bd3e1ff7c35..a720d1457606 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs @@ -35,8 +35,46 @@ impl<'tcx> GotocCtx<'tcx> { ) -> AssignsContract { let tcx = self.tcx; let function_under_contract_attrs = KaniAttributes::for_item(tcx, function_under_contract); - let wrapped_fn = function_under_contract_attrs.inner_check().unwrap().unwrap(); + let recursion_wrapper_id = + function_under_contract_attrs.checked_with_id().unwrap().unwrap(); + let expected_name = format!("{}::REENTRY", tcx.item_name(recursion_wrapper_id)); + let mut recursion_tracker = items.iter().filter_map(|i| match i { + MonoItem::Static(recursion_tracker) + if (*recursion_tracker).name().contains(expected_name.as_str()) => + { + Some(*recursion_tracker) + } + _ => None, + }); + let recursion_tracker_def = recursion_tracker + .next() + .expect("There should be at least one recursion tracker (REENTRY) in scope"); + assert!( + recursion_tracker.next().is_none(), + "Only one recursion tracker (REENTRY) may be in scope" + ); + + let span_of_recursion_wrapper = tcx.def_span(recursion_wrapper_id); + let location_of_recursion_wrapper = self.codegen_span(&span_of_recursion_wrapper); + // The name and location for the recursion tracker should match the exact information added + // to the symbol table, otherwise our contract instrumentation will silently failed. + // This happens because Kani relies on `--nondet-static-exclude` from CBMC to properly + // handle this tracker. CBMC silently fails if there is no match in the symbol table + // that correspond to the argument of this flag. + // More details at https://github.com/model-checking/kani/pull/3045. + let full_recursion_tracker_name = format!( + "{}:{}", + location_of_recursion_wrapper + .filename() + .expect("recursion location wrapper should have a file name"), + // We must use the pretty name of the tracker instead of the mangled name. + // This restrictions comes from `--nondet-static-exclude` in CBMC. + // Mode details at https://github.com/diffblue/cbmc/issues/8225. + recursion_tracker_def.name(), + ); + + let wrapped_fn = function_under_contract_attrs.inner_check().unwrap().unwrap(); let mut instance_under_contract = items.iter().filter_map(|i| match i { MonoItem::Fn(instance @ Instance { def, .. }) if wrapped_fn == rustc_internal::internal(tcx, def.def_id()) => @@ -56,23 +94,12 @@ impl<'tcx> GotocCtx<'tcx> { vec![] }); self.attach_modifies_contract(instance_of_check, assigns_contract); - let wrapper_name = self.symbol_name_stable(instance_of_check); - let recursion_wrapper_id = - function_under_contract_attrs.checked_with_id().unwrap().unwrap(); - let span_of_recursion_wrapper = tcx.def_span(recursion_wrapper_id); - let location_of_recursion_wrapper = self.codegen_span(&span_of_recursion_wrapper); - - let full_name = format!( - "{}:{}::REENTRY", - location_of_recursion_wrapper - .filename() - .expect("recursion location wrapper should have a file name"), - tcx.item_name(recursion_wrapper_id), - ); - - AssignsContract { recursion_tracker: full_name, contracted_function_name: wrapper_name } + AssignsContract { + recursion_tracker: full_recursion_tracker_name, + contracted_function_name: wrapper_name, + } } /// Convert the Kani level contract into a CBMC level contract by creating a diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/static_var.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/static_var.rs index cf4c6173637b..0a0a55ad3dd4 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/static_var.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/static_var.rs @@ -33,6 +33,10 @@ impl<'tcx> GotocCtx<'tcx> { let typ = self.codegen_ty_stable(instance.ty()); let location = self.codegen_span_stable(def.span()); + // Contracts instrumentation relies on `--nondet-static-exclude` to properly + // havoc static variables. Kani uses the location and pretty name to identify + // the correct variables. If the wrong name is used, CBMC may fail silently. + // More details at https://github.com/diffblue/cbmc/issues/8225. let symbol = Symbol::static_variable(symbol_name.clone(), symbol_name, typ, location) .with_is_hidden(false) // Static items are always user defined. .with_pretty_name(pretty_name); diff --git a/tests/expected/function-contract/generic_infinity_recursion.expected b/tests/expected/function-contract/generic_infinity_recursion.expected new file mode 100644 index 000000000000..34c886c358cb --- /dev/null +++ b/tests/expected/function-contract/generic_infinity_recursion.expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/generic_infinity_recursion.rs b/tests/expected/function-contract/generic_infinity_recursion.rs new file mode 100644 index 000000000000..d40b7694dbdb --- /dev/null +++ b/tests/expected/function-contract/generic_infinity_recursion.rs @@ -0,0 +1,17 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +//! Check Kani handling of generics and recursion with function contracts. + +#[kani::requires(x != 0)] +fn foo>(x: T) { + assert_ne!(x, 0); + foo(x); +} + +#[kani::proof_for_contract(foo)] +fn foo_harness() { + let input: i32 = kani::any(); + foo(input); +} From 99ee1a69d9cdd0b7f8e424b24463323d15fa403c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 3 Mar 2024 22:19:26 -0800 Subject: [PATCH 023/225] Automatic cargo update to 2024-03-04 (#3055) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 434c0e7bb330..ab853a65e544 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", @@ -35,9 +35,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -198,7 +198,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -403,9 +403,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.3" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", "hashbrown", @@ -498,7 +498,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -552,9 +552,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "matchers" @@ -816,9 +816,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" dependencies = [ "either", "rayon-core", @@ -966,7 +966,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -1078,7 +1078,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -1091,7 +1091,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -1106,9 +1106,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.50" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -1117,9 +1117,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", @@ -1144,7 +1144,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -1210,7 +1210,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -1322,9 +1322,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -1505,9 +1505,9 @@ checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" -version = "0.6.2" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a4191c47f15cc3ec71fcb4913cb83d58def65dd3787610213c649283b5ce178" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" dependencies = [ "memchr", ] @@ -1529,5 +1529,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] From 12768f247b35059e0535afe388f0c7dfabd6f907 Mon Sep 17 00:00:00 2001 From: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Date: Mon, 4 Mar 2024 16:39:48 -0500 Subject: [PATCH 024/225] Upgrade Rust toolchain to `nightly-2024-03-01` (#3052) Upgrades the Rust toolchain to `nightly-2024-03-01`. The Rust compiler PRs that triggered changes in this upgrades are: * https://github.com/rust-lang/rust/pull/121516 * https://github.com/rust-lang/rust/pull/121598 * https://github.com/rust-lang/rust/pull/121489 * https://github.com/rust-lang/rust/pull/121783 --- .../codegen/intrinsic.rs | 10 ++++---- kani-compiler/src/session.rs | 23 +++++++++++-------- kani-driver/src/cbmc_property_renderer.rs | 2 +- library/kani/src/lib.rs | 2 +- library/kani/src/models/mod.rs | 5 +--- rust-toolchain.toml | 2 +- tests/cargo-kani/assess-artifacts/expected | 2 +- .../assess-workspace-artifacts/expected | 2 +- .../intrinsics/simd-arith-overflows/main.rs | 9 ++------ .../simd-cmp-result-type-is-diff-size/main.rs | 11 ++------- .../intrinsics/simd-div-div-zero/main.rs | 7 ++---- .../intrinsics/simd-div-rem-overflow/main.rs | 11 +++------ .../simd-extract-wrong-type/main.rs | 7 ++---- .../intrinsics/simd-insert-wrong-type/main.rs | 7 ++---- .../intrinsics/simd-rem-div-zero/main.rs | 7 ++---- .../simd-result-type-is-float/main.rs | 11 ++------- .../simd-shl-shift-negative/main.rs | 7 ++---- .../simd-shl-shift-too-large/main.rs | 7 ++---- .../simd-shr-shift-negative/main.rs | 7 ++---- .../simd-shr-shift-too-large/main.rs | 7 ++---- .../simd-shuffle-indexes-out/main.rs | 7 ++---- .../main.rs | 7 ++---- .../main.rs | 7 ++---- tests/kani/Intrinsics/SIMD/Compare/float.rs | 14 ++--------- .../SIMD/Compare/result_type_is_same_size.rs | 11 ++------- tests/kani/Intrinsics/SIMD/Compare/signed.rs | 16 ++----------- .../kani/Intrinsics/SIMD/Compare/unsigned.rs | 16 ++----------- .../kani/Intrinsics/SIMD/Construction/main.rs | 8 ++----- tests/kani/Intrinsics/SIMD/Operators/arith.rs | 14 ++++------- .../kani/Intrinsics/SIMD/Operators/bitmask.rs | 9 ++------ .../Intrinsics/SIMD/Operators/bitshift.rs | 8 ++----- .../kani/Intrinsics/SIMD/Operators/bitwise.rs | 8 ++----- .../Intrinsics/SIMD/Operators/division.rs | 8 ++----- .../SIMD/Operators/division_float.rs | 7 ++---- .../SIMD/Operators/remainder_float_fixme.rs | 7 ++---- tests/kani/Intrinsics/SIMD/Shuffle/main.rs | 7 ++---- tests/kani/SIMD/simd_float_ops_fixme.rs | 8 ++----- tools/bookrunner/librustdoc/doctest.rs | 11 +++++---- 38 files changed, 91 insertions(+), 228 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 7996d434c351..20e3f34f48bf 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -415,6 +415,11 @@ impl<'tcx> GotocCtx<'tcx> { loc, "https://github.com/model-checking/kani/issues/374", ), + "catch_unwind" => self.codegen_unimplemented_stmt( + intrinsic, + loc, + "https://github.com/model-checking/kani/issues/267", + ), "ceilf32" => codegen_simple_intrinsic!(Ceilf), "ceilf64" => codegen_simple_intrinsic!(Ceil), "compare_bytes" => self.codegen_compare_bytes(fargs, place, loc), @@ -584,11 +589,6 @@ impl<'tcx> GotocCtx<'tcx> { "transmute" => self.codegen_intrinsic_transmute(fargs, ret_ty, place), "truncf32" => codegen_simple_intrinsic!(Truncf), "truncf64" => codegen_simple_intrinsic!(Trunc), - "try" => self.codegen_unimplemented_stmt( - intrinsic, - loc, - "https://github.com/model-checking/kani/issues/267", - ), "type_id" => codegen_intrinsic_const!(), "type_name" => codegen_intrinsic_const!(), "unaligned_volatile_load" => { diff --git a/kani-compiler/src/session.rs b/kani-compiler/src/session.rs index 5b5416a5f142..bc0930eb9e4b 100644 --- a/kani-compiler/src/session.rs +++ b/kani-compiler/src/session.rs @@ -4,12 +4,17 @@ //! Module used to configure a compiler session. use crate::args::Arguments; +use rustc_data_structures::sync::Lrc; +use rustc_errors::emitter::Emitter; use rustc_errors::{ - emitter::Emitter, emitter::HumanReadableErrorType, fallback_fluent_bundle, json::JsonEmitter, - ColorConfig, Diagnostic, TerminalUrl, + emitter::HumanReadableErrorType, fallback_fluent_bundle, json::JsonEmitter, ColorConfig, + DiagInner, }; use rustc_session::config::ErrorOutputType; use rustc_session::EarlyDiagCtxt; +use rustc_span::source_map::FilePathMapping; +use rustc_span::source_map::SourceMap; +use std::io; use std::io::IsTerminal; use std::panic; use std::sync::LazyLock; @@ -49,17 +54,15 @@ static JSON_PANIC_HOOK: LazyLock) + Sync + Send let msg = format!("Kani unexpectedly panicked at {info}.",); let fallback_bundle = fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false); - let mut emitter = JsonEmitter::basic( - false, - HumanReadableErrorType::Default(ColorConfig::Never), - None, + 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())), fallback_bundle, - None, false, - false, - TerminalUrl::No, + HumanReadableErrorType::Default(ColorConfig::Never), ); - let diagnostic = Diagnostic::new(rustc_errors::Level::Bug, msg); + let diagnostic = DiagInner::new(rustc_errors::Level::Bug, msg); emitter.emit_diagnostic(diagnostic); (*JSON_PANIC_HOOK)(info); })); diff --git a/kani-driver/src/cbmc_property_renderer.rs b/kani-driver/src/cbmc_property_renderer.rs index 62ef748a1ad5..c233959abd6a 100644 --- a/kani-driver/src/cbmc_property_renderer.rs +++ b/kani-driver/src/cbmc_property_renderer.rs @@ -837,7 +837,7 @@ fn annotate_properties_with_reach_results( let prop_match_id = check_marker_pat.captures(description.as_str()).unwrap().get(0).unwrap().as_str(); // Get the status associated to the ID we captured - let reach_status_opt = reach_map.get(&prop_match_id.to_string()); + let reach_status_opt = reach_map.get(prop_match_id); // Update the reachability status of the property if let Some(reach_status) = reach_status_opt { prop.reach = Some(*reach_status); diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index e3456f69fbb3..087e23c7d47e 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -13,7 +13,7 @@ // Used to model simd. #![feature(repr_simd)] // Features used for tests only. -#![cfg_attr(test, feature(platform_intrinsics, portable_simd))] +#![cfg_attr(test, feature(core_intrinsics, portable_simd))] // Required for rustc_diagnostic_item #![allow(internal_features)] diff --git a/library/kani/src/models/mod.rs b/library/kani/src/models/mod.rs index 194f220595c0..2081ddf639df 100644 --- a/library/kani/src/models/mod.rs +++ b/library/kani/src/models/mod.rs @@ -127,12 +127,9 @@ mod intrinsics { #[cfg(test)] mod test { use super::intrinsics as kani_intrinsic; + use std::intrinsics::simd::*; use std::{fmt::Debug, simd::*}; - extern "platform-intrinsic" { - fn simd_bitmask(x: T) -> U; - } - /// Test that the `simd_bitmask` model is equivalent to the intrinsic for all true and all false /// masks with lanes represented using i16. #[test] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 408b2e859604..747a9f2f5295 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-02-25" +channel = "nightly-2024-03-01" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/cargo-kani/assess-artifacts/expected b/tests/cargo-kani/assess-artifacts/expected index c1d3acbfd531..b8a25c834ab6 100644 --- a/tests/cargo-kani/assess-artifacts/expected +++ b/tests/cargo-kani/assess-artifacts/expected @@ -3,7 +3,7 @@ Analyzed 1 packages Unsupported feature | Crates | Instances | impacted | of use ---------------------+----------+----------- - try | 1 | 2 + catch_unwind | 1 | 2 ============================================ ========================================= Reason for failure | Number of tests diff --git a/tests/cargo-kani/assess-workspace-artifacts/expected b/tests/cargo-kani/assess-workspace-artifacts/expected index 4e9a26f89c27..fba2cd94f212 100644 --- a/tests/cargo-kani/assess-workspace-artifacts/expected +++ b/tests/cargo-kani/assess-workspace-artifacts/expected @@ -3,7 +3,7 @@ Analyzed 2 packages Unsupported feature | Crates | Instances | impacted | of use ---------------------+----------+----------- - try | 2 | 3 + catch_unwind | 2 | 3 ============================================ ========================================= Reason for failure | Number of tests diff --git a/tests/expected/intrinsics/simd-arith-overflows/main.rs b/tests/expected/intrinsics/simd-arith-overflows/main.rs index 74cfe8ae878f..fc710645fd66 100644 --- a/tests/expected/intrinsics/simd-arith-overflows/main.rs +++ b/tests/expected/intrinsics/simd-arith-overflows/main.rs @@ -2,19 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! This test ensures we detect overflows in SIMD arithmetic operations -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::{simd_add, simd_mul, simd_sub}; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct i8x2(i8, i8); -extern "platform-intrinsic" { - fn simd_add(x: T, y: T) -> T; - fn simd_sub(x: T, y: T) -> T; - fn simd_mul(x: T, y: T) -> T; -} - #[kani::proof] fn main() { let a = kani::any(); diff --git a/tests/expected/intrinsics/simd-cmp-result-type-is-diff-size/main.rs b/tests/expected/intrinsics/simd-cmp-result-type-is-diff-size/main.rs index f826ae92f3b7..f84cf1d8b9aa 100644 --- a/tests/expected/intrinsics/simd-cmp-result-type-is-diff-size/main.rs +++ b/tests/expected/intrinsics/simd-cmp-result-type-is-diff-size/main.rs @@ -3,7 +3,8 @@ //! Checks that storing the result of a vector operation in a vector of //! size different to the operands' sizes causes an error. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_eq; #[repr(simd)] #[allow(non_camel_case_types)] @@ -20,14 +21,6 @@ pub struct u64x2(u64, u64); #[derive(Clone, Copy, PartialEq, Eq)] pub struct u32x4(u32, u32, u32, u32); -// From : -// > The type checker ensures that `T` and `U` have the same length, and that -// > `U` is appropriately "boolean"-y. -// This means that `U` is allowed to be `i64` or `u64`, but not `f64`. -extern "platform-intrinsic" { - fn simd_eq(x: T, y: T) -> U; -} - #[kani::proof] fn main() { let x = u64x2(0, 0); diff --git a/tests/expected/intrinsics/simd-div-div-zero/main.rs b/tests/expected/intrinsics/simd-div-div-zero/main.rs index 4f613e362947..148ae62a252c 100644 --- a/tests/expected/intrinsics/simd-div-div-zero/main.rs +++ b/tests/expected/intrinsics/simd-div-div-zero/main.rs @@ -2,17 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Checks that `simd_div` triggers a failure when the divisor is zero. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_div; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct i32x2(i32, i32); -extern "platform-intrinsic" { - fn simd_div(x: T, y: T) -> T; -} - #[kani::proof] fn test_simd_div() { let dividend = kani::any(); diff --git a/tests/expected/intrinsics/simd-div-rem-overflow/main.rs b/tests/expected/intrinsics/simd-div-rem-overflow/main.rs index e7f2e9fbc48f..5f49e7db6154 100644 --- a/tests/expected/intrinsics/simd-div-rem-overflow/main.rs +++ b/tests/expected/intrinsics/simd-div-rem-overflow/main.rs @@ -1,20 +1,15 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// Checks that the `simd_div` and `simd_rem` intrinsics check for integer overflows. - -#![feature(repr_simd, platform_intrinsics)] +//! Checks that the `simd_div` and `simd_rem` intrinsics check for integer overflows. +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::{simd_div, simd_rem}; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct i32x2(i32, i32); -extern "platform-intrinsic" { - fn simd_div(x: T, y: T) -> T; - fn simd_rem(x: T, y: T) -> T; -} - unsafe fn do_simd_div(dividends: i32x2, divisors: i32x2) -> i32x2 { simd_div(dividends, divisors) } diff --git a/tests/expected/intrinsics/simd-extract-wrong-type/main.rs b/tests/expected/intrinsics/simd-extract-wrong-type/main.rs index 5d32f1ed9de8..b8fb5a3ffc6f 100644 --- a/tests/expected/intrinsics/simd-extract-wrong-type/main.rs +++ b/tests/expected/intrinsics/simd-extract-wrong-type/main.rs @@ -4,17 +4,14 @@ //! This test checks that we emit an error when the return type for //! `simd_extract` has a type different to the first argument's (i.e., the //! vector) base type. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_extract; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct i64x2(i64, i64); -extern "platform-intrinsic" { - fn simd_extract(x: T, idx: u32) -> U; -} - #[kani::proof] fn main() { let y = i64x2(0, 1); diff --git a/tests/expected/intrinsics/simd-insert-wrong-type/main.rs b/tests/expected/intrinsics/simd-insert-wrong-type/main.rs index ec2edfc110dd..6c4946a051b6 100644 --- a/tests/expected/intrinsics/simd-insert-wrong-type/main.rs +++ b/tests/expected/intrinsics/simd-insert-wrong-type/main.rs @@ -4,17 +4,14 @@ //! This test checks that we emit an error when the third argument for //! `simd_insert` (the value to be inserted) has a type different to the first //! argument's (i.e., the vector) base type. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_insert; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct i64x2(i64, i64); -extern "platform-intrinsic" { - fn simd_insert(x: T, idx: u32, b: U) -> T; -} - #[kani::proof] fn main() { let y = i64x2(0, 1); diff --git a/tests/expected/intrinsics/simd-rem-div-zero/main.rs b/tests/expected/intrinsics/simd-rem-div-zero/main.rs index 715de058154b..4393808ac039 100644 --- a/tests/expected/intrinsics/simd-rem-div-zero/main.rs +++ b/tests/expected/intrinsics/simd-rem-div-zero/main.rs @@ -2,17 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Checks that `simd_rem` triggers a failure when the divisor is zero -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_rem; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct i32x2(i32, i32); -extern "platform-intrinsic" { - fn simd_rem(x: T, y: T) -> T; -} - #[kani::proof] fn test_simd_rem() { let dividend = kani::any(); diff --git a/tests/expected/intrinsics/simd-result-type-is-float/main.rs b/tests/expected/intrinsics/simd-result-type-is-float/main.rs index 18a2152b93c5..01286de9cdd8 100644 --- a/tests/expected/intrinsics/simd-result-type-is-float/main.rs +++ b/tests/expected/intrinsics/simd-result-type-is-float/main.rs @@ -3,7 +3,8 @@ //! Checks that storing the result of a vector comparison in a vector of floats //! causes an error. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_eq; #[repr(simd)] #[allow(non_camel_case_types)] @@ -25,14 +26,6 @@ pub struct u32x4(u32, u32, u32, u32); #[derive(Clone, Copy, PartialEq)] pub struct f32x2(f32, f32); -// From : -// > The type checker ensures that `T` and `U` have the same length, and that -// > `U` is appropriately "boolean"-y. -// This means that `U` is allowed to be `i64` or `u64`, but not `f64`. -extern "platform-intrinsic" { - fn simd_eq(x: T, y: T) -> U; -} - #[kani::proof] fn main() { let x = u64x2(0, 0); diff --git a/tests/expected/intrinsics/simd-shl-shift-negative/main.rs b/tests/expected/intrinsics/simd-shl-shift-negative/main.rs index 0c7116b30567..2b7b2a418e19 100644 --- a/tests/expected/intrinsics/simd-shl-shift-negative/main.rs +++ b/tests/expected/intrinsics/simd-shl-shift-negative/main.rs @@ -2,17 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Checks that `simd_shl` returns a failure if the shift distance is negative. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_shl; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct i32x2(i32, i32); -extern "platform-intrinsic" { - fn simd_shl(x: T, y: T) -> T; -} - #[kani::proof] fn test_simd_shl() { let value = kani::any(); diff --git a/tests/expected/intrinsics/simd-shl-shift-too-large/main.rs b/tests/expected/intrinsics/simd-shl-shift-too-large/main.rs index fff9aadf1900..dada7a5a8b84 100644 --- a/tests/expected/intrinsics/simd-shl-shift-too-large/main.rs +++ b/tests/expected/intrinsics/simd-shl-shift-too-large/main.rs @@ -2,17 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Checks that `simd_shl` returns a failure if the shift distance is too large. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_shl; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct i32x2(i32, i32); -extern "platform-intrinsic" { - fn simd_shl(x: T, y: T) -> T; -} - #[kani::proof] fn test_simd_shl() { let value = kani::any(); diff --git a/tests/expected/intrinsics/simd-shr-shift-negative/main.rs b/tests/expected/intrinsics/simd-shr-shift-negative/main.rs index 580f0337db25..dc38955099a2 100644 --- a/tests/expected/intrinsics/simd-shr-shift-negative/main.rs +++ b/tests/expected/intrinsics/simd-shr-shift-negative/main.rs @@ -2,17 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Checks that `simd_shr` returns a failure if the shift distance is negative. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_shr; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct i32x2(i32, i32); -extern "platform-intrinsic" { - fn simd_shr(x: T, y: T) -> T; -} - #[kani::proof] fn test_simd_shr() { let value = kani::any(); diff --git a/tests/expected/intrinsics/simd-shr-shift-too-large/main.rs b/tests/expected/intrinsics/simd-shr-shift-too-large/main.rs index 3d9cd5e0c919..70ae0ad0da45 100644 --- a/tests/expected/intrinsics/simd-shr-shift-too-large/main.rs +++ b/tests/expected/intrinsics/simd-shr-shift-too-large/main.rs @@ -2,17 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Checks that `simd_shr` returns a failure if the shift distance is too large. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_shr; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct i32x2(i32, i32); -extern "platform-intrinsic" { - fn simd_shr(x: T, y: T) -> T; -} - #[kani::proof] fn test_simd_shr() { let value = kani::any(); diff --git a/tests/expected/intrinsics/simd-shuffle-indexes-out/main.rs b/tests/expected/intrinsics/simd-shuffle-indexes-out/main.rs index d8a4c22de85f..0f7c42d2d46c 100644 --- a/tests/expected/intrinsics/simd-shuffle-indexes-out/main.rs +++ b/tests/expected/intrinsics/simd-shuffle-indexes-out/main.rs @@ -3,17 +3,14 @@ //! Checks that `simd_shuffle` triggers an out-of-bounds failure when any of the //! indexes supplied is greater than the combined size of the input vectors. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_shuffle; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct i64x2(i64, i64); -extern "platform-intrinsic" { - fn simd_shuffle(x: T, y: T, idx: U) -> V; -} - #[kani::proof] fn main() { let y = i64x2(0, 1); diff --git a/tests/expected/intrinsics/simd-shuffle-result-type-is-diff-size/main.rs b/tests/expected/intrinsics/simd-shuffle-result-type-is-diff-size/main.rs index 25796f6c22e7..6345f5516101 100644 --- a/tests/expected/intrinsics/simd-shuffle-result-type-is-diff-size/main.rs +++ b/tests/expected/intrinsics/simd-shuffle-result-type-is-diff-size/main.rs @@ -3,7 +3,8 @@ //! Checks that Kani triggers an error when the result type doesn't have the //! length expected from a `simd_shuffle` call. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_shuffle; #[repr(simd)] #[allow(non_camel_case_types)] @@ -15,10 +16,6 @@ pub struct i64x2(i64, i64); #[derive(Clone, Copy, PartialEq, Eq)] pub struct i64x4(i64, i64, i64, i64); -extern "platform-intrinsic" { - fn simd_shuffle(x: T, y: T, idx: I) -> U; -} - #[kani::proof] fn main() { let y = i64x2(0, 1); diff --git a/tests/expected/intrinsics/simd-shuffle-result-type-is-diff-type/main.rs b/tests/expected/intrinsics/simd-shuffle-result-type-is-diff-type/main.rs index 6bdadae159f8..81f176700152 100644 --- a/tests/expected/intrinsics/simd-shuffle-result-type-is-diff-type/main.rs +++ b/tests/expected/intrinsics/simd-shuffle-result-type-is-diff-type/main.rs @@ -3,7 +3,8 @@ //! Checks that Kani triggers an error when the result type doesn't have the //! subtype expected from a `simd_shuffle` call. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_shuffle; #[repr(simd)] #[allow(non_camel_case_types)] @@ -15,10 +16,6 @@ pub struct i64x2(i64, i64); #[derive(Clone, Copy, PartialEq)] pub struct f64x2(f64, f64); -extern "platform-intrinsic" { - fn simd_shuffle(x: T, y: T, idx: I) -> U; -} - #[kani::proof] fn main() { let y = i64x2(0, 1); diff --git a/tests/kani/Intrinsics/SIMD/Compare/float.rs b/tests/kani/Intrinsics/SIMD/Compare/float.rs index 6d2113beb3a0..cc5765ef226b 100644 --- a/tests/kani/Intrinsics/SIMD/Compare/float.rs +++ b/tests/kani/Intrinsics/SIMD/Compare/float.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Checks that intrinsics for SIMD vectors of signed integers are supported -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::*; #[repr(simd)] #[allow(non_camel_case_types)] @@ -19,17 +20,6 @@ pub struct i64x2(i64, i64); #[derive(Clone, Copy, PartialEq)] pub struct i32x2(i32, i32); -// The predicate type U in the functions below must -// be a SIMD type, otherwise we get a compilation error -extern "platform-intrinsic" { - fn simd_eq(x: T, y: T) -> U; - fn simd_ne(x: T, y: T) -> U; - fn simd_lt(x: T, y: T) -> U; - fn simd_le(x: T, y: T) -> U; - fn simd_gt(x: T, y: T) -> U; - fn simd_ge(x: T, y: T) -> U; -} - macro_rules! assert_cmp { ($res_cmp: ident, $simd_cmp: ident, $x: expr, $y: expr, $($res: expr),+) => { let $res_cmp: i32x2 = $simd_cmp($x, $y); diff --git a/tests/kani/Intrinsics/SIMD/Compare/result_type_is_same_size.rs b/tests/kani/Intrinsics/SIMD/Compare/result_type_is_same_size.rs index cc38ad81864d..d3582057fd00 100644 --- a/tests/kani/Intrinsics/SIMD/Compare/result_type_is_same_size.rs +++ b/tests/kani/Intrinsics/SIMD/Compare/result_type_is_same_size.rs @@ -3,7 +3,8 @@ //! Checks that storing the result of a vector operation in a vector of //! size equal to the operands' sizes works. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_eq; #[repr(simd)] #[allow(non_camel_case_types)] @@ -20,14 +21,6 @@ pub struct u64x2(u64, u64); #[derive(Clone, Copy, PartialEq, Eq)] pub struct u32x2(u32, u32); -// From : -// > The type checker ensures that `T` and `U` have the same length, and that -// > `U` is appropriately "boolean"-y. -// This means that `U` is allowed to be `i64` or `u64`, but not `f64`. -extern "platform-intrinsic" { - fn simd_eq(x: T, y: T) -> U; -} - #[kani::proof] fn main() { let x = u64x2(0, 0); diff --git a/tests/kani/Intrinsics/SIMD/Compare/signed.rs b/tests/kani/Intrinsics/SIMD/Compare/signed.rs index be93395fd2d9..cfd781fa64c7 100644 --- a/tests/kani/Intrinsics/SIMD/Compare/signed.rs +++ b/tests/kani/Intrinsics/SIMD/Compare/signed.rs @@ -2,26 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Checks that intrinsics for SIMD vectors of signed integers are supported -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::*; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct i64x2(i64, i64); -// From : -// > The type checker ensures that `T` and `U` have the same length, and that -// > `U` is appropriately "boolean"-y. -// This means that `U` is allowed to be `i64` or `u64`, but not `f64`. -extern "platform-intrinsic" { - fn simd_eq(x: T, y: T) -> U; - fn simd_ne(x: T, y: T) -> U; - fn simd_lt(x: T, y: T) -> U; - fn simd_le(x: T, y: T) -> U; - fn simd_gt(x: T, y: T) -> U; - fn simd_ge(x: T, y: T) -> U; -} - macro_rules! assert_cmp { ($res_cmp: ident, $simd_cmp: ident, $x: expr, $y: expr, $($res: expr),+) => { let $res_cmp: i64x2 = $simd_cmp($x, $y); diff --git a/tests/kani/Intrinsics/SIMD/Compare/unsigned.rs b/tests/kani/Intrinsics/SIMD/Compare/unsigned.rs index d30640f123d2..ee39f750c8a2 100644 --- a/tests/kani/Intrinsics/SIMD/Compare/unsigned.rs +++ b/tests/kani/Intrinsics/SIMD/Compare/unsigned.rs @@ -2,26 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Checks that intrinsics for SIMD vectors of unsigned integers are supported -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::*; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct u64x2(u64, u64); -// From : -// > The type checker ensures that `T` and `U` have the same length, and that -// > `U` is appropriately "boolean"-y. -// This means that `U` is allowed to be `i64` or `u64`, but not `f64`. -extern "platform-intrinsic" { - fn simd_eq(x: T, y: T) -> U; - fn simd_ne(x: T, y: T) -> U; - fn simd_lt(x: T, y: T) -> U; - fn simd_le(x: T, y: T) -> U; - fn simd_gt(x: T, y: T) -> U; - fn simd_ge(x: T, y: T) -> U; -} - macro_rules! assert_cmp { ($res_cmp: ident, $simd_cmp: ident, $x: expr, $y: expr, $($res: expr),+) => { let $res_cmp: u64x2 = $simd_cmp($x, $y); diff --git a/tests/kani/Intrinsics/SIMD/Construction/main.rs b/tests/kani/Intrinsics/SIMD/Construction/main.rs index 6e9116e4dc15..5de3ae20d868 100644 --- a/tests/kani/Intrinsics/SIMD/Construction/main.rs +++ b/tests/kani/Intrinsics/SIMD/Construction/main.rs @@ -3,18 +3,14 @@ //! Checks that the `simd_extract` and `simd_insert` intrinsics are supported //! and return the expected results. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::{simd_extract, simd_insert}; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct i64x2(i64, i64); -extern "platform-intrinsic" { - fn simd_extract(x: T, idx: u32) -> U; - fn simd_insert(x: T, idx: u32, b: U) -> T; -} - #[kani::proof] fn main() { let y = i64x2(0, 1); diff --git a/tests/kani/Intrinsics/SIMD/Operators/arith.rs b/tests/kani/Intrinsics/SIMD/Operators/arith.rs index 6af147b826e0..d9f442a659ba 100644 --- a/tests/kani/Intrinsics/SIMD/Operators/arith.rs +++ b/tests/kani/Intrinsics/SIMD/Operators/arith.rs @@ -1,22 +1,16 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -//! This test doesn't work because support for SIMD intrinsics isn't available -//! at the moment in Kani. Support to be added in -//! -#![feature(repr_simd, platform_intrinsics)] +//! Checks that the SIMD intrinsics `simd_add`, `simd_sub` and +//! `simd_mul` are supported and return the expected results. +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::{simd_add, simd_mul, simd_sub}; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct i8x2(i8, i8); -extern "platform-intrinsic" { - fn simd_add(x: T, y: T) -> T; - fn simd_sub(x: T, y: T) -> T; - fn simd_mul(x: T, y: T) -> T; -} - macro_rules! verify_no_overflow { ($cf: ident, $uf: ident) => {{ let a: i8 = kani::any(); diff --git a/tests/kani/Intrinsics/SIMD/Operators/bitmask.rs b/tests/kani/Intrinsics/SIMD/Operators/bitmask.rs index 4d3293264b6e..6992408436b9 100644 --- a/tests/kani/Intrinsics/SIMD/Operators/bitmask.rs +++ b/tests/kani/Intrinsics/SIMD/Operators/bitmask.rs @@ -6,16 +6,11 @@ //! This is done by initializing vectors with the contents of 2-member tuples //! with symbolic values. The result of using each of the intrinsics is compared //! against the result of using the associated bitwise operator on the tuples. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] #![feature(generic_const_exprs)] #![feature(portable_simd)] -#![feature(core_intrinsics)] - use std::fmt::Debug; - -extern "platform-intrinsic" { - fn simd_bitmask(x: T) -> U; -} +use std::intrinsics::simd::simd_bitmask; #[repr(simd)] #[derive(Clone, Debug)] diff --git a/tests/kani/Intrinsics/SIMD/Operators/bitshift.rs b/tests/kani/Intrinsics/SIMD/Operators/bitshift.rs index 1041f918123f..fa2cd3c52cd6 100644 --- a/tests/kani/Intrinsics/SIMD/Operators/bitshift.rs +++ b/tests/kani/Intrinsics/SIMD/Operators/bitshift.rs @@ -3,7 +3,8 @@ //! Checks that the `simd_shl` and `simd_shr` intrinsics are supported and they //! return the expected results. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::{simd_shl, simd_shr}; #[repr(simd)] #[allow(non_camel_case_types)] @@ -15,11 +16,6 @@ pub struct i32x2(i32, i32); #[derive(Clone, Copy, PartialEq, Eq)] pub struct u32x2(u32, u32); -extern "platform-intrinsic" { - fn simd_shl(x: T, y: T) -> T; - fn simd_shr(x: T, y: T) -> T; -} - #[kani::proof] fn test_simd_shl() { let value = kani::any(); diff --git a/tests/kani/Intrinsics/SIMD/Operators/bitwise.rs b/tests/kani/Intrinsics/SIMD/Operators/bitwise.rs index aa53bb6ac9ea..b18410088f18 100644 --- a/tests/kani/Intrinsics/SIMD/Operators/bitwise.rs +++ b/tests/kani/Intrinsics/SIMD/Operators/bitwise.rs @@ -8,7 +8,8 @@ //! This is done by initializing vectors with the contents of 2-member tuples //! with symbolic values. The result of using each of the intrinsics is compared //! against the result of using the associated bitwise operator on the tuples. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::{simd_and, simd_or, simd_xor}; #[repr(simd)] #[allow(non_camel_case_types)] @@ -50,11 +51,6 @@ pub struct u32x2(u32, u32); #[derive(Clone, Copy, PartialEq, Eq)] pub struct u64x2(u64, u64); -extern "platform-intrinsic" { - fn simd_and(x: T, y: T) -> T; - fn simd_or(x: T, y: T) -> T; - fn simd_xor(x: T, y: T) -> T; -} macro_rules! compare_simd_op_with_normal_op { ($simd_op: ident, $normal_op: tt, $simd_type: ident) => { let tup_x: (_,_) = kani::any(); diff --git a/tests/kani/Intrinsics/SIMD/Operators/division.rs b/tests/kani/Intrinsics/SIMD/Operators/division.rs index 593ac3fdfe7b..e1e8dab39cca 100644 --- a/tests/kani/Intrinsics/SIMD/Operators/division.rs +++ b/tests/kani/Intrinsics/SIMD/Operators/division.rs @@ -3,18 +3,14 @@ //! Checks that the `simd_div` and `simd_rem` intrinsics are supported and they //! return the expected results. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::{simd_div, simd_rem}; #[repr(simd)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Eq)] pub struct i32x2(i32, i32); -extern "platform-intrinsic" { - fn simd_div(x: T, y: T) -> T; - fn simd_rem(x: T, y: T) -> T; -} - #[kani::proof] fn test_simd_div() { let dividend = kani::any(); diff --git a/tests/kani/Intrinsics/SIMD/Operators/division_float.rs b/tests/kani/Intrinsics/SIMD/Operators/division_float.rs index 97ea2e6e7ca7..711b67b87116 100644 --- a/tests/kani/Intrinsics/SIMD/Operators/division_float.rs +++ b/tests/kani/Intrinsics/SIMD/Operators/division_float.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Checks that the `simd_div` intrinsic returns the expected results for floating point numbers. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_div; #[repr(simd)] #[allow(non_camel_case_types)] @@ -19,10 +20,6 @@ impl f32x2 { } } -extern "platform-intrinsic" { - fn simd_div(x: T, y: T) -> T; -} - #[kani::proof] fn test_simd_div() { let dividends = f32x2::new_with(|| { diff --git a/tests/kani/Intrinsics/SIMD/Operators/remainder_float_fixme.rs b/tests/kani/Intrinsics/SIMD/Operators/remainder_float_fixme.rs index c97e96896bcf..e64498e20242 100644 --- a/tests/kani/Intrinsics/SIMD/Operators/remainder_float_fixme.rs +++ b/tests/kani/Intrinsics/SIMD/Operators/remainder_float_fixme.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Checks that the `simd_rem` intrinsic returns the expected results for floating point numbers. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_rem; #[repr(simd)] #[allow(non_camel_case_types)] @@ -19,10 +20,6 @@ impl f32x2 { } } -extern "platform-intrinsic" { - fn simd_rem(x: T, y: T) -> T; -} - #[kani::proof] fn test_simd_rem() { let dividends = f32x2::new_with(|| { diff --git a/tests/kani/Intrinsics/SIMD/Shuffle/main.rs b/tests/kani/Intrinsics/SIMD/Shuffle/main.rs index 6d822a18bdc1..9b6870e40004 100644 --- a/tests/kani/Intrinsics/SIMD/Shuffle/main.rs +++ b/tests/kani/Intrinsics/SIMD/Shuffle/main.rs @@ -3,7 +3,8 @@ //! Checks that `simd_shuffle` and `simd_shuffleN` (where `N` is a length) are //! supported and return the expected results. -#![feature(repr_simd, platform_intrinsics)] +#![feature(repr_simd, core_intrinsics)] +use std::intrinsics::simd::simd_shuffle; #[repr(simd)] #[allow(non_camel_case_types)] @@ -15,10 +16,6 @@ pub struct i64x2(i64, i64); #[derive(Clone, Copy, PartialEq, Eq)] pub struct i64x4(i64, i64, i64, i64); -extern "platform-intrinsic" { - fn simd_shuffle(x: T, y: T, idx: U) -> V; -} - #[kani::proof] fn main() { { diff --git a/tests/kani/SIMD/simd_float_ops_fixme.rs b/tests/kani/SIMD/simd_float_ops_fixme.rs index d258a2119eca..6b53d95d554a 100644 --- a/tests/kani/SIMD/simd_float_ops_fixme.rs +++ b/tests/kani/SIMD/simd_float_ops_fixme.rs @@ -4,14 +4,10 @@ //! Ensure we can handle SIMD defined in the standard library //! FIXME: #![allow(non_camel_case_types)] -#![feature(repr_simd, platform_intrinsics, portable_simd)] +#![feature(repr_simd, core_intrinsics, portable_simd)] +use std::intrinsics::simd::simd_add; use std::simd::f32x4; -extern "platform-intrinsic" { - fn simd_add(x: T, y: T) -> T; - fn simd_eq(x: T, y: T) -> U; -} - #[repr(simd)] #[derive(Clone, PartialEq, kani::Arbitrary)] pub struct f32x2(f32, f32); diff --git a/tools/bookrunner/librustdoc/doctest.rs b/tools/bookrunner/librustdoc/doctest.rs index cf0fdc106e38..8d4c8c5eda5c 100644 --- a/tools/bookrunner/librustdoc/doctest.rs +++ b/tools/bookrunner/librustdoc/doctest.rs @@ -65,6 +65,7 @@ pub fn make_test( // crate already is included. let result = rustc_driver::catch_fatal_errors(|| { rustc_span::create_session_if_not_set_then(edition, |_| { + use rustc_errors::emitter::stderr_destination; use rustc_errors::emitter::{Emitter, HumanEmitter}; use rustc_errors::DiagCtxt; use rustc_parse::maybe_new_parser_from_source_str; @@ -81,14 +82,14 @@ pub fn make_test( let fallback_bundle = rustc_errors::fallback_fluent_bundle(DEFAULT_LOCALE_RESOURCES.to_vec(), false); - supports_color = HumanEmitter::stderr(ColorConfig::Auto, fallback_bundle.clone()) - .diagnostic_width(Some(80)) - .supports_color(); + supports_color = + HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone()) + .diagnostic_width(Some(80)) + .supports_color(); let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); - // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser - let handler = DiagCtxt::with_emitter(Box::new(emitter)); + let handler = DiagCtxt::new(Box::new(emitter)); let sess = ParseSess::with_dcx(handler, sm); let mut found_main = false; From 45caad49ab8f8cec9c3a5b695261908ffec63f8d Mon Sep 17 00:00:00 2001 From: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> Date: Thu, 7 Mar 2024 13:15:51 -0500 Subject: [PATCH 025/225] Add `--use-local-toolchain` to Kani setup (#3056) Adds `--use-local-toolchain` to Kani's setup flow, which accepts a local toolchain and then uses that to finish the Kani setup. Some notes: 1. Why? This is mainly for installing GPG verified toolchains. 2. This is missing some cleanup and refactoring work, like ensuring that the user defined toolchain matches the one that Kani was built against etc. Marked as Todo, for later. Resolves [#3058](https://github.com/model-checking/kani/issues/3058) By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .gitignore | 1 + Cargo.lock | 76 ++++++++++- Cargo.toml | 1 + kani-compiler/build.rs | 2 + kani-driver/src/assess/scan.rs | 5 +- kani-driver/src/call_cargo.rs | 9 +- kani-driver/src/concrete_playback/playback.rs | 16 +-- kani-driver/src/session.rs | 22 +++ src/lib.rs | 129 +++++++++++++++--- src/setup.rs | 22 ++- 10 files changed, 236 insertions(+), 47 deletions(-) diff --git a/.gitignore b/.gitignore index a31c86965494..a4cfe4ff4e2b 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ desktop.ini Session.vim .cproject .idea +toolchains/ *.iml .vscode .project diff --git a/Cargo.lock b/Cargo.lock index ab853a65e544..3af5e6232a76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,6 +33,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anstream" version = "0.6.13" @@ -87,6 +96,17 @@ version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -125,7 +145,7 @@ version = "0.47.0" dependencies = [ "anyhow", "cargo_metadata", - "clap", + "clap 4.5.1", "which", ] @@ -167,6 +187,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim 0.8.0", + "textwrap", + "unicode-width", + "vec_map", +] + [[package]] name = "clap" version = "4.5.1" @@ -186,7 +221,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.0", ] [[package]] @@ -392,6 +427,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "home" version = "0.5.9" @@ -437,7 +481,7 @@ dependencies = [ name = "kani-compiler" version = "0.47.0" dependencies = [ - "clap", + "clap 4.5.1", "cprover_bindings", "home", "itertools", @@ -460,7 +504,7 @@ version = "0.47.0" dependencies = [ "anyhow", "cargo_metadata", - "clap", + "clap 4.5.1", "comfy-table", "console", "glob", @@ -487,6 +531,7 @@ name = "kani-verifier" version = "0.47.0" dependencies = [ "anyhow", + "clap 2.34.0", "home", "os_info", ] @@ -505,7 +550,7 @@ dependencies = [ name = "kani_metadata" version = "0.47.0" dependencies = [ - "clap", + "clap 4.5.1", "cprover_bindings", "serde", "strum 0.26.1", @@ -1050,6 +1095,12 @@ dependencies = [ "serde", ] +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "strsim" version = "0.11.0" @@ -1127,6 +1178,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.57" @@ -1305,6 +1365,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index b842bf8fbf0e..845751118363 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ include = ["/src", "/build.rs", "/rust-toolchain.toml", "/LICENSE-*", "/README.m [dependencies] anyhow = "1" home = "0.5" +clap = "2.33.3" os_info = { version = "3", default-features = false } [[bin]] diff --git a/kani-compiler/build.rs b/kani-compiler/build.rs index 37a9471a167d..497e9dfa13d2 100644 --- a/kani-compiler/build.rs +++ b/kani-compiler/build.rs @@ -20,6 +20,8 @@ macro_rules! path_str { /// kani-compiler with nightly only. We also link to the rustup rustc_driver library for now. pub fn main() { // Add rustup to the rpath in order to properly link with the correct rustc version. + + // This is for dev purposes only, if dev point/search toolchain in .rustup/toolchains/ let rustup_home = env::var("RUSTUP_HOME").unwrap(); let rustup_tc = env::var("RUSTUP_TOOLCHAIN").unwrap(); let rustup_lib = path_str!([&rustup_home, "toolchains", &rustup_tc, "lib"]); diff --git a/kani-driver/src/assess/scan.rs b/kani-driver/src/assess/scan.rs index 5e4dc81d7e2a..ef33f91eb522 100644 --- a/kani-driver/src/assess/scan.rs +++ b/kani-driver/src/assess/scan.rs @@ -4,12 +4,12 @@ use std::collections::HashSet; use std::path::Path; use std::path::PathBuf; -use std::process::Command; use std::time::Instant; use anyhow::Result; use cargo_metadata::Package; +use crate::session::setup_cargo_command; use crate::session::KaniSession; use super::metadata::AssessMetadata; @@ -168,7 +168,8 @@ fn invoke_assess( ) -> Result<()> { let dir = manifest.parent().expect("file not in a directory?"); let log = std::fs::File::create(logfile)?; - let mut cmd = Command::new("cargo"); + + let mut cmd = setup_cargo_command()?; cmd.arg("kani"); // Use of options before 'assess' subcommand is a hack, these should be factored out. // TODO: --only-codegen should be outright an option to assess. (perhaps tests too?) diff --git a/kani-driver/src/call_cargo.rs b/kani-driver/src/call_cargo.rs index 66166913c9cb..b68d18c84a40 100644 --- a/kani-driver/src/call_cargo.rs +++ b/kani-driver/src/call_cargo.rs @@ -4,8 +4,8 @@ use crate::args::VerificationArgs; use crate::call_single_file::to_rustc_arg; use crate::project::Artifact; -use crate::session::KaniSession; -use crate::{session, util}; +use crate::session::{setup_cargo_command, KaniSession}; +use crate::util; use anyhow::{bail, Context, Result}; use cargo_metadata::diagnostic::{Diagnostic, DiagnosticLevel}; use cargo_metadata::{Message, Metadata, MetadataCommand, Package, Target}; @@ -109,9 +109,8 @@ impl KaniSession { let mut failed_targets = vec![]; for package in packages { for verification_target in package_targets(&self.args, package) { - let mut cmd = Command::new("cargo"); - cmd.arg(session::toolchain_shorthand()) - .args(&cargo_args) + let mut cmd = setup_cargo_command()?; + cmd.args(&cargo_args) .args(vec!["-p", &package.name]) .args(&verification_target.to_args()) .args(&pkg_args) diff --git a/kani-driver/src/concrete_playback/playback.rs b/kani-driver/src/concrete_playback/playback.rs index 96ed30da904d..d8c1762da92b 100644 --- a/kani-driver/src/concrete_playback/playback.rs +++ b/kani-driver/src/concrete_playback/playback.rs @@ -7,7 +7,7 @@ use crate::args::common::Verbosity; use crate::args::playback_args::{CargoPlaybackArgs, KaniPlaybackArgs, MessageFormat}; use crate::call_cargo::cargo_config_args; use crate::call_single_file::base_rustc_flags; -use crate::session::{lib_playback_folder, InstallType}; +use crate::session::{lib_playback_folder, setup_cargo_command, InstallType}; use crate::{session, util}; use anyhow::Result; use std::ffi::OsString; @@ -17,8 +17,7 @@ use std::process::Command; use tracing::debug; pub fn playback_cargo(args: CargoPlaybackArgs) -> Result<()> { - let install = InstallType::new()?; - cargo_test(&install, args) + cargo_test(args) } pub fn playback_standalone(args: KaniPlaybackArgs) -> Result<()> { @@ -93,9 +92,10 @@ fn build_test(install: &InstallType, args: &KaniPlaybackArgs) -> Result } /// Invokes cargo test using Kani compiler and the provided arguments. -/// TODO: This should likely be inside KaniSession, but KaniSession requires `VerificationArgs` today. -/// For now, we just use InstallType directly. -fn cargo_test(install: &InstallType, args: CargoPlaybackArgs) -> Result<()> { +fn cargo_test(args: CargoPlaybackArgs) -> Result<()> { + let install = InstallType::new()?; + let mut cmd = setup_cargo_command()?; + let rustc_args = base_rustc_flags(lib_playback_folder()?); let mut cargo_args: Vec = vec!["test".into()]; @@ -123,9 +123,7 @@ fn cargo_test(install: &InstallType, args: CargoPlaybackArgs) -> Result<()> { } // Arguments that will only be passed to the target package. - let mut cmd = Command::new("cargo"); - cmd.arg(session::toolchain_shorthand()) - .args(&cargo_args) + cmd.args(&cargo_args) .env("RUSTC", &install.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/session.rs b/kani-driver/src/session.rs index 5b9b90854789..8cf4a20450f5 100644 --- a/kani-driver/src/session.rs +++ b/kani-driver/src/session.rs @@ -379,3 +379,25 @@ fn init_logger(args: &VerificationArgs) { ); tracing::subscriber::set_global_default(subscriber).unwrap(); } + +// Setup the default version of cargo being run, based on the type/mode of installation for kani +// If kani is being run in developer mode, then we use the one provided by rustup as we can assume that the developer will have rustup installed +// For release versions of Kani, we use a version of cargo that's in the toolchain that's been symlinked during `cargo-kani` setup. This will allow +// Kani to remove the runtime dependency on rustup later on. +pub fn setup_cargo_command() -> Result { + let install_type = InstallType::new()?; + + let cmd = match install_type { + InstallType::DevRepo(_) => { + let mut cmd = Command::new("cargo"); + cmd.arg(self::toolchain_shorthand()); + cmd + } + InstallType::Release(kani_dir) => { + let cargo_path = kani_dir.join("toolchain").join("bin").join("cargo"); + Command::new(cargo_path) + } + }; + + Ok(cmd) +} diff --git a/src/lib.rs b/src/lib.rs index 9a1ac90f5dda..202d46f0e5ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,7 @@ mod cmd; mod os_hacks; mod setup; +use clap::{App, Arg, SubCommand}; use std::ffi::OsString; use std::os::unix::prelude::CommandExt; use std::path::{Path, PathBuf}; @@ -28,16 +29,18 @@ use anyhow::{bail, Context, Result}; /// `bin` should be either `kani` or `cargo-kani` pub fn proxy(bin: &str) -> Result<()> { match parse_args(env::args_os().collect()) { - ArgsResult::ExplicitSetup { use_local_bundle } => setup::setup(use_local_bundle), + ArgsResult::ExplicitSetup { use_local_bundle, use_local_toolchain } => { + setup::setup(use_local_bundle, use_local_toolchain) + } ArgsResult::Default => { fail_if_in_dev_environment()?; if !setup::appears_setup() { - setup::setup(None)?; + setup::setup(None, None)?; } else { // This handles cases where the setup was left incomplete due to an interrupt // For example - https://github.com/model-checking/kani/issues/1545 if let Some(path_to_bundle) = setup::appears_incomplete() { - setup::setup(Some(path_to_bundle.clone().into_os_string()))?; + setup::setup(Some(path_to_bundle.clone().into_os_string()), None)?; // Suppress warning with unused assignment // and remove the bundle if it still exists let _ = fs::remove_file(path_to_bundle); @@ -51,28 +54,56 @@ pub fn proxy(bin: &str) -> Result<()> { /// Minimalist argument parsing result type #[derive(PartialEq, Eq, Debug)] enum ArgsResult { - ExplicitSetup { use_local_bundle: Option }, + ExplicitSetup { use_local_bundle: Option, use_local_toolchain: Option }, Default, } /// Parse `args` and decide what to do. fn parse_args(args: Vec) -> ArgsResult { - // In an effort to keep our dependencies minimal, we do the bare minimum argument parsing manually. - // `args_ez` makes it easy to do crude arg parsing with match. - let args_ez: Vec> = args.iter().map(|x| x.to_str()).collect(); - // "cargo kani setup" comes in as "cargo-kani kani setup" - // "cargo-kani setup" comes in as "cargo-kani setup" - match &args_ez[..] { - &[_, Some("setup"), Some("--use-local-bundle"), _] => { - ArgsResult::ExplicitSetup { use_local_bundle: Some(args[3].clone()) } - } - &[_, Some("kani"), Some("setup"), Some("--use-local-bundle"), _] => { - ArgsResult::ExplicitSetup { use_local_bundle: Some(args[4].clone()) } - } - &[_, Some("setup")] | &[_, Some("kani"), Some("setup")] => { - ArgsResult::ExplicitSetup { use_local_bundle: None } + let matches = App::new("kani") + .subcommand( + SubCommand::with_name("setup") + .arg(Arg::with_name("use-local-bundle").long("use-local-bundle").takes_value(true)) + .arg( + Arg::with_name("use-local-toolchain") + .long("use-local-toolchain") + .takes_value(true), + ), + ) + .subcommand( + SubCommand::with_name("kani").subcommand( + SubCommand::with_name("setup") + .arg( + Arg::with_name("use-local-bundle") + .long("use-local-bundle") + .takes_value(true), + ) + .arg( + Arg::with_name("use-local-toolchain") + .long("use-local-toolchain") + .takes_value(true), + ), + ), + ) + .get_matches_from(args); + + // Default is the behaviour for Kani when cargo-kani/ cargo kani runs on its own and sees that there is no setup done yet + // Explicit is when the user uses the sub-command cargo-kani setup explicitly + if let Some(matches) = matches.subcommand_matches("setup") { + let use_local_toolchain = matches.value_of_os("use-local-toolchain").map(OsString::from); + let use_local_bundle = matches.value_of_os("use-local-bundle").map(OsString::from); + ArgsResult::ExplicitSetup { use_local_bundle, use_local_toolchain } + } else if let Some(matches) = matches.subcommand_matches("kani") { + if let Some(matches) = matches.subcommand_matches("setup") { + let use_local_toolchain = + matches.value_of_os("use-local-toolchain").map(OsString::from); + let use_local_bundle = matches.value_of_os("use-local-bundle").map(OsString::from); + ArgsResult::ExplicitSetup { use_local_bundle, use_local_toolchain } + } else { + ArgsResult::Default } - _ => ArgsResult::Default, + } else { + ArgsResult::Default } } @@ -223,16 +254,72 @@ mod tests { assert_eq!(e, trial(&[])); } { - let e = ArgsResult::ExplicitSetup { use_local_bundle: None }; + let e = ArgsResult::ExplicitSetup { use_local_bundle: None, use_local_toolchain: None }; assert_eq!(e, trial(&["cargo-kani", "kani", "setup"])); assert_eq!(e, trial(&["cargo", "kani", "setup"])); assert_eq!(e, trial(&["cargo-kani", "setup"])); } { - let e = ArgsResult::ExplicitSetup { use_local_bundle: Some(OsString::from("FILE")) }; + let e = ArgsResult::ExplicitSetup { + use_local_bundle: Some(OsString::from("FILE")), + use_local_toolchain: None, + }; assert_eq!(e, trial(&["cargo-kani", "kani", "setup", "--use-local-bundle", "FILE"])); assert_eq!(e, trial(&["cargo", "kani", "setup", "--use-local-bundle", "FILE"])); assert_eq!(e, trial(&["cargo-kani", "setup", "--use-local-bundle", "FILE"])); } + { + let e = ArgsResult::ExplicitSetup { + use_local_bundle: None, + use_local_toolchain: Some(OsString::from("TOOLCHAIN")), + }; + assert_eq!( + e, + trial(&["cargo-kani", "kani", "setup", "--use-local-toolchain", "TOOLCHAIN"]) + ); + assert_eq!(e, trial(&["cargo", "kani", "setup", "--use-local-toolchain", "TOOLCHAIN"])); + assert_eq!(e, trial(&["cargo-kani", "setup", "--use-local-toolchain", "TOOLCHAIN"])); + } + { + let e = ArgsResult::ExplicitSetup { + use_local_bundle: Some(OsString::from("FILE")), + use_local_toolchain: Some(OsString::from("TOOLCHAIN")), + }; + assert_eq!( + e, + trial(&[ + "cargo-kani", + "kani", + "setup", + "--use-local-bundle", + "FILE", + "--use-local-toolchain", + "TOOLCHAIN" + ]) + ); + assert_eq!( + e, + trial(&[ + "cargo", + "kani", + "setup", + "--use-local-bundle", + "FILE", + "--use-local-toolchain", + "TOOLCHAIN" + ]) + ); + assert_eq!( + e, + trial(&[ + "cargo-kani", + "setup", + "--use-local-bundle", + "FILE", + "--use-local-toolchain", + "TOOLCHAIN" + ]) + ); + } } } diff --git a/src/setup.rs b/src/setup.rs index ffdcf340e336..050a8e166334 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -70,7 +70,10 @@ pub fn appears_incomplete() -> Option { } /// Sets up Kani by unpacking/installing to `~/.kani/kani-VERSION` -pub fn setup(use_local_bundle: Option) -> Result<()> { +pub fn setup( + use_local_bundle: Option, + use_local_toolchain: Option, +) -> Result<()> { let kani_dir = kani_dir()?; let os = os_info::get(); @@ -81,7 +84,7 @@ pub fn setup(use_local_bundle: Option) -> Result<()> { setup_kani_bundle(&kani_dir, use_local_bundle)?; - setup_rust_toolchain(&kani_dir)?; + setup_rust_toolchain(&kani_dir, use_local_toolchain)?; setup_python_deps(&kani_dir)?; @@ -139,14 +142,23 @@ pub(crate) fn get_rust_toolchain_version(kani_dir: &Path) -> Result { } /// Install the Rust toolchain version we require -fn setup_rust_toolchain(kani_dir: &Path) -> Result { +fn setup_rust_toolchain(kani_dir: &Path, use_local_toolchain: Option) -> Result { // Currently this means we require the bundle to have been unpacked first! let toolchain_version = get_rust_toolchain_version(kani_dir)?; println!("[3/5] Installing rust toolchain version: {}", &toolchain_version); - Command::new("rustup").args(["toolchain", "install", &toolchain_version]).run()?; - let toolchain = home::rustup_home()?.join("toolchains").join(&toolchain_version); + // Symlink to a local toolchain if the user explicitly requests + if let Some(local_toolchain_path) = use_local_toolchain { + let toolchain_path = Path::new(&local_toolchain_path); + // TODO: match the version against which kani was built + // Issue: https://github.com/model-checking/kani/issues/3060 + symlink_rust_toolchain(toolchain_path, kani_dir)?; + return Ok(toolchain_version); + } + // This is the default behaviour when no explicit path to a toolchain is mentioned + Command::new("rustup").args(["toolchain", "install", &toolchain_version]).run()?; + let toolchain = home::rustup_home()?.join("toolchains").join(&toolchain_version); symlink_rust_toolchain(&toolchain, kani_dir)?; Ok(toolchain_version) } From 2f3cc47eba7d27754e36e31e69c5820a7ca08158 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Fri, 8 Mar 2024 12:39:31 -0800 Subject: [PATCH 026/225] Replace internal reverse_postorder by a stable one (#3064) The `reverse_postorder` function used before is internal to the compiler and reflects the internal body representation. Besides that, I would like to introduce transformation on the top of SMIR body, which will break the 1:1 relationship between basic blocks from internal body and monomorphic body from StableMIR. --- .../codegen_cprover_gotoc/codegen/block.rs | 32 ++++++++++++++++++- .../codegen_cprover_gotoc/codegen/function.rs | 6 ++-- .../context/current_fn.rs | 9 ------ 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs index 692fdb9c385b..5fe28097a2e0 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/block.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use crate::codegen_cprover_gotoc::GotocCtx; -use stable_mir::mir::{BasicBlock, BasicBlockIdx}; +use stable_mir::mir::{BasicBlock, BasicBlockIdx, Body}; +use std::collections::HashSet; use tracing::debug; pub fn bb_label(bb: BasicBlockIdx) -> String { @@ -72,3 +73,32 @@ impl<'tcx> GotocCtx<'tcx> { } } } + +/// Iterate over the basic blocks in reverse post-order. +/// +/// The `reverse_postorder` function used before was internal to the compiler and reflected the +/// internal body representation. +/// +/// As we introduce transformations on the top of SMIR body, there will be not guarantee of a +/// 1:1 relationship between basic blocks from internal body and monomorphic body from StableMIR. +pub fn reverse_postorder(body: &Body) -> impl Iterator { + postorder(body, 0, &mut HashSet::with_capacity(body.blocks.len())).into_iter().rev() +} + +fn postorder( + body: &Body, + bb: BasicBlockIdx, + visited: &mut HashSet, +) -> Vec { + if visited.contains(&bb) { + return vec![]; + } + visited.insert(bb); + + let mut result = vec![]; + for succ in body.blocks[bb].terminator.successors() { + result.append(&mut postorder(body, succ, visited)); + } + result.push(bb); + result +} diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs index 78c7e2b6cbd6..7cf4b979f407 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs @@ -3,10 +3,10 @@ //! This file contains functions related to codegenning MIR functions into gotoc +use crate::codegen_cprover_gotoc::codegen::block::reverse_postorder; use crate::codegen_cprover_gotoc::GotocCtx; use cbmc::goto_program::{Expr, Stmt, Symbol}; use cbmc::InternString; -use rustc_middle::mir::traversal::reverse_postorder; use stable_mir::mir::mono::Instance; use stable_mir::mir::{Body, Local}; use stable_mir::ty::{RigidTy, TyKind}; @@ -67,9 +67,7 @@ impl<'tcx> GotocCtx<'tcx> { self.codegen_declare_variables(&body); // Get the order from internal body for now. - let internal_body = self.current_fn().body_internal(); - reverse_postorder(internal_body) - .for_each(|(bb, _)| self.codegen_block(bb.index(), &body.blocks[bb.index()])); + reverse_postorder(&body).for_each(|bb| self.codegen_block(bb, &body.blocks[bb])); let loc = self.codegen_span_stable(instance.def.span()); let stmts = self.current_fn_mut().extract_block(); diff --git a/kani-compiler/src/codegen_cprover_gotoc/context/current_fn.rs b/kani-compiler/src/codegen_cprover_gotoc/context/current_fn.rs index adac294e30eb..5a542ca07cd2 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/current_fn.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/current_fn.rs @@ -4,7 +4,6 @@ use crate::codegen_cprover_gotoc::GotocCtx; use cbmc::goto_program::Stmt; use cbmc::InternedString; -use rustc_middle::mir::Body as BodyInternal; use rustc_middle::ty::Instance as InstanceInternal; use rustc_smir::rustc_internal; use stable_mir::mir::mono::Instance; @@ -21,8 +20,6 @@ pub struct CurrentFnCtx<'tcx> { instance: Instance, /// The crate this function is from krate: String, - /// The MIR for the current instance. This is using the internal representation. - mir: &'tcx BodyInternal<'tcx>, /// The current instance. This is using the internal representation. instance_internal: InstanceInternal<'tcx>, /// A list of local declarations used to retrieve MIR component types. @@ -53,7 +50,6 @@ impl<'tcx> CurrentFnCtx<'tcx> { Self { block: vec![], instance, - mir: gcx.tcx.instance_mir(instance_internal.def), instance_internal, krate: instance.def.krate().name, locals, @@ -94,11 +90,6 @@ impl<'tcx> CurrentFnCtx<'tcx> { self.instance } - /// The internal MIR for the function we are currently compiling using internal APIs. - pub fn body_internal(&self) -> &'tcx BodyInternal<'tcx> { - self.mir - } - /// The name of the function we are currently compiling pub fn name(&self) -> String { self.name.clone() From ea710b36c7ef81b038df4c1a9ef3c18d2f6ab0ef Mon Sep 17 00:00:00 2001 From: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Date: Fri, 8 Mar 2024 16:42:19 -0500 Subject: [PATCH 027/225] Add option to override `--crate-name` from `kani` (#3054) Adds a hidden `--crate-name` option to standalone Kani (i.e., `kani`) only. This option allows users to override the crate name used during the compilation of single-file Rust programs, making it easier to apply Kani to non-Cargo projects (see #3046 for more details). Resolves #3046 --- kani-driver/src/args/mod.rs | 3 ++ kani-driver/src/main.rs | 2 +- kani-driver/src/project.rs | 27 +++++++--- kani-driver/src/util.rs | 20 ------- .../script-based-pre/crate-name/a/src/lib.rs | 6 +++ .../script-based-pre/crate-name/b/src/lib.rs | 7 +++ .../script-based-pre/crate-name/c/src/lib.rs | 8 +++ tests/script-based-pre/crate-name/config.yml | 4 ++ .../crate-name/crate-name.expected | 1 + .../script-based-pre/crate-name/crate-name.sh | 54 +++++++++++++++++++ tests/script-based-pre/crate-name/my-code.rs | 6 +++ 11 files changed, 111 insertions(+), 27 deletions(-) create mode 100644 tests/script-based-pre/crate-name/a/src/lib.rs create mode 100644 tests/script-based-pre/crate-name/b/src/lib.rs create mode 100644 tests/script-based-pre/crate-name/c/src/lib.rs create mode 100644 tests/script-based-pre/crate-name/config.yml create mode 100644 tests/script-based-pre/crate-name/crate-name.expected create mode 100755 tests/script-based-pre/crate-name/crate-name.sh create mode 100644 tests/script-based-pre/crate-name/my-code.rs diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index 39b3c1fd025f..aeda94168840 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -79,6 +79,9 @@ pub struct StandaloneArgs { #[command(subcommand)] pub command: Option, + + #[arg(long, hide = true)] + pub crate_name: Option, } /// Kani takes optional subcommands to request specialized behavior. diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 009f25b37b5a..798625560970 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -104,7 +104,7 @@ fn standalone_main() -> Result<()> { print_kani_version(InvocationType::Standalone); } - let project = project::standalone_project(&args.input.unwrap(), &session)?; + let project = project::standalone_project(&args.input.unwrap(), args.crate_name, &session)?; if session.args.only_codegen { Ok(()) } else { verify_project(project, session) } } diff --git a/kani-driver/src/project.rs b/kani-driver/src/project.rs index b99a7e9aff07..fcf3805ab3df 100644 --- a/kani-driver/src/project.rs +++ b/kani-driver/src/project.rs @@ -18,7 +18,7 @@ use crate::metadata::{from_json, merge_kani_metadata, mock_proof_harness}; use crate::session::KaniSession; -use crate::util::{crate_name, guess_rlib_name}; +use crate::util::crate_name; use anyhow::{Context, Result}; use kani_metadata::{ artifact::convert_type, ArtifactType, ArtifactType::*, HarnessMetadata, KaniMetadata, @@ -270,8 +270,12 @@ pub fn cargo_project(session: &KaniSession, keep_going: bool) -> Result } /// Generate a project directly using `kani-compiler` on a single crate. -pub fn standalone_project(input: &Path, session: &KaniSession) -> Result { - StandaloneProjectBuilder::try_new(input, session)?.build() +pub fn standalone_project( + input: &Path, + crate_name: Option, + session: &KaniSession, +) -> Result { + StandaloneProjectBuilder::try_new(input, crate_name, session)?.build() } /// Builder for a standalone project. @@ -291,7 +295,7 @@ struct StandaloneProjectBuilder<'a> { impl<'a> StandaloneProjectBuilder<'a> { /// Create a `StandaloneProjectBuilder` from the given input and session. /// This will perform a few validations before the build. - fn try_new(input: &Path, session: &'a KaniSession) -> Result { + fn try_new(input: &Path, krate_name: Option, session: &'a KaniSession) -> Result { // Ensure the directory exist and it's in its canonical form. let outdir = if let Some(target_dir) = &session.args.target_dir { std::fs::create_dir_all(target_dir)?; // This is a no-op if directory exists. @@ -299,7 +303,7 @@ impl<'a> StandaloneProjectBuilder<'a> { } else { input.canonicalize().unwrap().parent().unwrap().to_path_buf() }; - let crate_name = 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, @@ -313,7 +317,7 @@ impl<'a> StandaloneProjectBuilder<'a> { /// Build a project by compiling `self.input` file. fn build(self) -> Result { // Register artifacts that may be generated by the compiler / linker for future deletion. - let rlib_path = guess_rlib_name(&self.outdir.join(self.input.file_name().unwrap())); + let rlib_path = self.rlib_name(); self.session.record_temporary_file(&rlib_path); self.session.record_temporary_file(&self.metadata.path); @@ -339,6 +343,17 @@ impl<'a> StandaloneProjectBuilder<'a> { } result } + + /// Build the rlib name from the crate name. + /// This is only used by 'kani', never 'cargo-kani', so we hopefully don't have too many corner + /// cases to deal with. + fn rlib_name(&self) -> PathBuf { + let path = &self.outdir.join(self.input.file_name().unwrap()); + let basedir = path.parent().unwrap_or(Path::new(".")); + let rlib_name = format!("lib{}.rlib", self.crate_name); + + basedir.join(rlib_name) + } } /// Generate a `KaniMetadata` by extending the original metadata to contain the function under diff --git a/kani-driver/src/util.rs b/kani-driver/src/util.rs index 1e0957dc7726..bfbf9e8d04e1 100644 --- a/kani-driver/src/util.rs +++ b/kani-driver/src/util.rs @@ -26,18 +26,6 @@ pub fn crate_name(path: &Path) -> String { stem.replace(['-', '.'], "_") } -/// Attempt to guess the rlib name for rust source file. -/// This is only used by 'kani', never 'cargo-kani', so we hopefully don't have too many corner -/// cases to deal with. -/// In rustc, you can find some code dealing this this naming in: -/// compiler/rustc_codegen_ssa/src/back/link.rs -pub fn guess_rlib_name(path: &Path) -> PathBuf { - let basedir = path.parent().unwrap_or(Path::new(".")); - let rlib_name = format!("lib{}.rlib", crate_name(path)); - - basedir.join(rlib_name) -} - /// Given a path of some sort (usually from argv0), this attempts to extract the basename / stem /// of the executable. e.g. "/path/foo -> foo" "./foo.exe -> foo" "foo -> foo" pub fn executable_basename(argv0: &Option<&OsString>) -> Option { @@ -117,14 +105,6 @@ mod tests { assert_eq!(alter_extension(&q, "symtab.json"), PathBuf::from("file.more.symtab.json")); } - #[test] - fn check_guess_rlib_name() { - assert_eq!(guess_rlib_name(Path::new("mycrate.rs")), PathBuf::from("libmycrate.rlib")); - assert_eq!(guess_rlib_name(Path::new("my-crate.rs")), PathBuf::from("libmy_crate.rlib")); - assert_eq!(guess_rlib_name(Path::new("./foo.rs")), PathBuf::from("./libfoo.rlib")); - assert_eq!(guess_rlib_name(Path::new("a/b/foo.rs")), PathBuf::from("a/b/libfoo.rlib")); - } - #[test] fn check_exe_basename() { assert_eq!( diff --git a/tests/script-based-pre/crate-name/a/src/lib.rs b/tests/script-based-pre/crate-name/a/src/lib.rs new file mode 100644 index 000000000000..bff1d17f864a --- /dev/null +++ b/tests/script-based-pre/crate-name/a/src/lib.rs @@ -0,0 +1,6 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +pub fn add_a(left: usize, right: usize) -> usize { + left + right +} diff --git a/tests/script-based-pre/crate-name/b/src/lib.rs b/tests/script-based-pre/crate-name/b/src/lib.rs new file mode 100644 index 000000000000..5a1a11687fcf --- /dev/null +++ b/tests/script-based-pre/crate-name/b/src/lib.rs @@ -0,0 +1,7 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +extern crate a; + +pub fn add_b(left: usize, right: usize) -> usize { + a::add_a(left, right) +} diff --git a/tests/script-based-pre/crate-name/c/src/lib.rs b/tests/script-based-pre/crate-name/c/src/lib.rs new file mode 100644 index 000000000000..b4dadd69e1be --- /dev/null +++ b/tests/script-based-pre/crate-name/c/src/lib.rs @@ -0,0 +1,8 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +extern crate a; +extern crate b; + +pub fn add_c(left: usize, right: usize) -> usize { + b::add_b(left, right) +} diff --git a/tests/script-based-pre/crate-name/config.yml b/tests/script-based-pre/crate-name/config.yml new file mode 100644 index 000000000000..f022a9c03732 --- /dev/null +++ b/tests/script-based-pre/crate-name/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: crate-name.sh +expected: crate-name.expected diff --git a/tests/script-based-pre/crate-name/crate-name.expected b/tests/script-based-pre/crate-name/crate-name.expected new file mode 100644 index 000000000000..1a4e02d1ff62 --- /dev/null +++ b/tests/script-based-pre/crate-name/crate-name.expected @@ -0,0 +1 @@ +No proof harnesses (functions with #[kani::proof]) were found to verify. diff --git a/tests/script-based-pre/crate-name/crate-name.sh b/tests/script-based-pre/crate-name/crate-name.sh new file mode 100755 index 000000000000..229cc36938f9 --- /dev/null +++ b/tests/script-based-pre/crate-name/crate-name.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +# This test performs multiple checks focused on crate names. The first steps +# check expected results with the default naming scheme. The remaining ones +# check expected results with the `--crate-name=` feature which allows +# users to specify the crate name used for compilation with standalone `kani`. +set -eu + +check_file_exists() { + local file=$1 + if ! [ -e "${file}" ] + then + echo "error: expected \`${file}\` to have been generated" + exit 1 + fi +} + +# 1. Check expected results with the default naming scheme. +# Note: The assumed crate name is `lib`, so we generate `liblib.rlib`. +kani --only-codegen --keep-temps a/src/lib.rs +check_file_exists a/src/liblib.rlib +check_file_exists a/src/lib.kani-metadata.json +rm a/src/liblib.rlib +rm a/src/lib.kani-metadata.json + +# 2. Check expected results with the default naming scheme, which replaces +# some characters. +# Note: The assumed crate name is `my-code`, so we generate `libmy_code.rlib`. +kani --only-codegen --keep-temps my-code.rs +check_file_exists libmy_code.rlib +check_file_exists my_code.kani-metadata.json + +# 3. Check expected results with the `--crate-name=` feature. This feature +# allows users to specify the crate name used for compilation with standalone +# `kani`, enabling the compilation of multiple dependencies with similar +# names. +# Note: In the example below, compiling without `--crate-name=` would +# result in files named `liblib.rlib` for each dependency. +kani --only-codegen --keep-temps a/src/lib.rs --crate-name="a" +check_file_exists a/src/liba.rlib +check_file_exists a/src/a.kani-metadata.json + +RUSTFLAGS="--extern a=a/src/liba.rlib" kani --only-codegen --keep-temps b/src/lib.rs --crate-name="b" +check_file_exists b/src/libb.rlib +check_file_exists b/src/b.kani-metadata.json + +RUSTFLAGS="--extern b=b/src/libb.rlib --extern a=a/src/liba.rlib" kani c/src/lib.rs + +rm a/src/liba.rlib +rm a/src/a.kani-metadata.json +rm b/src/libb.rlib +rm b/src/b.kani-metadata.json diff --git a/tests/script-based-pre/crate-name/my-code.rs b/tests/script-based-pre/crate-name/my-code.rs new file mode 100644 index 000000000000..bff1d17f864a --- /dev/null +++ b/tests/script-based-pre/crate-name/my-code.rs @@ -0,0 +1,6 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +pub fn add_a(left: usize, right: usize) -> usize { + left + right +} From 556cf7189cc5fc6749bb16a88025afaebe29fae5 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Mon, 11 Mar 2024 11:58:02 -0700 Subject: [PATCH 028/225] cargo update and fix macos CI (#3067) Fix recurring [macos CI issue](https://github.com/actions/runner-images/issues/9471) (see https://github.com/model-checking/kani/actions/runs/8234799856/job/22521976090?pr=3065#step:3:202 for an example of a failing run) and do a `cargo update`. This subsumes https://github.com/model-checking/kani/pull/3065 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 | 50 ++++++++++++++--------------- scripts/setup/macos/install_deps.sh | 12 +++---- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3af5e6232a76..4db35ee6ab77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -145,7 +145,7 @@ version = "0.47.0" dependencies = [ "anyhow", "cargo_metadata", - "clap 4.5.1", + "clap 4.5.2", "which", ] @@ -204,9 +204,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651" dependencies = [ "clap_builder", "clap_derive", @@ -214,9 +214,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -481,7 +481,7 @@ dependencies = [ name = "kani-compiler" version = "0.47.0" dependencies = [ - "clap 4.5.1", + "clap 4.5.2", "cprover_bindings", "home", "itertools", @@ -492,8 +492,8 @@ dependencies = [ "serde", "serde_json", "shell-words", - "strum 0.26.1", - "strum_macros 0.26.1", + "strum 0.26.2", + "strum_macros 0.26.2", "tracing", "tracing-subscriber", ] @@ -504,7 +504,7 @@ version = "0.47.0" dependencies = [ "anyhow", "cargo_metadata", - "clap 4.5.1", + "clap 4.5.2", "comfy-table", "console", "glob", @@ -517,8 +517,8 @@ dependencies = [ "rustc-demangle", "serde", "serde_json", - "strum 0.26.1", - "strum_macros 0.26.1", + "strum 0.26.2", + "strum_macros 0.26.2", "tempfile", "toml", "tracing", @@ -550,11 +550,11 @@ dependencies = [ name = "kani_metadata" version = "0.47.0" dependencies = [ - "clap 4.5.1", + "clap 4.5.2", "cprover_bindings", "serde", - "strum 0.26.1", - "strum_macros 0.26.1", + "strum 0.26.2", + "strum_macros 0.26.2", ] [[package]] @@ -896,7 +896,7 @@ checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.5", + "regex-automata 0.4.6", "regex-syntax 0.8.2", ] @@ -911,9 +911,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -1115,9 +1115,9 @@ checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" [[package]] name = "strum" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" [[package]] name = "strum_macros" @@ -1134,9 +1134,9 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ "heck", "proc-macro2", @@ -1219,9 +1219,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" +checksum = "af06656561d28735e9c1cd63dfd57132c8155426aa6af24f36a00a351f88c48e" dependencies = [ "serde", "serde_spanned", @@ -1240,9 +1240,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.6" +version = "0.22.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" +checksum = "18769cd1cec395d70860ceb4d932812a0b4d06b1a4bb336745a4d21b9496e992" dependencies = [ "indexmap", "serde", diff --git a/scripts/setup/macos/install_deps.sh b/scripts/setup/macos/install_deps.sh index c544846b403f..429eb200541a 100755 --- a/scripts/setup/macos/install_deps.sh +++ b/scripts/setup/macos/install_deps.sh @@ -9,16 +9,14 @@ set -eux # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-software #brew update +# Install Python separately to workround recurring homebrew CI issue. +# See https://github.com/actions/runner-images/issues/9471 for more details. +brew install python@3 || true +brew link --overwrite python@3 + # Install dependencies via `brew` brew install universal-ctags wget jq -# Add Python package dependencies -PYTHON_DEPS=( - autopep8 -) - -python3 -m pip install "${PYTHON_DEPS[@]}" - # Get the directory containing this script SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" From a8db1207996b793ce0db975ab9f1b1805fa74d84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 14:13:58 -0700 Subject: [PATCH 029/225] Bump tests/perf/s2n-quic from `d103836` to `1a7faa8` (#3066) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `d103836` to `1a7faa8`.
Commits
  • 1a7faa8 feat(s2n-quic-xdp): add flag for scatter-gather mode (#2144)
  • d0c9f76 feat(s2n-quic-core): add stream state enums (#2132)
  • a14804f feat(s2n-quic-xdp): provide umem ptr method (#2146)
  • fb3ac86 build(deps): update env_logger requirement in /tools/xdp (#2101)
  • 2ad398c build(deps): bump docker/setup-buildx-action from 3.0.0 to 3.1.0 (#2137)
  • 0b2be23 build(s2n-quic-xdp): update toolchain and dependencies (#2147)
  • 670b7ad test(s2n-quic-core): switch solver to CaDiCaL for weighted_average_test (#2...
  • 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> Co-authored-by: Zyad Hassan <88045115+zhassan-aws@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 d103836ce086..1a7faa87ebcf 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit d103836ce086534e63c75a0b497079ed74e58c18 +Subproject commit 1a7faa87ebcf2b84a068f21af3306a6f42eb8c74 From e7a4d835fa5b0e81356ec96399270fbdcce7af01 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Tue, 12 Mar 2024 11:06:45 -0700 Subject: [PATCH 030/225] Upgrade toolchain to 2024-03-11 (#3071) Relevant upstream changes: https://github.com/rust-lang/rust/pull/120675: An intrinsic `Symbol` is now wrapped in a `IntrinsicDef` struct, so the relevant part of the code needed to be updated. https://github.com/rust-lang/rust/pull/121464: The second argument of the `create_wrapper_file` function changed from a vector to a string. https://github.com/rust-lang/rust/pull/121662: `NullOp::DebugAssertions` was renamed to `NullOp::UbCheck` and it now has data (currently unused by Kani) https://github.com/rust-lang/rust/pull/121728: Introduces `F16` and `F128`, so needed to add stubs for them https://github.com/rust-lang/rust/pull/121969: `parse_sess` was renamed to `psess`, so updated the relevant code. https://github.com/rust-lang/rust/pull/122059: The `is_val_statically_known` intrinsic is now used in some `core::fmt` code, so had to handle it in (codegen'ed to false). https://github.com/rust-lang/rust/pull/122233: This added a new `retag_box_to_raw` intrinsic. This is an operation that is primarily relevant for stacked borrows. For Kani, we just return the pointer. Resolves #3057 --- .../codegen/intrinsic.rs | 19 +++++++++++++++++++ .../codegen_cprover_gotoc/codegen/rvalue.rs | 2 +- .../src/codegen_cprover_gotoc/codegen/typ.rs | 6 ++++++ .../compiler_interface.rs | 2 +- kani-compiler/src/kani_middle/attributes.rs | 7 +++---- kani-compiler/src/kani_middle/intrinsics.rs | 8 ++++---- kani-compiler/src/kani_middle/reachability.rs | 2 +- kani-driver/src/assess/mod.rs | 2 +- rust-toolchain.toml | 2 +- tests/coverage/unreachable/variant/expected | 2 +- tools/bookrunner/librustdoc/html/markdown.rs | 2 +- 11 files changed, 39 insertions(+), 15 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 20e3f34f48bf..2cabc4cfd273 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -474,6 +474,11 @@ impl<'tcx> GotocCtx<'tcx> { let binop_stmt = codegen_intrinsic_binop!(sub); self.add_finite_args_checks(intrinsic, fargs_clone, binop_stmt, span) } + "is_val_statically_known" => { + // Returning false is sound according do this intrinsic's documentation: + // https://doc.rust-lang.org/nightly/std/intrinsics/fn.is_val_statically_known.html + self.codegen_expr_to_place_stable(place, Expr::c_false()) + } "likely" => self.codegen_expr_to_place_stable(place, fargs.remove(0)), "log10f32" => unstable_codegen!(codegen_simple_intrinsic!(Log10f)), "log10f64" => unstable_codegen!(codegen_simple_intrinsic!(Log10)), @@ -506,6 +511,7 @@ impl<'tcx> GotocCtx<'tcx> { "ptr_offset_from" => self.codegen_ptr_offset_from(fargs, place, loc), "ptr_offset_from_unsigned" => self.codegen_ptr_offset_from_unsigned(fargs, place, loc), "raw_eq" => self.codegen_intrinsic_raw_eq(instance, fargs, place, loc), + "retag_box_to_raw" => self.codegen_retag_box_to_raw(fargs, place, loc), "rintf32" => codegen_simple_intrinsic!(Rintf), "rintf64" => codegen_simple_intrinsic!(Rint), "rotate_left" => codegen_intrinsic_binop!(rol), @@ -1259,6 +1265,19 @@ impl<'tcx> GotocCtx<'tcx> { self.codegen_expr_to_place_stable(p, e) } + // This is an operation that is primarily relevant for stacked borrow + // checks. For Kani, we simply return the pointer. + fn codegen_retag_box_to_raw( + &mut self, + mut fargs: Vec, + p: &Place, + _loc: Location, + ) -> Stmt { + assert_eq!(fargs.len(), 1, "raw_box_to_box expected one argument"); + let arg = fargs.remove(0); + self.codegen_expr_to_place_stable(p, arg) + } + fn vtable_info( &mut self, info: VTableInfo, diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index 3df6d807aaea..5867448d9973 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -725,7 +725,7 @@ impl<'tcx> GotocCtx<'tcx> { .bytes(), Type::size_t(), ), - NullOp::DebugAssertions => Expr::c_false(), + NullOp::UbCheck(_) => Expr::c_false(), } } Rvalue::ShallowInitBox(ref operand, content_ty) => { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index 57af958abf41..089d871d22b4 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -542,6 +542,9 @@ impl<'tcx> GotocCtx<'tcx> { ty::Float(k) => match k { FloatTy::F32 => Type::float(), FloatTy::F64 => Type::double(), + // `F16` and `F128` are not yet handled. + // Tracked here: + FloatTy::F16 | FloatTy::F128 => unimplemented!(), }, ty::Adt(def, _) if def.repr().simd() => self.codegen_vector(ty), ty::Adt(def, subst) => { @@ -1346,6 +1349,9 @@ impl<'tcx> GotocCtx<'tcx> { Primitive::F32 => self.tcx.types.f32, Primitive::F64 => self.tcx.types.f64, + // `F16` and `F128` are not yet handled. + // Tracked here: + Primitive::F16 | Primitive::F128 => unimplemented!(), Primitive::Pointer(_) => Ty::new_ptr( self.tcx, ty::TypeAndMut { ty: self.tcx.types.u8, mutbl: Mutability::Not }, diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index 91e6c2538195..cad8bcbfb9ce 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -397,7 +397,7 @@ impl CodegenBackend for GotocCodegenBackend { let path = MaybeTempDir::new(tmp_dir, sess.opts.cg.save_temps); let (metadata, _metadata_position) = create_wrapper_file( sess, - b".rmeta".to_vec(), + ".rmeta".to_string(), codegen_results.metadata.raw_data(), ); let metadata = emit_wrapper_file(sess, &metadata, &path, METADATA_FILENAME); diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index 621107acd13e..df494d7daa3c 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -611,9 +611,8 @@ fn parse_modify_values<'a>( let mut iter = t.trees(); std::iter::from_fn(move || { let tree = iter.next()?; - let wrong_token_err = || { - tcx.sess.parse_sess.dcx.span_err(tree.span(), "Unexpected token. Expected identifier.") - }; + let wrong_token_err = + || tcx.sess.psess.dcx.span_err(tree.span(), "Unexpected token. Expected identifier."); let result = match tree { TokenTree::Token(token, _) => { if let TokenKind::Ident(id, _) = &token.kind { @@ -640,7 +639,7 @@ fn parse_modify_values<'a>( match iter.next() { None | Some(comma_tok!()) => (), Some(not_comma) => { - tcx.sess.parse_sess.dcx.span_err( + tcx.sess.psess.dcx.span_err( not_comma.span(), "Unexpected token, expected end of attribute or comma", ); diff --git a/kani-compiler/src/kani_middle/intrinsics.rs b/kani-compiler/src/kani_middle/intrinsics.rs index 404cb0f277c3..51e6140b7e1a 100644 --- a/kani-compiler/src/kani_middle/intrinsics.rs +++ b/kani-compiler/src/kani_middle/intrinsics.rs @@ -6,7 +6,7 @@ use rustc_index::IndexVec; use rustc_middle::mir::{Body, Const as mirConst, ConstValue, Operand, TerminatorKind}; use rustc_middle::mir::{Local, LocalDecl}; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::ty::{Const, GenericArgsRef}; +use rustc_middle::ty::{Const, GenericArgsRef, IntrinsicDef}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Symbol}; use tracing::{debug, trace}; @@ -33,8 +33,8 @@ impl<'tcx> ModelIntrinsics<'tcx> { let terminator = block.terminator_mut(); if let TerminatorKind::Call { func, args, .. } = &mut terminator.kind { let func_ty = func.ty(&self.local_decls, self.tcx); - if let Some((intrinsic_name, generics)) = resolve_rust_intrinsic(self.tcx, func_ty) - { + if let Some((intrinsic, generics)) = resolve_rust_intrinsic(self.tcx, func_ty) { + let intrinsic_name = intrinsic.name; trace!(?func, ?intrinsic_name, "run_pass"); if intrinsic_name == sym::simd_bitmask { self.replace_simd_bitmask(func, args, generics) @@ -99,7 +99,7 @@ fn simd_len_and_type<'tcx>(tcx: TyCtxt<'tcx>, simd_ty: Ty<'tcx>) -> (Const<'tcx> fn resolve_rust_intrinsic<'tcx>( tcx: TyCtxt<'tcx>, func_ty: Ty<'tcx>, -) -> Option<(Symbol, GenericArgsRef<'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)); diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 11ba57cb1cff..2fa1bf057c1c 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -515,8 +515,8 @@ fn collect_alloc_items(alloc_id: AllocId) -> Vec { } #[cfg(debug_assertions)] +#[allow(dead_code)] mod debug { - #![allow(dead_code)] use std::fmt::{Display, Formatter}; use std::{ diff --git a/kani-driver/src/assess/mod.rs b/kani-driver/src/assess/mod.rs index d183e880d8b2..2df4d993d2d7 100644 --- a/kani-driver/src/assess/mod.rs +++ b/kani-driver/src/assess/mod.rs @@ -170,7 +170,7 @@ fn reconstruct_metadata_structure( } if !package_artifacts.is_empty() { let mut merged = crate::metadata::merge_kani_metadata(package_artifacts); - merged.crate_name = package.name.clone(); + merged.crate_name.clone_from(&package.name); package_metas.push(merged); } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 747a9f2f5295..b6f6e8a63534 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-03-01" +channel = "nightly-2024-03-11" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/coverage/unreachable/variant/expected b/tests/coverage/unreachable/variant/expected index 08a2f824da83..8fa3ec8b870f 100644 --- a/tests/coverage/unreachable/variant/expected +++ b/tests/coverage/unreachable/variant/expected @@ -1,4 +1,4 @@ -coverage/unreachable/variant/main.rs, 15, PARTIAL +coverage/unreachable/variant/main.rs, 15, FULL coverage/unreachable/variant/main.rs, 16, NONE coverage/unreachable/variant/main.rs, 17, NONE coverage/unreachable/variant/main.rs, 18, FULL diff --git a/tools/bookrunner/librustdoc/html/markdown.rs b/tools/bookrunner/librustdoc/html/markdown.rs index 3f827b77ff2b..857b42bcd76a 100644 --- a/tools/bookrunner/librustdoc/html/markdown.rs +++ b/tools/bookrunner/librustdoc/html/markdown.rs @@ -225,7 +225,7 @@ impl LangString { let mut data = LangString::default(); let mut ignores = vec![]; - data.original = string.to_owned(); + string.clone_into(&mut data.original); for token in Self::tokens(string) { match token { From 0cc4b24638288166c5dcd73da55e37e8cd9399e9 Mon Sep 17 00:00:00 2001 From: Kareem Khazem Date: Wed, 13 Mar 2024 21:24:32 +0000 Subject: [PATCH 031/225] Emit `dead` goto-instructions on MIR StatementDead (#3063) This commit adds a new `Dead` goto-instruction that gets codegened whenever Kani sees a MIR `StatementDead` statement. This new goto instruction corresponds to the CBMC [code_deadt]( https://diffblue.github.io/cbmc/classcode__deadt.html) statement that marks the point where a local variable goes out of scope. This new instruction is needed to detect invalid accesses of dead local variables. The commit also codegens a CBMC `Decl` instruction upon seeing a MIR StatementLive. This ensures that variables that go out of scope at the end of a loop are not falsely marked as having a dead dereference when they are accessed on the next loop iteration. Resolves #3061. 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/stmt.rs | 7 +++++++ cprover_bindings/src/irep/to_irep.rs | 1 + .../src/codegen_cprover_gotoc/codegen/place.rs | 2 +- .../codegen_cprover_gotoc/codegen/statement.rs | 6 ++++-- kani-driver/src/call_single_file.rs | 2 ++ tests/coverage/reachable/assert-false/expected | 6 +++--- .../reachable/assert/reachable_pass/expected | 2 +- .../reachable/bounds/reachable_fail/expected | 2 +- .../reachable/div-zero/reachable_fail/expected | 2 +- .../reachable/overflow/reachable_fail/expected | 2 +- .../reachable/rem-zero/reachable_fail/expected | 2 +- tests/coverage/unreachable/assert/expected | 4 ++-- tests/coverage/unreachable/assert_eq/expected | 2 +- tests/coverage/unreachable/assert_ne/expected | 2 +- tests/coverage/unreachable/check_id/expected | 4 ++-- .../coverage/unreachable/if-statement/expected | 2 +- .../unreachable/tutorial_unreachable/expected | 2 +- .../unreachable/while-loop-break/expected | 2 +- .../dead-invalid-access-via-raw/expected | 16 ++++++++++++++++ .../dead-invalid-access-via-raw/main.rs | 17 +++++++++++++++++ 20 files changed, 65 insertions(+), 20 deletions(-) create mode 100644 tests/expected/dead-invalid-access-via-raw/expected create mode 100644 tests/expected/dead-invalid-access-via-raw/main.rs diff --git a/cprover_bindings/src/goto_program/stmt.rs b/cprover_bindings/src/goto_program/stmt.rs index 58755a0bffae..951e58f9a954 100644 --- a/cprover_bindings/src/goto_program/stmt.rs +++ b/cprover_bindings/src/goto_program/stmt.rs @@ -57,6 +57,8 @@ pub enum StmtBody { Break, /// `continue;` Continue, + /// End-of-life of a local variable + Dead(Expr), /// `lhs.typ lhs = value;` or `lhs.typ lhs;` Decl { lhs: Expr, // SymbolExpr @@ -232,6 +234,11 @@ impl Stmt { BuiltinFn::CProverCover.call(vec![cond], loc).as_stmt(loc) } + /// Local variable goes out of scope + pub fn dead(symbol: Expr, loc: Location) -> Self { + stmt!(Dead(symbol), loc) + } + /// `lhs.typ lhs = value;` or `lhs.typ lhs;` pub fn decl(lhs: Expr, value: Option, loc: Location) -> Self { assert!(lhs.is_symbol()); diff --git a/cprover_bindings/src/irep/to_irep.rs b/cprover_bindings/src/irep/to_irep.rs index 16b8b69c8fe7..5c17ecf87b5d 100644 --- a/cprover_bindings/src/irep/to_irep.rs +++ b/cprover_bindings/src/irep/to_irep.rs @@ -433,6 +433,7 @@ impl ToIrep for StmtBody { } StmtBody::Break => code_irep(IrepId::Break, vec![]), StmtBody::Continue => code_irep(IrepId::Continue, vec![]), + StmtBody::Dead(symbol) => code_irep(IrepId::Dead, vec![symbol.to_irep(mm)]), StmtBody::Decl { lhs, value } => { if value.is_some() { code_irep( diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs index 9296b713c862..6b764fb63365 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs @@ -381,7 +381,7 @@ impl<'tcx> GotocCtx<'tcx> { } /// Codegen for a local - fn codegen_local(&mut self, l: Local) -> Expr { + pub fn codegen_local(&mut self, l: Local) -> Expr { let local_ty = self.local_ty_stable(l); // Check if the local is a function definition (see comment above) if let Some(fn_def) = self.codegen_local_fndef(local_ty) { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 05b153f9583f..6379a023423f 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -75,8 +75,10 @@ impl<'tcx> GotocCtx<'tcx> { .goto_expr; self.codegen_set_discriminant(dest_ty, dest_expr, *variant_index, location) } - StatementKind::StorageLive(_) => Stmt::skip(location), // TODO: fix me - StatementKind::StorageDead(_) => Stmt::skip(location), // TODO: fix me + StatementKind::StorageLive(var_id) => { + Stmt::decl(self.codegen_local(*var_id), None, location) + } + StatementKind::StorageDead(var_id) => Stmt::dead(self.codegen_local(*var_id), location), StatementKind::Intrinsic(NonDivergingIntrinsic::CopyNonOverlapping( CopyNonOverlapping { src, dst, count }, )) => { diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index 9b6e0598daa5..fc41bac69d9c 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -122,6 +122,8 @@ impl KaniSession { "symbol-mangling-version=v0", "-Z", "panic_abort_tests=yes", + "-Z", + "sanitizer=address", ] .map(OsString::from), ); diff --git a/tests/coverage/reachable/assert-false/expected b/tests/coverage/reachable/assert-false/expected index 7a9fef1ca77c..97ffbe1d96e4 100644 --- a/tests/coverage/reachable/assert-false/expected +++ b/tests/coverage/reachable/assert-false/expected @@ -1,8 +1,8 @@ coverage/reachable/assert-false/main.rs, 6, FULL coverage/reachable/assert-false/main.rs, 7, FULL -coverage/reachable/assert-false/main.rs, 11, FULL -coverage/reachable/assert-false/main.rs, 12, FULL -coverage/reachable/assert-false/main.rs, 15, FULL +coverage/reachable/assert-false/main.rs, 11, PARTIAL +coverage/reachable/assert-false/main.rs, 12, PARTIAL +coverage/reachable/assert-false/main.rs, 15, PARTIAL coverage/reachable/assert-false/main.rs, 16, FULL coverage/reachable/assert-false/main.rs, 17, PARTIAL coverage/reachable/assert-false/main.rs, 19, FULL diff --git a/tests/coverage/reachable/assert/reachable_pass/expected b/tests/coverage/reachable/assert/reachable_pass/expected index 67ae085a3e83..9d21185b3a83 100644 --- a/tests/coverage/reachable/assert/reachable_pass/expected +++ b/tests/coverage/reachable/assert/reachable_pass/expected @@ -1,4 +1,4 @@ coverage/reachable/assert/reachable_pass/test.rs, 6, FULL -coverage/reachable/assert/reachable_pass/test.rs, 7, FULL +coverage/reachable/assert/reachable_pass/test.rs, 7, PARTIAL coverage/reachable/assert/reachable_pass/test.rs, 8, FULL coverage/reachable/assert/reachable_pass/test.rs, 10, FULL diff --git a/tests/coverage/reachable/bounds/reachable_fail/expected b/tests/coverage/reachable/bounds/reachable_fail/expected index af2f30e51fe2..fedfec8b2a1e 100644 --- a/tests/coverage/reachable/bounds/reachable_fail/expected +++ b/tests/coverage/reachable/bounds/reachable_fail/expected @@ -1,4 +1,4 @@ coverage/reachable/bounds/reachable_fail/test.rs, 5, PARTIAL coverage/reachable/bounds/reachable_fail/test.rs, 6, NONE -coverage/reachable/bounds/reachable_fail/test.rs, 10, FULL +coverage/reachable/bounds/reachable_fail/test.rs, 10, PARTIAL coverage/reachable/bounds/reachable_fail/test.rs, 11, NONE diff --git a/tests/coverage/reachable/div-zero/reachable_fail/expected b/tests/coverage/reachable/div-zero/reachable_fail/expected index 1ec1abefffd8..c1ac77404680 100644 --- a/tests/coverage/reachable/div-zero/reachable_fail/expected +++ b/tests/coverage/reachable/div-zero/reachable_fail/expected @@ -1,4 +1,4 @@ coverage/reachable/div-zero/reachable_fail/test.rs, 5, PARTIAL coverage/reachable/div-zero/reachable_fail/test.rs, 6, NONE -coverage/reachable/div-zero/reachable_fail/test.rs, 10, FULL +coverage/reachable/div-zero/reachable_fail/test.rs, 10, PARTIAL coverage/reachable/div-zero/reachable_fail/test.rs, 11, NONE diff --git a/tests/coverage/reachable/overflow/reachable_fail/expected b/tests/coverage/reachable/overflow/reachable_fail/expected index f20826fb1a8e..d45edcc37a63 100644 --- a/tests/coverage/reachable/overflow/reachable_fail/expected +++ b/tests/coverage/reachable/overflow/reachable_fail/expected @@ -1,5 +1,5 @@ coverage/reachable/overflow/reachable_fail/test.rs, 8, PARTIAL coverage/reachable/overflow/reachable_fail/test.rs, 9, FULL coverage/reachable/overflow/reachable_fail/test.rs, 13, FULL -coverage/reachable/overflow/reachable_fail/test.rs, 14, FULL +coverage/reachable/overflow/reachable_fail/test.rs, 14, PARTIAL coverage/reachable/overflow/reachable_fail/test.rs, 15, NONE diff --git a/tests/coverage/reachable/rem-zero/reachable_fail/expected b/tests/coverage/reachable/rem-zero/reachable_fail/expected index f7fa6ed7efeb..7852461e4f57 100644 --- a/tests/coverage/reachable/rem-zero/reachable_fail/expected +++ b/tests/coverage/reachable/rem-zero/reachable_fail/expected @@ -1,4 +1,4 @@ coverage/reachable/rem-zero/reachable_fail/test.rs, 5, PARTIAL coverage/reachable/rem-zero/reachable_fail/test.rs, 6, NONE -coverage/reachable/rem-zero/reachable_fail/test.rs, 10, FULL +coverage/reachable/rem-zero/reachable_fail/test.rs, 10, PARTIAL coverage/reachable/rem-zero/reachable_fail/test.rs, 11, NONE diff --git a/tests/coverage/unreachable/assert/expected b/tests/coverage/unreachable/assert/expected index f5b5f8044769..9bc6d8faa4f9 100644 --- a/tests/coverage/unreachable/assert/expected +++ b/tests/coverage/unreachable/assert/expected @@ -1,6 +1,6 @@ coverage/unreachable/assert/test.rs, 6, FULL -coverage/unreachable/assert/test.rs, 7, FULL -coverage/unreachable/assert/test.rs, 9, FULL +coverage/unreachable/assert/test.rs, 7, PARTIAL +coverage/unreachable/assert/test.rs, 9, PARTIAL coverage/unreachable/assert/test.rs, 10, NONE coverage/unreachable/assert/test.rs, 12, NONE coverage/unreachable/assert/test.rs, 16, FULL diff --git a/tests/coverage/unreachable/assert_eq/expected b/tests/coverage/unreachable/assert_eq/expected index f4e7608b2c13..9b13c3c96ded 100644 --- a/tests/coverage/unreachable/assert_eq/expected +++ b/tests/coverage/unreachable/assert_eq/expected @@ -1,5 +1,5 @@ coverage/unreachable/assert_eq/test.rs, 6, FULL coverage/unreachable/assert_eq/test.rs, 7, FULL -coverage/unreachable/assert_eq/test.rs, 8, FULL +coverage/unreachable/assert_eq/test.rs, 8, PARTIAL coverage/unreachable/assert_eq/test.rs, 9, NONE coverage/unreachable/assert_eq/test.rs, 11, FULL diff --git a/tests/coverage/unreachable/assert_ne/expected b/tests/coverage/unreachable/assert_ne/expected index 3b57defb4c36..f027f432e280 100644 --- a/tests/coverage/unreachable/assert_ne/expected +++ b/tests/coverage/unreachable/assert_ne/expected @@ -1,6 +1,6 @@ coverage/unreachable/assert_ne/test.rs, 6, FULL coverage/unreachable/assert_ne/test.rs, 7, FULL coverage/unreachable/assert_ne/test.rs, 8, FULL -coverage/unreachable/assert_ne/test.rs, 10, FULL +coverage/unreachable/assert_ne/test.rs, 10, PARTIAL coverage/unreachable/assert_ne/test.rs, 11, NONE coverage/unreachable/assert_ne/test.rs, 14, FULL diff --git a/tests/coverage/unreachable/check_id/expected b/tests/coverage/unreachable/check_id/expected index 214cbfa827bd..a2d296f0f9a3 100644 --- a/tests/coverage/unreachable/check_id/expected +++ b/tests/coverage/unreachable/check_id/expected @@ -1,5 +1,5 @@ coverage/unreachable/check_id/main.rs, 5, FULL -coverage/unreachable/check_id/main.rs, 6, FULL +coverage/unreachable/check_id/main.rs, 6, PARTIAL coverage/unreachable/check_id/main.rs, 8, NONE coverage/unreachable/check_id/main.rs, 10, FULL coverage/unreachable/check_id/main.rs, 14, FULL @@ -12,5 +12,5 @@ coverage/unreachable/check_id/main.rs, 20, FULL coverage/unreachable/check_id/main.rs, 21, FULL coverage/unreachable/check_id/main.rs, 22, FULL coverage/unreachable/check_id/main.rs, 23, FULL -coverage/unreachable/check_id/main.rs, 24, FULL +coverage/unreachable/check_id/main.rs, 24, PARTIAL coverage/unreachable/check_id/main.rs, 25, NONE diff --git a/tests/coverage/unreachable/if-statement/expected b/tests/coverage/unreachable/if-statement/expected index 4460f23a80de..8b481863a163 100644 --- a/tests/coverage/unreachable/if-statement/expected +++ b/tests/coverage/unreachable/if-statement/expected @@ -1,4 +1,4 @@ -coverage/unreachable/if-statement/main.rs, 5, FULL +coverage/unreachable/if-statement/main.rs, 5, PARTIAL coverage/unreachable/if-statement/main.rs, 7, PARTIAL coverage/unreachable/if-statement/main.rs, 8, NONE coverage/unreachable/if-statement/main.rs, 9, NONE diff --git a/tests/coverage/unreachable/tutorial_unreachable/expected b/tests/coverage/unreachable/tutorial_unreachable/expected index cf45a502d295..624aa520edc9 100644 --- a/tests/coverage/unreachable/tutorial_unreachable/expected +++ b/tests/coverage/unreachable/tutorial_unreachable/expected @@ -1,5 +1,5 @@ coverage/unreachable/tutorial_unreachable/main.rs, 6, FULL coverage/unreachable/tutorial_unreachable/main.rs, 7, FULL -coverage/unreachable/tutorial_unreachable/main.rs, 8, FULL +coverage/unreachable/tutorial_unreachable/main.rs, 8, PARTIAL coverage/unreachable/tutorial_unreachable/main.rs, 9, NONE coverage/unreachable/tutorial_unreachable/main.rs, 11, FULL diff --git a/tests/coverage/unreachable/while-loop-break/expected b/tests/coverage/unreachable/while-loop-break/expected index a0e43c183846..dc66d3e823d3 100644 --- a/tests/coverage/unreachable/while-loop-break/expected +++ b/tests/coverage/unreachable/while-loop-break/expected @@ -1,5 +1,5 @@ coverage/unreachable/while-loop-break/main.rs, 8, FULL -coverage/unreachable/while-loop-break/main.rs, 9, FULL +coverage/unreachable/while-loop-break/main.rs, 9, PARTIAL coverage/unreachable/while-loop-break/main.rs, 10, FULL coverage/unreachable/while-loop-break/main.rs, 11, FULL coverage/unreachable/while-loop-break/main.rs, 13, FULL diff --git a/tests/expected/dead-invalid-access-via-raw/expected b/tests/expected/dead-invalid-access-via-raw/expected new file mode 100644 index 000000000000..1d464eb5f031 --- /dev/null +++ b/tests/expected/dead-invalid-access-via-raw/expected @@ -0,0 +1,16 @@ +SUCCESS\ +address must be a multiple of its type's alignment +FAILURE\ +unsafe { *raw_ptr } == 10 +SUCCESS\ +pointer NULL +SUCCESS\ +pointer invalid +SUCCESS\ +deallocated dynamic object +FAILURE\ +dead object +SUCCESS\ +pointer outside object bounds +SUCCESS\ +invalid integer address diff --git a/tests/expected/dead-invalid-access-via-raw/main.rs b/tests/expected/dead-invalid-access-via-raw/main.rs new file mode 100644 index 000000000000..ed3ea655839e --- /dev/null +++ b/tests/expected/dead-invalid-access-via-raw/main.rs @@ -0,0 +1,17 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// This test checks an issue reported in github.com/model-checking/kani#3063. +// The access of the raw pointer should fail because the value being dereferenced has gone out of +// scope at the time of access. + +#[kani::proof] +pub fn check_invalid_ptr() { + let raw_ptr = { + let var = 10; + &var as *const _ + }; + + // This should fail since it is de-referencing a dead object. + assert_eq!(unsafe { *raw_ptr }, 10); +} From e004eccab617546526ab696511c99f7f957f6074 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Wed, 13 Mar 2024 15:54:31 -0700 Subject: [PATCH 032/225] Bump Kani version to 0.48.0 (#3075) These are the original release notes for the reference: ## What's Changed * Automatic cargo update to 2024-02-26 by @github-actions in https://github.com/model-checking/kani/pull/3043 * Upgrade rust toolchain to 2024-02-17 by @celinval in https://github.com/model-checking/kani/pull/3040 * Upgrade `windows-targets` crate to version 0.52.4 by @adpaco-aws in https://github.com/model-checking/kani/pull/3049 * Fix `codegen_atomic_binop` for `atomic_ptr` by @qinheping in https://github.com/model-checking/kani/pull/3047 * Upgrade Rust toolchain to `nightly-2024-02-25` by @adpaco-aws in https://github.com/model-checking/kani/pull/3048 * Update s2n-quic submodule by @zhassan-aws in https://github.com/model-checking/kani/pull/3050 * Update s2n-quic submodule weekly through dependabot by @zhassan-aws in https://github.com/model-checking/kani/pull/3053 * Retrieve info for recursion tracker reliably by @feliperodri in https://github.com/model-checking/kani/pull/3045 * Automatic cargo update to 2024-03-04 by @github-actions in https://github.com/model-checking/kani/pull/3055 * Upgrade Rust toolchain to `nightly-2024-03-01` by @adpaco-aws in https://github.com/model-checking/kani/pull/3052 * Add `--use-local-toolchain` to Kani setup by @jaisnan in https://github.com/model-checking/kani/pull/3056 * Replace internal reverse_postorder by a stable one by @celinval in https://github.com/model-checking/kani/pull/3064 * Add option to override `--crate-name` from `kani` by @adpaco-aws in https://github.com/model-checking/kani/pull/3054 * cargo update and fix macos CI by @zhassan-aws in https://github.com/model-checking/kani/pull/3067 * Bump tests/perf/s2n-quic from `d103836` to `1a7faa8` by @dependabot in https://github.com/model-checking/kani/pull/3066 * Upgrade toolchain to 2024-03-11 by @zhassan-aws in https://github.com/model-checking/kani/pull/3071 * Emit `dead` goto-instructions on MIR StatementDead by @karkhaz in https://github.com/model-checking/kani/pull/3063 **Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.47.0...kani-0.48.0 --- CHANGELOG.md | 15 +++++++++++++++ Cargo.lock | 18 +++++++++--------- 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_macros/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- tools/build-kani/Cargo.toml | 2 +- 11 files changed, 33 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 968906b6f4b5..08f687d8e31c 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.48.0] + +### Major Changes +* We fixed a soundness bug that in some cases may cause Kani to not detect a use-after-free issue in https://github.com/model-checking/kani/pull/3063 + +### What's Changed +* Fix `codegen_atomic_binop` for `atomic_ptr` by @qinheping in https://github.com/model-checking/kani/pull/3047 +* Retrieve info for recursion tracker reliably by @feliperodri in https://github.com/model-checking/kani/pull/3045 +* Add `--use-local-toolchain` to Kani setup by @jaisnan in https://github.com/model-checking/kani/pull/3056 +* Replace internal reverse_postorder by a stable one by @celinval in https://github.com/model-checking/kani/pull/3064 +* Add option to override `--crate-name` from `kani` by @adpaco-aws in https://github.com/model-checking/kani/pull/3054 +* Rust toolchain upgraded to 2024-03-11 by @adpaco-ws @celinval @zyadh + +**Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.47.0...kani-0.48.0 + ## [0.47.0] ### What's Changed diff --git a/Cargo.lock b/Cargo.lock index 4db35ee6ab77..7009622b1733 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,7 +141,7 @@ dependencies = [ [[package]] name = "build-kani" -version = "0.47.0" +version = "0.48.0" dependencies = [ "anyhow", "cargo_metadata", @@ -292,7 +292,7 @@ dependencies = [ [[package]] name = "cprover_bindings" -version = "0.47.0" +version = "0.48.0" dependencies = [ "lazy_static", "linear-map", @@ -472,14 +472,14 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "kani" -version = "0.47.0" +version = "0.48.0" dependencies = [ "kani_macros", ] [[package]] name = "kani-compiler" -version = "0.47.0" +version = "0.48.0" dependencies = [ "clap 4.5.2", "cprover_bindings", @@ -500,7 +500,7 @@ dependencies = [ [[package]] name = "kani-driver" -version = "0.47.0" +version = "0.48.0" dependencies = [ "anyhow", "cargo_metadata", @@ -528,7 +528,7 @@ dependencies = [ [[package]] name = "kani-verifier" -version = "0.47.0" +version = "0.48.0" dependencies = [ "anyhow", "clap 2.34.0", @@ -538,7 +538,7 @@ dependencies = [ [[package]] name = "kani_macros" -version = "0.47.0" +version = "0.48.0" dependencies = [ "proc-macro-error", "proc-macro2", @@ -548,7 +548,7 @@ dependencies = [ [[package]] name = "kani_metadata" -version = "0.47.0" +version = "0.48.0" dependencies = [ "clap 4.5.2", "cprover_bindings", @@ -1079,7 +1079,7 @@ checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "std" -version = "0.47.0" +version = "0.48.0" dependencies = [ "kani", ] diff --git a/Cargo.toml b/Cargo.toml index 845751118363..c863ffdc1945 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-verifier" -version = "0.47.0" +version = "0.48.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 3882e5ad300c..b9d0259b3577 100644 --- a/cprover_bindings/Cargo.toml +++ b/cprover_bindings/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "cprover_bindings" -version = "0.47.0" +version = "0.48.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index c5ac44d0be1e..a5b7fd006180 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-compiler" -version = "0.47.0" +version = "0.48.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index a009f444840b..36a979a1366d 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-driver" -version = "0.47.0" +version = "0.48.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 92cba41e27f8..6b6db72f4a6d 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_metadata" -version = "0.47.0" +version = "0.48.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index 27fc6fc9ad6d..4da7f91a9ed7 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.47.0" +version = "0.48.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 46b6cf3daadd..b2254b5c8954 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_macros" -version = "0.47.0" +version = "0.48.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index bdd10673f640..29467fddf70b 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.47.0" +version = "0.48.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 8c80063d7ff5..b75c373655bd 100644 --- a/tools/build-kani/Cargo.toml +++ b/tools/build-kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "build-kani" -version = "0.47.0" +version = "0.48.0" edition = "2021" description = "Builds Kani, Sysroot and release bundle." license = "MIT OR Apache-2.0" From fb6300d8a5d235cd14e3cb23ad0285ca173be020 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 13 Mar 2024 16:41:24 -0700 Subject: [PATCH 033/225] Add method to assert a pointer is valid (#3062) This new method will assert that a pointer isn't null, and that its is valid for access of a given type. Co-authored-by: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> --- cprover_bindings/src/goto_program/expr.rs | 12 + cprover_bindings/src/irep/to_irep.rs | 5 + .../codegen_cprover_gotoc/overrides/hooks.rs | 36 +++ kani_metadata/src/unstable.rs | 2 + library/kani/src/lib.rs | 42 +-- library/kani/src/mem.rs | 282 ++++++++++++++++++ scripts/kani-regression.sh | 3 +- .../function-contract/valid_ptr.expected | 11 + tests/expected/function-contract/valid_ptr.rs | 61 ++++ tests/kani/MemPredicates/fat_ptr_validity.rs | 69 +++++ tests/kani/MemPredicates/thin_ptr_validity.rs | 60 ++++ 11 files changed, 564 insertions(+), 19 deletions(-) create mode 100644 library/kani/src/mem.rs create mode 100644 tests/expected/function-contract/valid_ptr.expected create mode 100644 tests/expected/function-contract/valid_ptr.rs create mode 100644 tests/kani/MemPredicates/fat_ptr_validity.rs create mode 100644 tests/kani/MemPredicates/thin_ptr_validity.rs diff --git a/cprover_bindings/src/goto_program/expr.rs b/cprover_bindings/src/goto_program/expr.rs index 09adbdb20df3..28f8afb4fae7 100644 --- a/cprover_bindings/src/goto_program/expr.rs +++ b/cprover_bindings/src/goto_program/expr.rs @@ -126,6 +126,10 @@ pub enum ExprValue { Nondet, /// `NULL` PointerConstant(u64), + ReadOk { + ptr: Expr, + size: Expr, + }, // `op++` etc SelfOp { op: SelfOperator, @@ -717,6 +721,14 @@ impl Expr { expr!(Nondet, typ) } + /// `read_ok(ptr, size)` + pub fn read_ok(ptr: Expr, size: Expr) -> Self { + assert_eq!(*ptr.typ(), Type::void_pointer()); + assert_eq!(*size.typ(), Type::size_t()); + + expr!(ReadOk { ptr, size }, Type::bool()) + } + /// `e.g. NULL` pub fn pointer_constant(c: u64, typ: Type) -> Self { assert!(typ.is_pointer()); diff --git a/cprover_bindings/src/irep/to_irep.rs b/cprover_bindings/src/irep/to_irep.rs index 5c17ecf87b5d..9ec8c49e80bf 100644 --- a/cprover_bindings/src/irep/to_irep.rs +++ b/cprover_bindings/src/irep/to_irep.rs @@ -293,6 +293,11 @@ impl ToIrep for ExprValue { Irep::just_bitpattern_id(*i, mm.pointer_width, false) )], }, + ExprValue::ReadOk { ptr, size } => Irep { + id: IrepId::ROk, + sub: vec![ptr.to_irep(mm), size.to_irep(mm)], + named_sub: linear_map![], + }, ExprValue::SelfOp { op, e } => side_effect_irep(op.to_irep_id(), vec![e.to_irep(mm)]), ExprValue::StatementExpression { statements: ops } => side_effect_irep( IrepId::StatementExpression, diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index 0141c2886539..2fcbaa36fb44 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -217,6 +217,41 @@ impl GotocHook for Panic { } } +/// Encodes __CPROVER_r_ok +struct IsReadOk; +impl GotocHook for IsReadOk { + fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { + matches_function(tcx, instance, "KaniIsReadOk") + } + + fn handle( + &self, + gcx: &mut GotocCtx, + _instance: Instance, + mut fargs: Vec, + assign_to: &Place, + target: Option, + span: Span, + ) -> Stmt { + assert_eq!(fargs.len(), 2); + let size = fargs.pop().unwrap(); + let ptr = fargs.pop().unwrap().cast_to(Type::void_pointer()); + let target = target.unwrap(); + let loc = gcx.codegen_caller_span_stable(span); + let ret_place = + unwrap_or_return_codegen_unimplemented_stmt!(gcx, gcx.codegen_place_stable(assign_to)); + let ret_type = ret_place.goto_expr.typ().clone(); + + Stmt::block( + vec![ + ret_place.goto_expr.assign(Expr::read_ok(ptr, size).cast_to(ret_type), loc), + Stmt::goto(bb_label(target), Location::none()), + ], + Location::none(), + ) + } +} + struct RustAlloc; // Removing this hook causes regression failures. // https://github.com/model-checking/kani/issues/1170 @@ -373,6 +408,7 @@ pub fn fn_hooks() -> GotocHooks { Rc::new(Assert), Rc::new(Cover), Rc::new(Nondet), + Rc::new(IsReadOk), Rc::new(RustAlloc), Rc::new(MemCmp), Rc::new(UntrackedDeref), diff --git a/kani_metadata/src/unstable.rs b/kani_metadata/src/unstable.rs index 060508666735..3820f3f2238e 100644 --- a/kani_metadata/src/unstable.rs +++ b/kani_metadata/src/unstable.rs @@ -82,6 +82,8 @@ pub enum UnstableFeature { LineCoverage, /// Enable function contracts [RFC 9](https://model-checking.github.io/kani/rfc/rfcs/0009-function-contracts.html) FunctionContracts, + /// Memory predicate APIs. + MemPredicates, } impl UnstableFeature { diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index 087e23c7d47e..e3d27c4b1bfb 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -14,13 +14,16 @@ #![feature(repr_simd)] // Features used for tests only. #![cfg_attr(test, feature(core_intrinsics, portable_simd))] -// Required for rustc_diagnostic_item +// Required for `rustc_diagnostic_item` and `core_intrinsics` #![allow(internal_features)] +// Required for implementing memory predicates. +#![feature(ptr_metadata)] pub mod arbitrary; #[cfg(feature = "concrete_playback")] mod concrete_playback; pub mod futures; +pub mod mem; pub mod slice; pub mod tuple; pub mod vec; @@ -33,6 +36,7 @@ mod models; pub use arbitrary::Arbitrary; #[cfg(feature = "concrete_playback")] pub use concrete_playback::concrete_playback_run; + #[cfg(not(feature = "concrete_playback"))] /// NOP `concrete_playback` for type checking during verification mode. pub fn concrete_playback_run(_: Vec>, _: F) { @@ -80,18 +84,12 @@ pub fn assume(cond: bool) { /// `implies!(premise => conclusion)` means that if the `premise` is true, so /// must be the `conclusion`. /// -/// This simply expands to `!premise || conclusion` and is intended to be used -/// in function contracts to make them more readable, as the concept of an -/// implication is more natural to think about than its expansion. -/// -/// For further convenience multiple comma separated premises are allowed, and -/// are joined with `||` in the expansion. E.g. `implies!(a, b => c)` expands to -/// `!a || !b || c` and says that `c` is true if both `a` and `b` are true (see -/// also [Horn Clauses](https://en.wikipedia.org/wiki/Horn_clause)). +/// This simply expands to `!premise || conclusion` and is intended to make checks more readable, +/// as the concept of an implication is more natural to think about than its expansion. #[macro_export] macro_rules! implies { - ($($premise:expr),+ => $conclusion:expr) => { - $(!$premise)||+ || ($conclusion) + ($premise:expr => $conclusion:expr) => { + !($premise) || ($conclusion) }; } @@ -217,13 +215,7 @@ pub(crate) unsafe fn any_raw_internal() -> T { #[inline(never)] #[allow(dead_code)] fn any_raw_inner() -> T { - // while we could use `unreachable!()` or `panic!()` as the body of this - // function, both cause Kani to produce a warning on any program that uses - // kani::any() (see https://github.com/model-checking/kani/issues/2010). - // This function is handled via a hook anyway, so we just need to put a body - // that rustc does not complain about. An infinite loop works out nicely. - #[allow(clippy::empty_loop)] - loop {} + kani_intrinsic() } /// Function used to generate panic with a static message as this is the only one currently @@ -239,6 +231,20 @@ pub const fn panic(message: &'static str) -> ! { panic!("{}", message) } +/// An empty body that can be used to define Kani intrinsic functions. +/// +/// A Kani intrinsic is a function that is interpreted by Kani compiler. +/// While we could use `unreachable!()` or `panic!()` as the body of a kani intrinsic +/// function, both cause Kani to produce a warning since we don't support caller location. +/// (see https://github.com/model-checking/kani/issues/2010). +/// +/// This function is dead, since its caller is always handled via a hook anyway, +/// so we just need to put a body that rustc does not complain about. +/// An infinite loop works out nicely. +fn kani_intrinsic() -> T { + #[allow(clippy::empty_loop)] + loop {} +} /// A macro to check if a condition is satisfiable at a specific location in the /// code. /// diff --git a/library/kani/src/mem.rs b/library/kani/src/mem.rs new file mode 100644 index 000000000000..203eae2229c4 --- /dev/null +++ b/library/kani/src/mem.rs @@ -0,0 +1,282 @@ +// 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. +//! 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. + +use crate::kani_intrinsic; +use crate::mem::private::Internal; +use std::mem::{align_of, size_of}; +use std::ptr::{DynMetadata, NonNull, Pointee}; + +/// Assert that the pointer is valid for access according to [crate::mem] conditions 1, 2 and 3. +/// +/// Note that an unaligned pointer is still considered valid. +/// +/// TODO: Kani should automatically add those checks when a de-reference happens. +/// https://github.com/model-checking/kani/issues/2975 +/// +/// This function will either panic or return `true`. This is to make it easier to use it in +/// contracts. +#[crate::unstable( + feature = "mem-predicates", + issue = 2690, + reason = "experimental memory predicate API" +)] +pub fn assert_valid_ptr(ptr: *const T) -> bool +where + T: ?Sized, + ::Metadata: PtrProperties, +{ + crate::assert(!ptr.is_null(), "Expected valid pointer, but found `null`"); + + let (thin_ptr, metadata) = ptr.to_raw_parts(); + can_read(&metadata, thin_ptr) +} + +fn can_read(metadata: &M, data_ptr: *const ()) -> bool +where + M: PtrProperties, + T: ?Sized, +{ + let marker = Internal; + let sz = metadata.pointee_size(marker); + if metadata.dangling(marker) as *const _ == data_ptr { + crate::assert(sz == 0, "Dangling pointer is only valid for zero-sized access") + } else { + crate::assert( + is_read_ok(data_ptr, sz), + "Expected valid pointer, but found dangling pointer", + ); + } + true +} + +mod private { + /// Define like this to restrict usage of PtrProperties functions outside Kani. + #[derive(Copy, Clone)] + pub struct Internal; +} + +/// Trait that allow us to extract information from pointers without de-referencing them. +#[doc(hidden)] +pub trait PtrProperties { + fn pointee_size(&self, _: Internal) -> usize; + + fn min_alignment(&self, _: Internal) -> usize; + + fn dangling(&self, _: Internal) -> *const (); +} + +/// Get the information for sized types (they don't have metadata). +impl PtrProperties for () { + fn pointee_size(&self, _: Internal) -> usize { + size_of::() + } + + fn min_alignment(&self, _: Internal) -> usize { + align_of::() + } + + fn dangling(&self, _: Internal) -> *const () { + NonNull::::dangling().as_ptr() as *const _ + } +} + +/// Get the information from the str metadata. +impl PtrProperties for usize { + #[inline(always)] + fn pointee_size(&self, _: Internal) -> usize { + *self + } + + /// String slices are a UTF-8 representation of characters that have the same layout as slices + /// of type [u8]. + /// + fn min_alignment(&self, _: Internal) -> usize { + align_of::() + } + + fn dangling(&self, _: Internal) -> *const () { + NonNull::::dangling().as_ptr() as _ + } +} + +/// Get the information from the slice metadata. +impl PtrProperties<[T]> for usize { + fn pointee_size(&self, _: Internal) -> usize { + *self * size_of::() + } + + fn min_alignment(&self, _: Internal) -> usize { + align_of::() + } + + fn dangling(&self, _: Internal) -> *const () { + NonNull::::dangling().as_ptr() as _ + } +} + +/// Get the information from the vtable. +impl PtrProperties for DynMetadata +where + T: ?Sized, +{ + fn pointee_size(&self, _: Internal) -> usize { + self.size_of() + } + + fn min_alignment(&self, _: Internal) -> usize { + self.align_of() + } + + fn dangling(&self, _: Internal) -> *const () { + NonNull::<&T>::dangling().as_ptr() as _ + } +} + +/// Check if the pointer `_ptr` contains an allocated address of size equal or greater than `_size`. +/// +/// This function should only be called to ensure a pointer is valid. The opposite isn't true. +/// I.e.: This function always returns `true` if the pointer is valid. +/// Otherwise, it returns non-det boolean. +#[rustc_diagnostic_item = "KaniIsReadOk"] +#[inline(never)] +fn is_read_ok(_ptr: *const (), _size: usize) -> bool { + kani_intrinsic() +} + +#[cfg(test)] +mod tests { + use super::{assert_valid_ptr, PtrProperties}; + use crate::mem::private::Internal; + use std::fmt::Debug; + use std::intrinsics::size_of; + use std::mem::{align_of, align_of_val, size_of_val}; + use std::ptr; + use std::ptr::{NonNull, Pointee}; + + fn size_of_t(ptr: *const T) -> usize + where + T: ?Sized, + ::Metadata: PtrProperties, + { + let (_, metadata) = ptr.to_raw_parts(); + metadata.pointee_size(Internal) + } + + fn align_of_t(ptr: *const T) -> usize + where + T: ?Sized, + ::Metadata: PtrProperties, + { + let (_, metadata) = ptr.to_raw_parts(); + metadata.min_alignment(Internal) + } + + #[test] + fn test_size_of() { + assert_eq!(size_of_t("hi"), size_of_val("hi")); + assert_eq!(size_of_t(&0u8), size_of_val(&0u8)); + assert_eq!(size_of_t(&0u8 as *const dyn std::fmt::Display), size_of_val(&0u8)); + assert_eq!(size_of_t(&[0u8, 1u8] as &[u8]), size_of_val(&[0u8, 1u8])); + assert_eq!(size_of_t(&[] as &[u8]), size_of_val::<[u8; 0]>(&[])); + assert_eq!( + size_of_t(NonNull::::dangling().as_ptr() as *const dyn std::fmt::Display), + size_of::() + ); + } + + #[test] + fn test_alignment() { + assert_eq!(align_of_t("hi"), align_of_val("hi")); + assert_eq!(align_of_t(&0u8), align_of_val(&0u8)); + assert_eq!(align_of_t(&0u32 as *const dyn std::fmt::Display), align_of_val(&0u32)); + assert_eq!(align_of_t(&[0isize, 1isize] as &[isize]), align_of_val(&[0isize, 1isize])); + assert_eq!(align_of_t(&[] as &[u8]), align_of_val::<[u8; 0]>(&[])); + assert_eq!( + align_of_t(NonNull::::dangling().as_ptr() as *const dyn std::fmt::Display), + align_of::() + ); + } + + #[test] + pub fn test_empty_slice() { + let slice_ptr = Vec::::new().as_slice() as *const [char]; + assert_valid_ptr(slice_ptr); + } + + #[test] + pub fn test_empty_str() { + let slice_ptr = String::new().as_str() as *const str; + assert_valid_ptr(slice_ptr); + } + + #[test] + fn test_dangling_zst() { + test_dangling_of_t::<()>(); + test_dangling_of_t::<[(); 10]>(); + } + + fn test_dangling_of_t() { + let dangling: *const T = NonNull::::dangling().as_ptr(); + assert_valid_ptr(dangling); + + let vec_ptr = Vec::::new().as_ptr(); + assert_valid_ptr(vec_ptr); + } + + #[test] + #[should_panic(expected = "Dangling pointer is only valid for zero-sized access")] + fn test_dangling_char() { + test_dangling_of_t::(); + } + + #[test] + #[should_panic(expected = "Dangling pointer is only valid for zero-sized access")] + fn test_dangling_slice() { + test_dangling_of_t::<&str>(); + } + + #[test] + #[should_panic(expected = "Expected valid pointer, but found `null`")] + fn test_null_fat_ptr() { + assert_valid_ptr(ptr::null::() as *const dyn Debug); + } + + #[test] + #[should_panic(expected = "Expected valid pointer, but found `null`")] + fn test_null_char() { + assert_valid_ptr(ptr::null::()); + } +} diff --git a/scripts/kani-regression.sh b/scripts/kani-regression.sh index 20782abe5cbc..3b029c475d5f 100755 --- a/scripts/kani-regression.sh +++ b/scripts/kani-regression.sh @@ -41,7 +41,8 @@ cargo test -p cprover_bindings cargo test -p kani-compiler cargo test -p kani-driver cargo test -p kani_metadata -cargo test -p kani --lib # skip doc tests. +# skip doc tests and enable assertions to fail +cargo test -p kani --lib --features concrete_playback # Test the actual macros, skipping doc tests and enabling extra traits for "syn" # so we can debug print AST RUSTFLAGS=--cfg=kani_sysroot cargo test -p kani_macros --features syn/extra-traits --lib diff --git a/tests/expected/function-contract/valid_ptr.expected b/tests/expected/function-contract/valid_ptr.expected new file mode 100644 index 000000000000..a9dc8dd5992d --- /dev/null +++ b/tests/expected/function-contract/valid_ptr.expected @@ -0,0 +1,11 @@ +Checking harness pre_condition::harness_invalid_ptr... +Failed Checks: Dangling pointer is only valid for zero-sized access + +Checking harness pre_condition::harness_stack_ptr... +VERIFICATION:- SUCCESSFUL + +Checking harness pre_condition::harness_head_ptr... +VERIFICATION:- SUCCESSFUL + +Verification failed for - pre_condition::harness_invalid_ptr +Complete - 2 successfully verified harnesses, 1 failures, 3 total diff --git a/tests/expected/function-contract/valid_ptr.rs b/tests/expected/function-contract/valid_ptr.rs new file mode 100644 index 000000000000..da212c7fa63e --- /dev/null +++ b/tests/expected/function-contract/valid_ptr.rs @@ -0,0 +1,61 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts -Zmem-predicates + +//! Test that it is sound to use `assert_valid_ptr` inside a contract pre-condition. +//! We cannot validate post-condition yet. This can be done once we fix: +//! +#![feature(pointer_is_aligned)] + +mod pre_condition { + /// This contract should succeed only if the input is a valid pointer. + #[kani::requires(kani::mem::assert_valid_ptr(ptr) && ptr.is_aligned())] + unsafe fn read_ptr(ptr: *const i32) -> i32 { + *ptr + } + + #[kani::proof_for_contract(read_ptr)] + fn harness_head_ptr() { + let boxed = Box::new(10); + let raw_ptr = Box::into_raw(boxed); + assert_eq!(unsafe { read_ptr(raw_ptr) }, 10); + let _ = unsafe { Box::from_raw(raw_ptr) }; + } + + #[kani::proof_for_contract(read_ptr)] + fn harness_stack_ptr() { + let val = -20; + assert_eq!(unsafe { read_ptr(&val) }, -20); + } + + #[kani::proof_for_contract(read_ptr)] + fn harness_invalid_ptr() { + let ptr = std::ptr::NonNull::::dangling().as_ptr(); + assert_eq!(unsafe { read_ptr(ptr) }, -20); + } +} + +/// TODO: Enable once we fix: +#[cfg(not_supported)] +mod post_condition { + + /// This contract should fail since we are creating a dangling pointer. + #[kani::ensures(kani::mem::assert_valid_ptr(result.0))] + unsafe fn new_invalid_ptr() -> PtrWrapper { + let var = 'c'; + PtrWrapper(&var as *const _) + } + + #[kani::proof_for_contract(new_invalid_ptr)] + fn harness() { + let _ = unsafe { new_invalid_ptr() }; + } + + struct PtrWrapper(*const T); + + impl kani::Arbitrary for PtrWrapper { + fn any() -> Self { + unreachable!("Do not invoke stubbing") + } + } +} diff --git a/tests/kani/MemPredicates/fat_ptr_validity.rs b/tests/kani/MemPredicates/fat_ptr_validity.rs new file mode 100644 index 000000000000..51d784f06566 --- /dev/null +++ b/tests/kani/MemPredicates/fat_ptr_validity.rs @@ -0,0 +1,69 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z mem-predicates +//! Check that Kani's memory predicates work for fat pointers. + +extern crate kani; + +use kani::mem::assert_valid_ptr; + +mod valid_access { + use super::*; + #[kani::proof] + pub fn check_valid_dyn_ptr() { + let boxed: Box> = Box::new(10u8); + let raw_ptr = Box::into_raw(boxed); + assert_valid_ptr(raw_ptr); + let boxed = unsafe { Box::from_raw(raw_ptr) }; + assert_ne!(*boxed, 0); + } + + #[kani::proof] + pub fn check_valid_slice_ptr() { + let array = ['a', 'b', 'c']; + let slice = &array as *const [char]; + assert_valid_ptr(slice); + assert_eq!(unsafe { &*slice }[0], 'a'); + assert_eq!(unsafe { &*slice }[2], 'c'); + } + + #[kani::proof] + pub fn check_valid_zst() { + let slice_ptr = Vec::::new().as_slice() as *const [char]; + assert_valid_ptr(slice_ptr); + + let str_ptr = String::new().as_str() as *const str; + assert_valid_ptr(str_ptr); + } +} + +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_valid_ptr(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_valid_ptr(raw_ptr); + } + + #[kani::proof] + #[kani::should_panic] + pub fn check_invalid_slice_len() { + let array = [10usize; 10]; + let invalid: *const [usize; 11] = &array as *const [usize; 10] as *const [usize; 11]; + let ptr: *const [usize] = invalid as *const _; + assert_valid_ptr(ptr); + } + + unsafe fn new_dead_ptr(val: T) -> *const T { + let local = val; + &local as *const _ + } +} diff --git a/tests/kani/MemPredicates/thin_ptr_validity.rs b/tests/kani/MemPredicates/thin_ptr_validity.rs new file mode 100644 index 000000000000..638b7e1d1fc8 --- /dev/null +++ b/tests/kani/MemPredicates/thin_ptr_validity.rs @@ -0,0 +1,60 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z mem-predicates +//! Check that Kani's memory predicates work for thin pointers. + +extern crate kani; + +use kani::mem::assert_valid_ptr; +use std::ptr::NonNull; + +mod valid_access { + use super::*; + #[kani::proof] + pub fn check_dangling_zst() { + let dangling = NonNull::<()>::dangling().as_ptr(); + assert_valid_ptr(dangling); + + let vec_ptr = Vec::<()>::new().as_ptr(); + assert_valid_ptr(vec_ptr); + + let dangling = NonNull::<[char; 0]>::dangling().as_ptr(); + assert_valid_ptr(dangling); + } + + #[kani::proof] + pub fn check_valid_array() { + let array = ['a', 'b', 'c']; + assert_valid_ptr(&array); + } +} + +mod invalid_access { + use super::*; + #[kani::proof] + #[kani::should_panic] + pub fn check_invalid_ptr() { + let raw_ptr = unsafe { new_dead_ptr::(0) }; + assert_valid_ptr(raw_ptr); + } + + #[kani::proof] + #[kani::should_panic] + pub fn check_invalid_array() { + let raw_ptr = unsafe { new_dead_ptr::<[char; 2]>(['a', 'b']) }; + assert_valid_ptr(raw_ptr); + } + + #[kani::proof] + #[kani::should_panic] + pub fn check_invalid_zst() { + let raw_ptr: *const [char; 0] = + unsafe { new_dead_ptr::<[char; 2]>(['a', 'b']) } as *const _; + assert_valid_ptr(raw_ptr); + } + + unsafe fn new_dead_ptr(val: T) -> *const T { + let local = val; + &local as *const _ + } +} From 6dfe0a0eb9a677c8832b651c041f0353bfb8cc88 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Wed, 13 Mar 2024 18:01:34 -0700 Subject: [PATCH 034/225] Add a multi-crate test for #3061 (#3076) This is a follow-up on #3063 that adds a test with multiple crates to make sure this scenario is correctly handled and that Kani reports the bug. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- tests/cargo-kani/storage-markers/Cargo.toml | 5 +++++ .../storage-markers/crate-with-bug/Cargo.toml | 8 ++++++++ .../storage-markers/crate-with-bug/src/lib.rs | 12 ++++++++++++ .../storage-markers/crate-with-harness/Cargo.toml | 9 +++++++++ .../crate-with-harness/call_fn_with_bug.expected | 3 +++ .../storage-markers/crate-with-harness/src/lib.rs | 11 +++++++++++ 6 files changed, 48 insertions(+) create mode 100644 tests/cargo-kani/storage-markers/Cargo.toml create mode 100644 tests/cargo-kani/storage-markers/crate-with-bug/Cargo.toml create mode 100644 tests/cargo-kani/storage-markers/crate-with-bug/src/lib.rs create mode 100644 tests/cargo-kani/storage-markers/crate-with-harness/Cargo.toml create mode 100644 tests/cargo-kani/storage-markers/crate-with-harness/call_fn_with_bug.expected create mode 100644 tests/cargo-kani/storage-markers/crate-with-harness/src/lib.rs diff --git a/tests/cargo-kani/storage-markers/Cargo.toml b/tests/cargo-kani/storage-markers/Cargo.toml new file mode 100644 index 000000000000..cb98b6df5835 --- /dev/null +++ b/tests/cargo-kani/storage-markers/Cargo.toml @@ -0,0 +1,5 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +[workspace] +members = ["crate-with-bug", "crate-with-harness"] +resolver = "2" diff --git a/tests/cargo-kani/storage-markers/crate-with-bug/Cargo.toml b/tests/cargo-kani/storage-markers/crate-with-bug/Cargo.toml new file mode 100644 index 000000000000..3cec8dac16b0 --- /dev/null +++ b/tests/cargo-kani/storage-markers/crate-with-bug/Cargo.toml @@ -0,0 +1,8 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +[package] +name = "crate-with-bug" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/tests/cargo-kani/storage-markers/crate-with-bug/src/lib.rs b/tests/cargo-kani/storage-markers/crate-with-bug/src/lib.rs new file mode 100644 index 000000000000..3e6967bdbfe4 --- /dev/null +++ b/tests/cargo-kani/storage-markers/crate-with-bug/src/lib.rs @@ -0,0 +1,12 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// This function contains a use-after-free bug. + +pub fn fn_with_bug() -> i32 { + let raw_ptr = { + let var = 10; + &var as *const i32 + }; + unsafe { *raw_ptr } +} diff --git a/tests/cargo-kani/storage-markers/crate-with-harness/Cargo.toml b/tests/cargo-kani/storage-markers/crate-with-harness/Cargo.toml new file mode 100644 index 000000000000..7cfe7126d845 --- /dev/null +++ b/tests/cargo-kani/storage-markers/crate-with-harness/Cargo.toml @@ -0,0 +1,9 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +[package] +name = "crate-with-harness" +version = "0.1.0" +edition = "2021" + +[dependencies] +crate-with-bug = { path = "../crate-with-bug" } diff --git a/tests/cargo-kani/storage-markers/crate-with-harness/call_fn_with_bug.expected b/tests/cargo-kani/storage-markers/crate-with-harness/call_fn_with_bug.expected new file mode 100644 index 000000000000..68ef53cc06ec --- /dev/null +++ b/tests/cargo-kani/storage-markers/crate-with-harness/call_fn_with_bug.expected @@ -0,0 +1,3 @@ +Status: FAILURE\ +Description: "dereference failure: dead object"\ +in function crate_with_bug::fn_with_bug diff --git a/tests/cargo-kani/storage-markers/crate-with-harness/src/lib.rs b/tests/cargo-kani/storage-markers/crate-with-harness/src/lib.rs new file mode 100644 index 000000000000..a44cfaf976be --- /dev/null +++ b/tests/cargo-kani/storage-markers/crate-with-harness/src/lib.rs @@ -0,0 +1,11 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// This test checks that Kani captures the case of a use-after-free issue as +// described in https://github.com/model-checking/kani/issues/3061 even across +// crates. The test calls a function from another crate that has the bug. + +#[kani::proof] +pub fn call_fn_with_bug() { + let _x = crate_with_bug::fn_with_bug(); +} From a52564d0cfa264f2c314660a7f6dd18c610b75ff Mon Sep 17 00:00:00 2001 From: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> Date: Thu, 14 Mar 2024 18:00:42 -0400 Subject: [PATCH 035/225] Revert clap from arg parsing during setup (#3078) `--help` and `--version` commands were being overriden by clap, so we are going back to the simple parsing with the added logic for `use-local-toolchain`. 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 | 76 +++------------------------------------------ Cargo.toml | 1 - src/lib.rs | 91 ++++++++++++++++++++++++++++-------------------------- 3 files changed, 52 insertions(+), 116 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7009622b1733..89c60b06eec7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,15 +33,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anstream" version = "0.6.13" @@ -96,17 +87,6 @@ version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -145,7 +125,7 @@ version = "0.48.0" dependencies = [ "anyhow", "cargo_metadata", - "clap 4.5.2", + "clap", "which", ] @@ -187,21 +167,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim 0.8.0", - "textwrap", - "unicode-width", - "vec_map", -] - [[package]] name = "clap" version = "4.5.2" @@ -221,7 +186,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.0", + "strsim", ] [[package]] @@ -427,15 +392,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "home" version = "0.5.9" @@ -481,7 +437,7 @@ dependencies = [ name = "kani-compiler" version = "0.48.0" dependencies = [ - "clap 4.5.2", + "clap", "cprover_bindings", "home", "itertools", @@ -504,7 +460,7 @@ version = "0.48.0" dependencies = [ "anyhow", "cargo_metadata", - "clap 4.5.2", + "clap", "comfy-table", "console", "glob", @@ -531,7 +487,6 @@ name = "kani-verifier" version = "0.48.0" dependencies = [ "anyhow", - "clap 2.34.0", "home", "os_info", ] @@ -550,7 +505,7 @@ dependencies = [ name = "kani_metadata" version = "0.48.0" dependencies = [ - "clap 4.5.2", + "clap", "cprover_bindings", "serde", "strum 0.26.2", @@ -1095,12 +1050,6 @@ dependencies = [ "serde", ] -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "strsim" version = "0.11.0" @@ -1178,15 +1127,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.57" @@ -1365,12 +1305,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index c863ffdc1945..8d397f2b0bd4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ include = ["/src", "/build.rs", "/rust-toolchain.toml", "/LICENSE-*", "/README.m [dependencies] anyhow = "1" home = "0.5" -clap = "2.33.3" os_info = { version = "3", default-features = false } [[bin]] diff --git a/src/lib.rs b/src/lib.rs index 202d46f0e5ac..0015829f61d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,6 @@ mod cmd; mod os_hacks; mod setup; -use clap::{App, Arg, SubCommand}; use std::ffi::OsString; use std::os::unix::prelude::CommandExt; use std::path::{Path, PathBuf}; @@ -60,50 +59,54 @@ enum ArgsResult { /// Parse `args` and decide what to do. fn parse_args(args: Vec) -> ArgsResult { - let matches = App::new("kani") - .subcommand( - SubCommand::with_name("setup") - .arg(Arg::with_name("use-local-bundle").long("use-local-bundle").takes_value(true)) - .arg( - Arg::with_name("use-local-toolchain") - .long("use-local-toolchain") - .takes_value(true), - ), - ) - .subcommand( - SubCommand::with_name("kani").subcommand( - SubCommand::with_name("setup") - .arg( - Arg::with_name("use-local-bundle") - .long("use-local-bundle") - .takes_value(true), - ) - .arg( - Arg::with_name("use-local-toolchain") - .long("use-local-toolchain") - .takes_value(true), - ), - ), - ) - .get_matches_from(args); - - // Default is the behaviour for Kani when cargo-kani/ cargo kani runs on its own and sees that there is no setup done yet - // Explicit is when the user uses the sub-command cargo-kani setup explicitly - if let Some(matches) = matches.subcommand_matches("setup") { - let use_local_toolchain = matches.value_of_os("use-local-toolchain").map(OsString::from); - let use_local_bundle = matches.value_of_os("use-local-bundle").map(OsString::from); - ArgsResult::ExplicitSetup { use_local_bundle, use_local_toolchain } - } else if let Some(matches) = matches.subcommand_matches("kani") { - if let Some(matches) = matches.subcommand_matches("setup") { - let use_local_toolchain = - matches.value_of_os("use-local-toolchain").map(OsString::from); - let use_local_bundle = matches.value_of_os("use-local-bundle").map(OsString::from); - ArgsResult::ExplicitSetup { use_local_bundle, use_local_toolchain } - } else { - ArgsResult::Default + // In an effort to keep our dependencies minimal, we do the bare minimum argument parsing manually. + // `args_ez` makes it easy to do crude arg parsing with match. + let args_ez: Vec> = args.iter().map(|x| x.to_str()).collect(); + // "cargo kani setup" comes in as "cargo-kani kani setup" + // "cargo-kani setup" comes in as "cargo-kani setup" + match &args_ez[..] { + &[_, Some("setup"), Some("--use-local-bundle"), _, Some("--use-local-toolchain"), _] => { + ArgsResult::ExplicitSetup { + use_local_bundle: Some(args[3].clone()), + use_local_toolchain: Some(args[5].clone()), + } + } + &[ + _, + Some("kani"), + Some("setup"), + Some("--use-local-bundle"), + _, + Some("--use-local-toolchain"), + _, + ] => ArgsResult::ExplicitSetup { + use_local_bundle: Some(args[4].clone()), + use_local_toolchain: Some(args[6].clone()), + }, + &[_, Some("setup"), Some("--use-local-bundle"), _] => ArgsResult::ExplicitSetup { + use_local_bundle: Some(args[3].clone()), + use_local_toolchain: None, + }, + &[_, Some("kani"), Some("setup"), Some("--use-local-bundle"), _] => { + ArgsResult::ExplicitSetup { + use_local_bundle: Some(args[4].clone()), + use_local_toolchain: None, + } + } + &[_, Some("setup"), Some("--use-local-toolchain"), _] => ArgsResult::ExplicitSetup { + use_local_bundle: None, + use_local_toolchain: Some(args[3].clone()), + }, + &[_, Some("kani"), Some("setup"), Some("--use-local-toolchain"), _] => { + ArgsResult::ExplicitSetup { + use_local_bundle: None, + use_local_toolchain: Some(args[4].clone()), + } + } + &[_, Some("setup")] | &[_, Some("kani"), Some("setup")] => { + ArgsResult::ExplicitSetup { use_local_bundle: None, use_local_toolchain: None } } - } else { - ArgsResult::Default + _ => ArgsResult::Default, } } From 4a1e1fc9c9d71ff69f7f646813c47dbd090556cc Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Thu, 14 Mar 2024 19:33:45 -0700 Subject: [PATCH 036/225] Upgrade Rust toolchain to 2024-03-14 (#3081) Upgrade Rust toolchain to 2024-03-14 Resolves #3073 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_queries/mod.rs | 18 ------------------ rust-toolchain.toml | 2 +- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/kani-compiler/src/kani_queries/mod.rs b/kani-compiler/src/kani_queries/mod.rs index e918bf1b8a73..d6b37a35f9cb 100644 --- a/kani-compiler/src/kani_queries/mod.rs +++ b/kani-compiler/src/kani_queries/mod.rs @@ -4,7 +4,6 @@ use cbmc::{InternString, InternedString}; use kani_metadata::AssignsContract; -use std::fmt::{Display, Formatter, Write}; use std::{ collections::HashMap, path::PathBuf, @@ -65,20 +64,3 @@ impl QueryDb { self.modifies_contracts.iter() } } - -struct PrintList(I); - -impl + Clone> Display for PrintList { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_char('[')?; - let mut is_first = true; - for e in self.0.clone() { - if is_first { - f.write_str(", ")?; - is_first = false; - } - e.fmt(f)?; - } - f.write_char(']') - } -} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index b6f6e8a63534..5b83049feef5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-03-11" +channel = "nightly-2024-03-14" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From b5ddbb8272ff1dc7068d43f0c75686445bfa051b Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Mon, 18 Mar 2024 06:22:20 -0700 Subject: [PATCH 037/225] Disable removal of storage markers (#3083) Instead of turning on the address sanitizer to ensure storage markers (`StorageLive` and `StorageDead`) are kept in MIR, directly disable the MIR pass that removes them from MIR (https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp/topic/.E2.9C.94.20Keeping.20MIR's.20storage.20markers). This is to ensure we don't get additional instrumentation that is not relevant for Kani, and may unnecessarily increase the code size and compilation time. 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_single_file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index fc41bac69d9c..4e8086e7e37b 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -123,7 +123,7 @@ impl KaniSession { "-Z", "panic_abort_tests=yes", "-Z", - "sanitizer=address", + "mir-enable-passes=-RemoveStorageMarkers", ] .map(OsString::from), ); From cffa07ce8a463e7934c42dc5a45270ebe2687cd1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 15:33:07 +0000 Subject: [PATCH 038/225] Automatic cargo update to 2024-03-18 (#3086) Dependency upgrade resulting from `cargo update`. --- Cargo.lock | 70 +++++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 89c60b06eec7..d0b86eaf9206 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,9 +83,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" [[package]] name = "autocfg" @@ -169,9 +169,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.2" +version = "4.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651" +checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" dependencies = [ "clap_builder", "clap_derive", @@ -191,14 +191,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -392,6 +392,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "home" version = "0.5.9" @@ -498,7 +504,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -670,12 +676,12 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "os_info" -version = "3.7.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" +checksum = "6cbb46d5d01695d7a1fb8be5f0d1968bd2b2b8ba1d1b3e7062ce2a0593e57af1" dependencies = [ "log", - "winapi", + "windows-sys", ] [[package]] @@ -751,9 +757,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -966,7 +972,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1000,9 +1006,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.32" +version = "0.9.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" +checksum = "a0623d197252096520c6f2a5e1171ee436e5af99a5d7caa2891e55e61950e6d9" dependencies = [ "indexmap", "itoa", @@ -1074,11 +1080,11 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1087,11 +1093,11 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1106,9 +1112,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" dependencies = [ "proc-macro2", "quote", @@ -1129,22 +1135,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1210,7 +1216,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1289,9 +1295,9 @@ checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "unsafe-libyaml" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "utf8parse" @@ -1529,5 +1535,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] From f54b9564ea07b71a69c4f5091ba2f077ecc0ce93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 11:39:55 -0700 Subject: [PATCH 039/225] Bump tests/perf/s2n-quic from `1a7faa8` to `9e39ca0` (#3087) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `1a7faa8` to `9e39ca0`.
Commits
  • 9e39ca0 build(deps): update heck requirement from 0.4 to 0.5 (#2152)
  • 2d4df73 feat(s2n-quic-xdp): add umem hugepage option (#2145)
  • 440f4f4 build(deps): bump docker/setup-buildx-action from 3.1.0 to 3.2.0 (#2153)
  • d73c648 build(deps): bump docker/login-action from 3.0.0 to 3.1.0 (#2154)
  • 21e2bbb chore(s2n-quic): release 1.34.0 (#2151)
  • 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 1a7faa87ebcf..9e39ca0f4aa0 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 1a7faa87ebcf2b84a068f21af3306a6f42eb8c74 +Subproject commit 9e39ca0f4aa00ffacc9d65879ccc807460c8b04b From 54786ad85bcae8b80948575900e085e6ed035ab8 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Mon, 18 Mar 2024 14:05:36 -0700 Subject: [PATCH 040/225] Upgrade toolchain to nightly-2024-03-15 (#3084) Note that with this upgrade Kani can no longer automatically detect loop bounds when using range. For more details, see: https://github.com/model-checking/kani/issues/3088 --- docs/src/getting-started/verification-results/src/main.rs | 1 + rust-toolchain.toml | 2 +- tests/coverage/unreachable/abort/main.rs | 2 +- tests/expected/abort/main.rs | 1 + tests/expected/iterator/main.rs | 1 + tests/kani/Coroutines/main.rs | 1 + 6 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/src/getting-started/verification-results/src/main.rs b/docs/src/getting-started/verification-results/src/main.rs index 7a03b34f0f9e..72653cf4dc8f 100644 --- a/docs/src/getting-started/verification-results/src/main.rs +++ b/docs/src/getting-started/verification-results/src/main.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT #[kani::proof] +#[kani::unwind(4)] // ANCHOR: success_example fn success_example() { let mut sum = 0; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5b83049feef5..7035d1aba20f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-03-14" +channel = "nightly-2024-03-15" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/coverage/unreachable/abort/main.rs b/tests/coverage/unreachable/abort/main.rs index 2941ec126f3c..39c0b0efb54f 100644 --- a/tests/coverage/unreachable/abort/main.rs +++ b/tests/coverage/unreachable/abort/main.rs @@ -5,7 +5,7 @@ use std::process; -#[kani::proof] +#[cfg_attr(kani, kani::proof, kani::unwind(5))] fn main() { for i in 0..4 { if i == 1 { diff --git a/tests/expected/abort/main.rs b/tests/expected/abort/main.rs index 2941ec126f3c..9e2f5b7a808c 100644 --- a/tests/expected/abort/main.rs +++ b/tests/expected/abort/main.rs @@ -6,6 +6,7 @@ use std::process; #[kani::proof] +#[kani::unwind(5)] fn main() { for i in 0..4 { if i == 1 { diff --git a/tests/expected/iterator/main.rs b/tests/expected/iterator/main.rs index b1cb4a89cfbf..5cf9402bcb23 100644 --- a/tests/expected/iterator/main.rs +++ b/tests/expected/iterator/main.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT #[kani::proof] +#[kani::unwind(4)] fn main() { let mut z = 1; for i in 1..4 { diff --git a/tests/kani/Coroutines/main.rs b/tests/kani/Coroutines/main.rs index 10d92571aaa6..14cbeb426321 100644 --- a/tests/kani/Coroutines/main.rs +++ b/tests/kani/Coroutines/main.rs @@ -9,6 +9,7 @@ use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; #[kani::proof] +#[kani::unwind(3)] fn main() { let mut add_one = |mut resume: u8| { loop { From ec2b8b3021ee9c25747f47643db342a4e1b8baa1 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 20 Mar 2024 07:21:54 +0100 Subject: [PATCH 041/225] Add optional scatterplot to benchcomp output (#3077) Scatterplots should make it easier to immediately spot performance trends (and indeed any differences) rather than having to process a (large) number of table rows. Uses mermaid-js to produce markdown-embedded plots that will display on the job summary page. Scatterplots are not directly supported by mermaid-js at this point (xycharts only do line or bar charts), so quadrant plots are employed with various diagram items drawn in white to make them disappear. --- tests/perf/s2n-quic | 2 +- .../benchcomp/visualizers/__init__.py | 126 +++++++++++++++++- tools/benchcomp/configs/perf-regression.yaml | 1 + tools/benchcomp/test/test_regression.py | 16 +++ 4 files changed, 138 insertions(+), 7 deletions(-) diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 9e39ca0f4aa0..1a7faa87ebcf 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 9e39ca0f4aa00ffacc9d65879ccc807460c8b04b +Subproject commit 1a7faa87ebcf2b84a068f21af3306a6f42eb8c74 diff --git a/tools/benchcomp/benchcomp/visualizers/__init__.py b/tools/benchcomp/benchcomp/visualizers/__init__.py index f9987832ad62..0215ebd02bdc 100644 --- a/tools/benchcomp/benchcomp/visualizers/__init__.py +++ b/tools/benchcomp/benchcomp/visualizers/__init__.py @@ -3,8 +3,10 @@ import dataclasses +import enum import json import logging +import math import subprocess import sys import textwrap @@ -125,11 +127,21 @@ def __call__(self, results): +class Plot(enum.Enum): + """Scatterplot configuration options + """ + OFF = 1 + LINEAR = 2 + LOG = 3 + + + class dump_markdown_results_table: """Print Markdown-formatted tables displaying benchmark results For each metric, this visualization prints out a table of benchmarks, - showing the value of the metric for each variant. + showing the value of the metric for each variant, combined with an optional + scatterplot. The 'out_file' key is mandatory; specify '-' to print to stdout. @@ -145,12 +157,16 @@ class dump_markdown_results_table: particular combinations of values for different variants, such as regressions or performance improvements. + 'scatterplot' takes the values 'off' (default), 'linear' (linearly scaled + axes), or 'log' (logarithmically scaled axes). + Sample configuration: ``` visualize: - type: dump_markdown_results_table out_file: "-" + scatterplot: linear extra_columns: runtime: - column_name: ratio @@ -187,9 +203,10 @@ class dump_markdown_results_table: """ - def __init__(self, out_file, extra_columns=None): + def __init__(self, out_file, extra_columns=None, scatterplot=None): self.get_out_file = benchcomp.Outfile(out_file) self.extra_columns = self._eval_column_text(extra_columns or {}) + self.scatterplot = self._parse_scatterplot_config(scatterplot) @staticmethod @@ -206,12 +223,48 @@ def _eval_column_text(column_spec): return column_spec + @staticmethod + def _parse_scatterplot_config(scatterplot_config_string): + if (scatterplot_config_string is None or + scatterplot_config_string == "off"): + return Plot.OFF + elif scatterplot_config_string == "linear": + return Plot.LINEAR + elif scatterplot_config_string == "log": + return Plot.LOG + else: + logging.error( + "Invalid scatterplot configuration '%s'", + scatterplot_config_string) + sys.exit(1) + + @staticmethod def _get_template(): return textwrap.dedent("""\ {% for metric, benchmarks in d["metrics"].items() %} ## {{ metric }} + {% if scatterplot and metric in d["scaled_metrics"] and d["scaled_variants"][metric]|length == 2 -%} + ```mermaid + %%{init: { "quadrantChart": { "titlePadding": 15, "xAxisLabelPadding": 20, "yAxisLabelPadding": 20, "quadrantLabelFontSize": 0, "pointRadius": 2, "pointLabelFontSize": 2 }, "themeVariables": { "quadrant1Fill": "#FFFFFF", "quadrant2Fill": "#FFFFFF", "quadrant3Fill": "#FFFFFF", "quadrant4Fill": "#FFFFFF", "quadrant1TextFill": "#FFFFFF", "quadrant2TextFill": "#FFFFFF", "quadrant3TextFill": "#FFFFFF", "quadrant4TextFill": "#FFFFFF", "quadrantInternalBorderStrokeFill": "#FFFFFF" } } }%% + quadrantChart + title {{ metric }} + x-axis {{ d["scaled_variants"][metric][0] }} + y-axis {{ d["scaled_variants"][metric][1] }} + quadrant-1 1 + quadrant-2 2 + quadrant-3 3 + quadrant-4 4 + {%- for bench_name, bench_variants in d["scaled_metrics"][metric]["benchmarks"].items () %} + {% set v0 = bench_variants[d["scaled_variants"][metric][0]] -%} + {% set v1 = bench_variants[d["scaled_variants"][metric][1]] -%} + "{{ bench_name }}": [{{ v0|round(3) }}, {{ v1|round(3) }}] + {%- endfor %} + ``` + Scatterplot axis ranges are {{ d["scaled_metrics"][metric]["min_value"] }} (bottom/left) to {{ d["scaled_metrics"][metric]["max_value"] }} (top/right). + + {% endif -%} | Benchmark | {% for variant in d["variants"][metric] %} {{ variant }} |{% endfor %} | --- |{% for variant in d["variants"][metric] %} --- |{% endfor -%} {% for bench_name, bench_variants in benchmarks.items () %} @@ -228,7 +281,48 @@ def _get_variant_names(results): @staticmethod - def _organize_results_into_metrics(results): + def _compute_scaled_metric(data_for_metric, log_scaling): + min_value = math.inf + max_value = -math.inf + for bench, bench_result in data_for_metric.items(): + for variant, variant_result in bench_result.items(): + if isinstance(variant_result, (bool, str)): + return None + if not isinstance(variant_result, (int, float)): + return None + if variant_result < min_value: + min_value = variant_result + if variant_result > max_value: + max_value = variant_result + ret = { + "benchmarks": {bench: {} for bench in data_for_metric.keys()}, + "min_value": "log({})".format(min_value) if log_scaling else min_value, + "max_value": "log({})".format(max_value) if log_scaling else max_value, + } + # 1.0 is not a permissible value for mermaid, so make sure all scaled + # results stay below that by use 0.99 as hard-coded value or + # artificially increasing the range by 10 per cent + if min_value == math.inf or min_value == max_value: + for bench, bench_result in data_for_metric.items(): + ret["benchmarks"][bench] = {variant: 0.99 for variant in bench_result.keys()} + else: + if log_scaling: + min_value = math.log(min_value, 10) + max_value = math.log(max_value, 10) + value_range = max_value - min_value + value_range = value_range * 1.1 + for bench, bench_result in data_for_metric.items(): + for variant, variant_result in bench_result.items(): + if log_scaling: + abs_value = math.log(variant_result, 10) + else: + abs_value = variant_result + ret["benchmarks"][bench][variant] = (abs_value - min_value) / value_range + return ret + + + @staticmethod + def _organize_results_into_metrics(results, log_scaling): ret = {metric: {} for metric in results["metrics"]} for bench, bench_result in results["benchmarks"].items(): for variant, variant_result in bench_result["variants"].items(): @@ -246,7 +340,13 @@ def _organize_results_into_metrics(results): ret[metric][bench] = { variant: variant_result["metrics"][metric] } - return ret + ret_scaled = {} + for metric, bench_result in ret.items(): + scaled = dump_markdown_results_table._compute_scaled_metric( + bench_result, log_scaling) + if scaled is not None: + ret_scaled[metric] = scaled + return (ret, ret_scaled) def _add_extra_columns(self, metrics): @@ -271,13 +371,26 @@ def _get_variants(metrics): return ret + @staticmethod + def _get_scaled_variants(metrics): + ret = {} + for metric, entries in metrics.items(): + for bench, variants in entries["benchmarks"].items(): + ret[metric] = list(variants.keys()) + break + return ret + + def __call__(self, results): - metrics = self._organize_results_into_metrics(results) + (metrics, scaled) = self._organize_results_into_metrics( + results, self.scatterplot == Plot.LOG) self._add_extra_columns(metrics) data = { "metrics": metrics, "variants": self._get_variants(metrics), + "scaled_metrics": scaled, + "scaled_variants": self._get_scaled_variants(scaled), } env = jinja2.Environment( @@ -285,6 +398,7 @@ def __call__(self, results): enabled_extensions=("html"), default_for_string=True)) template = env.from_string(self._get_template()) - output = template.render(d=data)[:-1] + include_scatterplot = self.scatterplot != Plot.OFF + output = template.render(d=data, scatterplot=include_scatterplot)[:-1] with self.get_out_file() as handle: print(output, file=handle) diff --git a/tools/benchcomp/configs/perf-regression.yaml b/tools/benchcomp/configs/perf-regression.yaml index a0d88e0558db..c938b3dd861f 100644 --- a/tools/benchcomp/configs/perf-regression.yaml +++ b/tools/benchcomp/configs/perf-regression.yaml @@ -33,6 +33,7 @@ visualize: - type: dump_markdown_results_table out_file: '-' + scatterplot: linear extra_columns: # For these two metrics, display the difference between old and new and diff --git a/tools/benchcomp/test/test_regression.py b/tools/benchcomp/test/test_regression.py index 87df67a071cc..54cf134eed79 100644 --- a/tools/benchcomp/test/test_regression.py +++ b/tools/benchcomp/test/test_regression.py @@ -436,6 +436,7 @@ def test_markdown_results_table(self): "visualize": [{ "type": "dump_markdown_results_table", "out_file": "-", + "scatterplot": "linear", "extra_columns": { "runtime": [{ "column_name": "ratio", @@ -461,6 +462,21 @@ def test_markdown_results_table(self): run_bc.stdout, textwrap.dedent(""" ## runtime + ```mermaid + %%{init: { "quadrantChart": { "titlePadding": 15, "xAxisLabelPadding": 20, "yAxisLabelPadding": 20, "quadrantLabelFontSize": 0, "pointRadius": 2, "pointLabelFontSize": 2 }, "themeVariables": { "quadrant1Fill": "#FFFFFF", "quadrant2Fill": "#FFFFFF", "quadrant3Fill": "#FFFFFF", "quadrant4Fill": "#FFFFFF", "quadrant1TextFill": "#FFFFFF", "quadrant2TextFill": "#FFFFFF", "quadrant3TextFill": "#FFFFFF", "quadrant4TextFill": "#FFFFFF", "quadrantInternalBorderStrokeFill": "#FFFFFF" } } }%% + quadrantChart + title runtime + x-axis variant_1 + y-axis variant_2 + quadrant-1 1 + quadrant-2 2 + quadrant-3 3 + quadrant-4 4 + "bench_1": [0.0, 0.909] + "bench_2": [0.909, 0.0] + ``` + Scatterplot axis ranges are 5 (bottom/left) to 10 (top/right). + | Benchmark | variant_1 | variant_2 | ratio | | --- | --- | --- | --- | | bench_1 | 5 | 10 | **2.0** | From f8c30d9d5849a8abcef8df4aae11c2bc61f7213f Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 20 Mar 2024 18:05:59 +0100 Subject: [PATCH 042/225] Benchcomp scatterplots: quote axis labels (#3097) Mermaid does not accept `@` characters in axis labels when the labels aren't in quotes. (Seen when trying to run CBMC's upcoming benchcomp suite.) Note: the title must not be quoted, else the quotes themselves will be rendered. --- tools/benchcomp/benchcomp/visualizers/__init__.py | 4 ++-- tools/benchcomp/test/test_regression.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/benchcomp/benchcomp/visualizers/__init__.py b/tools/benchcomp/benchcomp/visualizers/__init__.py index 0215ebd02bdc..8de4a9f130b6 100644 --- a/tools/benchcomp/benchcomp/visualizers/__init__.py +++ b/tools/benchcomp/benchcomp/visualizers/__init__.py @@ -250,8 +250,8 @@ def _get_template(): %%{init: { "quadrantChart": { "titlePadding": 15, "xAxisLabelPadding": 20, "yAxisLabelPadding": 20, "quadrantLabelFontSize": 0, "pointRadius": 2, "pointLabelFontSize": 2 }, "themeVariables": { "quadrant1Fill": "#FFFFFF", "quadrant2Fill": "#FFFFFF", "quadrant3Fill": "#FFFFFF", "quadrant4Fill": "#FFFFFF", "quadrant1TextFill": "#FFFFFF", "quadrant2TextFill": "#FFFFFF", "quadrant3TextFill": "#FFFFFF", "quadrant4TextFill": "#FFFFFF", "quadrantInternalBorderStrokeFill": "#FFFFFF" } } }%% quadrantChart title {{ metric }} - x-axis {{ d["scaled_variants"][metric][0] }} - y-axis {{ d["scaled_variants"][metric][1] }} + x-axis "{{ d["scaled_variants"][metric][0] }}" + y-axis "{{ d["scaled_variants"][metric][1] }}" quadrant-1 1 quadrant-2 2 quadrant-3 3 diff --git a/tools/benchcomp/test/test_regression.py b/tools/benchcomp/test/test_regression.py index 54cf134eed79..c277797162e0 100644 --- a/tools/benchcomp/test/test_regression.py +++ b/tools/benchcomp/test/test_regression.py @@ -466,8 +466,8 @@ def test_markdown_results_table(self): %%{init: { "quadrantChart": { "titlePadding": 15, "xAxisLabelPadding": 20, "yAxisLabelPadding": 20, "quadrantLabelFontSize": 0, "pointRadius": 2, "pointLabelFontSize": 2 }, "themeVariables": { "quadrant1Fill": "#FFFFFF", "quadrant2Fill": "#FFFFFF", "quadrant3Fill": "#FFFFFF", "quadrant4Fill": "#FFFFFF", "quadrant1TextFill": "#FFFFFF", "quadrant2TextFill": "#FFFFFF", "quadrant3TextFill": "#FFFFFF", "quadrant4TextFill": "#FFFFFF", "quadrantInternalBorderStrokeFill": "#FFFFFF" } } }%% quadrantChart title runtime - x-axis variant_1 - y-axis variant_2 + x-axis "variant_1" + y-axis "variant_2" quadrant-1 1 quadrant-2 2 quadrant-3 3 From 54f92679238f9241d696523b1b4f1928ed368bce Mon Sep 17 00:00:00 2001 From: Kareem Khazem Date: Fri, 22 Mar 2024 16:49:14 +0000 Subject: [PATCH 043/225] Expand ${var} in benchcomp variant `env` (#3090) The values of environment variables in the benchcomp configuration file can now contain strings of the form '${var}'. Benchcomp will replace these strings with the value of the environment variable 'var'. This is intended to allow users to have several benchcomp variants, each of which differs only in the environment. This fixes #2981. --- docs/src/benchcomp-conf.md | 20 ++++++++ tools/benchcomp/benchcomp/entry/run.py | 44 ++++++++++++++++- tools/benchcomp/test/test_regression.py | 37 ++++++++++++++ tools/benchcomp/test/test_unit.py | 66 +++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 tools/benchcomp/test/test_unit.py diff --git a/docs/src/benchcomp-conf.md b/docs/src/benchcomp-conf.md index 77236d0917bf..de57ac831221 100644 --- a/docs/src/benchcomp-conf.md +++ b/docs/src/benchcomp-conf.md @@ -4,6 +4,26 @@ This page lists the different visualizations that are available. +## Variants + +A *variant* is a single invocation of a benchmark suite. Benchcomp runs several +variants, so that their performance can be compared later. A variant consists of +a command-line argument, working directory, and environment. Benchcomp invokes +the command using the operating system environment, updated with the keys and +values in `env`. If any values in `env` contain strings of the form `${var}`, +Benchcomp expands them to the value of the environment variable `$var`. + +```yaml +variants: + variant_1: + config: + command_line: echo "Hello, world" + directory: /tmp + env: + PATH: /my/local/directory:${PATH} +``` + + ## Built-in visualizations The following visualizations are available; these can be added to the `visualize` list of `benchcomp.yaml`. diff --git a/tools/benchcomp/benchcomp/entry/run.py b/tools/benchcomp/benchcomp/entry/run.py index a870e7e9a1b0..28457381da2b 100644 --- a/tools/benchcomp/benchcomp/entry/run.py +++ b/tools/benchcomp/benchcomp/entry/run.py @@ -13,6 +13,7 @@ import logging import os import pathlib +import re import shutil import subprocess import typing @@ -53,9 +54,10 @@ def __post_init__(self): else: self.working_copy = pathlib.Path(self.directory) + def __call__(self): - env = dict(os.environ) - env.update(self.env) + update_environment_with = _EnvironmentUpdater() + env = update_environment_with(self.env) if self.copy_benchmarks_dir: shutil.copytree( @@ -128,6 +130,44 @@ def __call__(self): tmp_symlink.rename(self.out_symlink) + +@dataclasses.dataclass +class _EnvironmentUpdater: + """Update the OS environment with keys and values containing variables + + When called, this class returns the operating environment updated with new + keys and values. The values can contain variables of the form '${var_name}'. + The class evaluates those variables using values already in the environment. + """ + + os_environment: dict = dataclasses.field( + default_factory=lambda : dict(os.environ)) + pattern: re.Pattern = re.compile(r"\$\{(\w+?)\}") + + + def _evaluate(self, key, value): + """Evaluate all ${var} in value using self.os_environment""" + old_value = value + + for variable in re.findall(self.pattern, value): + if variable not in self.os_environment: + logging.error( + "Couldn't evaluate ${%s} in the value '%s' for environment " + "variable '%s'. Ensure the environment variable $%s is set", + variable, old_value, key, variable) + sys.exit(1) + value = re.sub( + r"\$\{" + variable + "\}", self.os_environment[variable], value) + return value + + + def __call__(self, new_environment): + ret = dict(self.os_environment) + for key, value in new_environment.items(): + ret[key] = self._evaluate(key, value) + return ret + + def get_default_out_symlink(): return "latest" diff --git a/tools/benchcomp/test/test_regression.py b/tools/benchcomp/test/test_regression.py index c277797162e0..55730bc0463f 100644 --- a/tools/benchcomp/test/test_regression.py +++ b/tools/benchcomp/test/test_regression.py @@ -662,6 +662,43 @@ def test_return_0_on_fail(self): result = yaml.safe_load(handle) + def test_env_expansion(self): + """Ensure that config parser expands '${}' in env key""" + + with tempfile.TemporaryDirectory() as tmp: + run_bc = Benchcomp({ + "variants": { + "env_set": { + "config": { + "command_line": 'echo "$__BENCHCOMP_ENV_VAR" > out', + "directory": tmp, + "env": {"__BENCHCOMP_ENV_VAR": "foo:${PATH}"} + } + }, + }, + "run": { + "suites": { + "suite_1": { + "parser": { + # The word 'bin' typically appears in $PATH, so + # check that what was echoed contains 'bin'. + "command": textwrap.dedent("""\ + grep bin out && grep '^foo:' out && echo '{ + "benchmarks": {}, + "metrics": {} + }' + """) + }, + "variants": ["env_set"] + } + } + }, + "visualize": [], + }) + run_bc() + self.assertEqual(run_bc.proc.returncode, 0, msg=run_bc.stderr) + + def test_env(self): """Ensure that benchcomp reads the 'env' key of variant config""" diff --git a/tools/benchcomp/test/test_unit.py b/tools/benchcomp/test/test_unit.py new file mode 100644 index 000000000000..12320116f217 --- /dev/null +++ b/tools/benchcomp/test/test_unit.py @@ -0,0 +1,66 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# Benchcomp regression testing suite. This suite uses Python's stdlib unittest +# module, but nevertheless actually runs the binary rather than running unit +# tests. + +import unittest +import uuid + +import benchcomp.entry.run + + + +class TestEnvironmentUpdater(unittest.TestCase): + def test_environment_construction(self): + """Test that the default constructor reads the OS environment""" + + update_environment = benchcomp.entry.run._EnvironmentUpdater() + environment = update_environment({}) + self.assertIn("PATH", environment) + + + def test_placeholder_construction(self): + """Test that the placeholder constructor reads the placeholder""" + + key, value = [str(uuid.uuid4()) for _ in range(2)] + update_environment = benchcomp.entry.run._EnvironmentUpdater({ + key: value, + }) + environment = update_environment({}) + self.assertIn(key, environment) + self.assertEqual(environment[key], value) + + + def test_environment_update(self): + """Test that the environment is updated""" + + key, value, update = [str(uuid.uuid4()) for _ in range(3)] + update_environment = benchcomp.entry.run._EnvironmentUpdater({ + key: value, + }) + environment = update_environment({ + key: update + }) + self.assertIn(key, environment) + self.assertEqual(environment[key], update) + + + def test_environment_update_variable(self): + """Test that the environment is updated""" + + old_env = { + "key1": str(uuid.uuid4()), + "key2": str(uuid.uuid4()), + } + + actual_update = "${key2}xxx${key1}" + expected_update = f"{old_env['key2']}xxx{old_env['key1']}" + + update_environment = benchcomp.entry.run._EnvironmentUpdater(old_env) + environment = update_environment({ + "key1": actual_update, + }) + self.assertIn("key1", environment) + self.assertEqual(environment["key1"], expected_update) From 65e520250bcb6a2f18fbef9d2456544b10d10d4f Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Fri, 22 Mar 2024 12:46:27 -0700 Subject: [PATCH 044/225] Add test for #3099 (#3100) Add a reproducer for #3099. --- tests/kani/Spurious/storage_fixme.rs | 652 +++++++++++++++++++++++++++ 1 file changed, 652 insertions(+) create mode 100644 tests/kani/Spurious/storage_fixme.rs diff --git a/tests/kani/Spurious/storage_fixme.rs b/tests/kani/Spurious/storage_fixme.rs new file mode 100644 index 000000000000..51d13f31bcef --- /dev/null +++ b/tests/kani/Spurious/storage_fixme.rs @@ -0,0 +1,652 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Modifications Copyright Kani Contributors +// See GitHub history for details. + +// Our handling of storage markers causes spurious failures in this test. +// https://github.com/model-checking/kani/issues/3099 +// The code is extracted from the implementation of `BTreeMap` which is where we +// originally saw the spurious failures while trying to enable storage markers +// for `std` in https://github.com/model-checking/kani/pull/3080 + +use std::alloc::Layout; +use std::marker::PhantomData; +use std::mem::ManuallyDrop; + +use std::ptr::{self, NonNull}; + +/// This replaces the value behind the `v` unique reference by calling the +/// relevant function, and returns a result obtained along the way. +/// +/// If a panic occurs in the `change` closure, the entire process will be aborted. +#[inline] +fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { + let value = unsafe { ptr::read(v) }; + let (new_value, ret) = change(value); + unsafe { + ptr::write(v, new_value); + } + ret +} + +const B: usize = 6; +const CAPACITY: usize = 2 * B - 1; + +/// The underlying representation of leaf nodes and part of the representation of internal nodes. +struct LeafNode { + /// We want to be covariant in `K` and `V`. + parent: Option>, + + /// This node's index into the parent node's `edges` array. + /// `*node.parent.edges[node.parent_idx]` should be the same thing as `node`. + /// This is only guaranteed to be initialized when `parent` is non-null. + parent_idx: u16, + + /// The number of keys and values this node stores. + len: u16, +} + +impl LeafNode { + /// Creates a new boxed `LeafNode`. + fn new() -> Box { + Box::new(LeafNode { parent: None, parent_idx: 0, len: 0 }) + } +} + +/// The underlying representation of internal nodes. As with `LeafNode`s, these should be hidden +/// behind `BoxedNode`s to prevent dropping uninitialized keys and values. Any pointer to an +/// `InternalNode` can be directly cast to a pointer to the underlying `LeafNode` portion of the +/// node, allowing code to act on leaf and internal nodes generically without having to even check +/// which of the two a pointer is pointing at. This property is enabled by the use of `repr(C)`. +// gdb_providers.py uses this type name for introspection. +struct InternalNode {} + +// N.B. `NodeRef` is always covariant in `K` and `V`, even when the `BorrowType` +// is `Mut`. This is technically wrong, but cannot result in any unsafety due to +// internal use of `NodeRef` because we stay completely generic over `K` and `V`. +// However, whenever a public type wraps `NodeRef`, make sure that it has the +// correct variance. +/// +/// A reference to a node. +/// +/// This type has a number of parameters that controls how it acts: +/// - `BorrowType`: A dummy type that describes the kind of borrow and carries a lifetime. +/// - When this is `Immut<'a>`, the `NodeRef` acts roughly like `&'a Node`. +/// - When this is `ValMut<'a>`, the `NodeRef` acts roughly like `&'a Node` +/// with respect to keys and tree structure, but also allows many +/// mutable references to values throughout the tree to coexist. +/// - When this is `Mut<'a>`, the `NodeRef` acts roughly like `&'a mut Node`, +/// although insert methods allow a mutable pointer to a value to coexist. +/// - When this is `Owned`, the `NodeRef` acts roughly like `Box`, +/// but does not have a destructor, and must be cleaned up manually. +/// - When this is `Dying`, the `NodeRef` still acts roughly like `Box`, +/// but has methods to destroy the tree bit by bit, and ordinary methods, +/// while not marked as unsafe to call, can invoke UB if called incorrectly. +/// Since any `NodeRef` allows navigating through the tree, `BorrowType` +/// effectively applies to the entire tree, not just to the node itself. +/// - `K` and `V`: These are the types of keys and values stored in the nodes. +/// - `Type`: This can be `Leaf`, `Internal`, or `LeafOrInternal`. When this is +/// `Leaf`, the `NodeRef` points to a leaf node, when this is `Internal` the +/// `NodeRef` points to an internal node, and when this is `LeafOrInternal` the +/// `NodeRef` could be pointing to either type of node. +/// `Type` is named `NodeType` when used outside `NodeRef`. +/// +/// Both `BorrowType` and `NodeType` restrict what methods we implement, to +/// exploit static type safety. There are limitations in the way we can apply +/// such restrictions: +/// - For each type parameter, we can only define a method either generically +/// or for one particular type. For example, we cannot define a method like +/// `into_kv` generically for all `BorrowType`, or once for all types that +/// carry a lifetime, because we want it to return `&'a` references. +/// Therefore, we define it only for the least powerful type `Immut<'a>`. +/// - We cannot get implicit coercion from say `Mut<'a>` to `Immut<'a>`. +/// Therefore, we have to explicitly call `reborrow` on a more powerful +/// `NodeRef` in order to reach a method like `into_kv`. +/// +/// All methods on `NodeRef` that return some kind of reference, either: +/// - Take `self` by value, and return the lifetime carried by `BorrowType`. +/// Sometimes, to invoke such a method, we need to call `reborrow_mut`. +/// - Take `self` by reference, and (implicitly) return that reference's +/// lifetime, instead of the lifetime carried by `BorrowType`. That way, +/// the borrow checker guarantees that the `NodeRef` remains borrowed as long +/// as the returned reference is used. +/// The methods supporting insert bend this rule by returning a raw pointer, +/// i.e., a reference without any lifetime. +struct NodeRef { + /// The number of levels that the node and the level of leaves are apart, a + /// constant of the node that cannot be entirely described by `Type`, and that + /// the node itself does not store. We only need to store the height of the root + /// node, and derive every other node's height from it. + /// Must be zero if `Type` is `Leaf` and non-zero if `Type` is `Internal`. + height: usize, + /// The pointer to the leaf or internal node. The definition of `InternalNode` + /// ensures that the pointer is valid either way. + node: NonNull, + _marker: PhantomData<(BorrowType, Type)>, +} + +/// The root node of an owned tree. +/// +/// Note that this does not have a destructor, and must be cleaned up manually. +type Root = NodeRef; + +impl NodeRef { + fn new_leaf() -> Self { + Self::from_new_leaf(LeafNode::new()) + } + + fn from_new_leaf(leaf: Box) -> Self { + NodeRef { height: 0, node: NonNull::from(Box::leak(leaf)), _marker: PhantomData } + } +} + +impl NodeRef { + /// Unpack a node reference that was packed as `NodeRef::parent`. + fn from_internal(node: NonNull, height: usize) -> Self { + debug_assert!(height > 0); + NodeRef { height, node: node.cast(), _marker: PhantomData } + } +} + +impl NodeRef { + /// Finds the length of the node. This is the number of keys or values. + /// The number of edges is `len() + 1`. + /// Note that, despite being safe, calling this function can have the side effect + /// of invalidating mutable references that unsafe code has created. + fn len(&self) -> usize { + // Crucially, we only access the `len` field here. If BorrowType is marker::ValMut, + // there might be outstanding mutable references to values that we must not invalidate. + unsafe { usize::from((*Self::as_leaf_ptr(self)).len) } + } + + /// Exposes the leaf portion of any leaf or internal node. + /// + /// Returns a raw ptr to avoid invalidating other references to this node. + fn as_leaf_ptr(this: &Self) -> *mut LeafNode { + // The node must be valid for at least the LeafNode portion. + // This is not a reference in the NodeRef type because we don't know if + // it should be unique or shared. + this.node.as_ptr() + } +} + +impl NodeRef { + /// Finds the parent of the current node. Returns `Ok(handle)` if the current + /// node actually has a parent, where `handle` points to the edge of the parent + /// that points to the current node. Returns `Err(self)` if the current node has + /// no parent, giving back the original `NodeRef`. + /// + /// The method name assumes you picture trees with the root node on top. + /// + /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should + /// both, upon success, do nothing. + fn ascend(self) -> Result, marker::Edge>, Self> { + // We need to use raw pointers to nodes because, if BorrowType is marker::ValMut, + // there might be outstanding mutable references to values that we must not invalidate. + let leaf_ptr: *const _ = Self::as_leaf_ptr(&self); + unsafe { (*leaf_ptr).parent } + .as_ref() + .map(|parent| Handle { + node: NodeRef::from_internal(*parent, self.height + 1), + idx: unsafe { usize::from((*leaf_ptr).parent_idx) }, + _marker: PhantomData, + }) + .ok_or(self) + } + + fn first_edge(self) -> Handle { + unsafe { Handle::new_edge(self, 0) } + } +} + +impl NodeRef { + /// Similar to `ascend`, gets a reference to a node's parent node, but also + /// deallocates the current node in the process. This is unsafe because the + /// current node will still be accessible despite being deallocated. + unsafe fn deallocate_and_ascend( + self, + ) -> Option, marker::Edge>> { + let height = self.height; + let node = self.node; + let ret = self.ascend().ok(); + unsafe { + std::alloc::dealloc( + node.as_ptr() as *mut u8, + if height > 0 { Layout::new::() } else { Layout::new::() }, + ); + } + ret + } +} + +impl<'a, Type> NodeRef, Type> { + /// Borrows exclusive access to the leaf portion of a leaf or internal node. + fn as_leaf_mut(&mut self) -> &mut LeafNode { + let ptr = Self::as_leaf_ptr(self); + // SAFETY: we have exclusive access to the entire node. + unsafe { &mut *ptr } + } +} + +impl<'a, Type> NodeRef, Type> { + /// Borrows exclusive access to the length of the node. + fn len_mut(&mut self) -> &mut u16 { + &mut self.as_leaf_mut().len + } +} + +impl NodeRef { + /// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe + /// because the return value cannot be used to destroy the root, and there + /// cannot be other references to the tree. + fn borrow_mut(&mut self) -> NodeRef, Type> { + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } + + /// Irreversibly transitions to a reference that permits traversal and offers + /// destructive methods and little else. + fn into_dying(self) -> NodeRef { + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } +} + +impl<'a> NodeRef, marker::Leaf> { + /// Adds a key-value pair to the end of the node, and returns + /// a handle to the inserted value. + /// + /// # Safety + /// + /// The returned handle has an unbound lifetime. + unsafe fn push_with_handle<'b>( + &mut self, + ) -> Handle, marker::Leaf>, marker::KV> { + let len = self.len_mut(); + let idx = usize::from(*len); + assert!(idx < CAPACITY); + *len += 1; + unsafe { + Handle::new_kv( + NodeRef { height: self.height, node: self.node, _marker: PhantomData }, + idx, + ) + } + } + + /// Adds a key-value pair to the end of the node, and returns + /// the mutable reference of the inserted value. + fn push(&mut self) -> i32 { + // SAFETY: The unbound handle is no longer accessible. + let _ = unsafe { self.push_with_handle() }; + 0 + } +} + +impl NodeRef { + /// Removes any static information asserting that this node is a `Leaf` node. + fn forget_type(self) -> NodeRef { + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } +} + +impl NodeRef { + /// Removes any static information asserting that this node is an `Internal` node. + fn forget_type(self) -> NodeRef { + NodeRef { height: self.height, node: self.node, _marker: PhantomData } + } +} + +impl NodeRef { + /// Checks whether a node is an `Internal` node or a `Leaf` node. + fn force(self) -> ForceResult> { + if self.height == 0 { + ForceResult::Leaf(NodeRef { + height: self.height, + node: self.node, + _marker: PhantomData, + }) + } else { + panic!() + } + } +} + +/// A reference to a specific key-value pair or edge within a node. The `Node` parameter +/// must be a `NodeRef`, while the `Type` can either be `KV` (signifying a handle on a key-value +/// pair) or `Edge` (signifying a handle on an edge). +/// +/// Note that even `Leaf` nodes can have `Edge` handles. Instead of representing a pointer to +/// a child node, these represent the spaces where child pointers would go between the key-value +/// pairs. For example, in a node with length 2, there would be 3 possible edge locations - one +/// to the left of the node, one between the two pairs, and one at the right of the node. +struct Handle { + node: Node, + idx: usize, + _marker: PhantomData, +} + +impl Handle { + /// Retrieves the node that contains the edge or key-value pair this handle points to. + fn into_node(self) -> Node { + self.node + } +} + +impl Handle, marker::KV> { + /// Creates a new handle to a key-value pair in `node`. + /// Unsafe because the caller must ensure that `idx < node.len()`. + unsafe fn new_kv(node: NodeRef, idx: usize) -> Self { + debug_assert!(idx < node.len()); + + Handle { node, idx, _marker: PhantomData } + } + + fn right_edge(self) -> Handle, marker::Edge> { + unsafe { Handle::new_edge(self.node, self.idx + 1) } + } +} + +impl Handle, marker::Edge> { + /// Creates a new handle to an edge in `node`. + /// Unsafe because the caller must ensure that `idx <= node.len()`. + unsafe fn new_edge(node: NodeRef, idx: usize) -> Self { + debug_assert!(idx <= node.len()); + + Handle { node, idx, _marker: PhantomData } + } + + fn right_kv(self) -> Result, marker::KV>, Self> { + if self.idx < self.node.len() { + Ok(unsafe { Handle::new_kv(self.node, self.idx) }) + } else { + Err(self) + } + } +} + +impl Handle, marker::Edge> { + fn forget_node_type(self) -> Handle, marker::Edge> { + unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } + } +} + +impl Handle, marker::Edge> { + fn forget_node_type(self) -> Handle, marker::Edge> { + unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } + } +} + +impl Handle, Type> { + /// Checks whether the underlying node is an `Internal` node or a `Leaf` node. + fn force(self) -> ForceResult, Type>> { + match self.node.force() { + ForceResult::Leaf(node) => { + ForceResult::Leaf(Handle { node, idx: self.idx, _marker: PhantomData }) + } + } + } +} +enum ForceResult { + Leaf(Leaf), +} + +mod marker { + use std::marker::PhantomData; + + pub enum Leaf {} + pub enum Internal {} + pub enum Owned {} + pub enum Dying {} + pub struct Mut<'a>(PhantomData<&'a mut ()>); + + pub trait BorrowType { + /// If node references of this borrow type allow traversing to other + /// nodes in the tree, this constant is set to `true`. It can be used + /// for a compile-time assertion. + const TRAVERSAL_PERMIT: bool = true; + } + impl BorrowType for Owned { + /// Reject traversal, because it isn't needed. Instead traversal + /// happens using the result of `borrow_mut`. + /// By disabling traversal, and only creating new references to roots, + /// we know that every reference of the `Owned` type is to a root node. + const TRAVERSAL_PERMIT: bool = false; + } + impl BorrowType for Dying {} + impl<'a> BorrowType for Mut<'a> {} + + pub enum KV {} + pub enum Edge {} +} + +enum LazyLeafHandle { + Root(NodeRef), // not yet descended + Edge(Handle, marker::Edge>), +} + +// `front` and `back` are always both `None` or both `Some`. +struct LazyLeafRange { + front: Option>, +} + +impl LazyLeafRange { + fn none() -> Self { + LazyLeafRange { front: None } + } +} + +impl LazyLeafRange { + fn take_front(&mut self) -> Option, marker::Edge>> { + match self.front.take()? { + LazyLeafHandle::Root(root) => Some(root.first_leaf_edge()), + LazyLeafHandle::Edge(edge) => Some(edge), + } + } + + #[inline] + unsafe fn deallocating_next_unchecked( + &mut self, + ) -> Handle, marker::KV> { + debug_assert!(self.front.is_some()); + let front = self.init_front().unwrap(); + unsafe { front.deallocating_next_unchecked() } + } + + #[inline] + fn deallocating_end(&mut self) { + if let Some(front) = self.take_front() { + front.deallocating_end() + } + } +} + +impl LazyLeafRange { + fn init_front( + &mut self, + ) -> Option<&mut Handle, marker::Edge>> { + if let Some(LazyLeafHandle::Root(root)) = &self.front { + self.front = Some(LazyLeafHandle::Edge(unsafe { ptr::read(root) }.first_leaf_edge())); + } + match &mut self.front { + None => None, + Some(LazyLeafHandle::Edge(edge)) => Some(edge), + // SAFETY: the code above would have replaced it. + Some(LazyLeafHandle::Root(_)) => panic!(), + } + } +} + +fn full_range( + root1: NodeRef, +) -> LazyLeafRange { + LazyLeafRange { front: Some(LazyLeafHandle::Root(root1)) } +} + +impl NodeRef { + /// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree. + /// The results are non-unique references allowing massively destructive mutation, so must be + /// used with the utmost care. + fn full_range(self) -> LazyLeafRange { + // We duplicate the root NodeRef here -- we will never access it in a way + // that overlaps references obtained from the root. + full_range(self) + } +} + +impl Handle, marker::Edge> { + /// Given a leaf edge handle into a dying tree, returns the next leaf edge + /// on the right side, and the key-value pair in between, if they exist. + /// + /// If the given edge is the last one in a leaf, this method deallocates + /// the leaf, as well as any ancestor nodes whose last edge was reached. + /// This implies that if no more key-value pair follows, the entire tree + /// will have been deallocated and there is nothing left to return. + /// + /// # Safety + /// - The given edge must not have been previously returned by counterpart + /// `deallocating_next_back`. + /// - The returned KV handle is only valid to access the key and value, + /// and only valid until the next call to a `deallocating_` method. + unsafe fn deallocating_next( + self, + ) -> Option<(Self, Handle, marker::KV>)> { + let mut edge = self.forget_node_type(); + loop { + edge = match edge.right_kv() { + Ok(kv) => return Some((unsafe { ptr::read(&kv) }.next_leaf_edge(), kv)), + Err(last_edge) => match unsafe { last_edge.into_node().deallocate_and_ascend() } { + Some(parent_edge) => parent_edge.forget_node_type(), + None => return None, + }, + } + } + } + + /// Deallocates a pile of nodes from the leaf up to the root. + /// This is the only way to deallocate the remainder of a tree after + /// `deallocating_next` and `deallocating_next_back` have been nibbling at + /// both sides of the tree, and have hit the same edge. As it is intended + /// only to be called when all keys and values have been returned, + /// no cleanup is done on any of the keys or values. + fn deallocating_end(self) { + let mut edge = self.forget_node_type(); + while let Some(parent_edge) = unsafe { edge.into_node().deallocate_and_ascend() } { + edge = parent_edge.forget_node_type(); + } + } +} + +impl Handle, marker::Edge> { + /// Moves the leaf edge handle to the next leaf edge and returns the key and value + /// in between, deallocating any node left behind while leaving the corresponding + /// edge in its parent node dangling. + /// + /// # Safety + /// - There must be another KV in the direction travelled. + /// - That KV was not previously returned by counterpart + /// `deallocating_next_back_unchecked` on any copy of the handles + /// being used to traverse the tree. + /// + /// The only safe way to proceed with the updated handle is to compare it, drop it, + /// or call this method or counterpart `deallocating_next_back_unchecked` again. + unsafe fn deallocating_next_unchecked( + &mut self, + ) -> Handle, marker::KV> { + replace(self, |leaf_edge| unsafe { leaf_edge.deallocating_next().unwrap() }) + } +} + +impl NodeRef { + /// Returns the leftmost leaf edge in or underneath a node - in other words, the edge + /// you need first when navigating forward (or last when navigating backward). + #[inline] + fn first_leaf_edge(self) -> Handle, marker::Edge> { + let node = self; + match node.force() { + ForceResult::Leaf(leaf) => return leaf.first_edge(), + } + } +} + +impl Handle, marker::KV> { + /// Returns the leaf edge closest to a KV for forward navigation. + fn next_leaf_edge(self) -> Handle, marker::Edge> { + match self.force() { + ForceResult::Leaf(leaf_kv) => leaf_kv.right_edge(), + } + } +} + +struct Foo { + root: Option, + length: usize, +} + +impl Drop for Foo { + fn drop(&mut self) { + drop(unsafe { core::ptr::read(self) }.into_iter()) + } +} + +impl IntoIterator for Foo { + type Item = (); + type IntoIter = IntoIter; + + fn into_iter(self) -> IntoIter { + let mut me = ManuallyDrop::new(self); + if let Some(root) = me.root.take() { + let full_range = root.into_dying().full_range(); + + IntoIter { range: full_range, length: me.length } + } else { + IntoIter { range: LazyLeafRange::none(), length: 0 } + } + } +} + +impl Drop for IntoIter { + fn drop(&mut self) { + while let Some(_kv) = self.dying_next() {} + } +} + +impl IntoIter { + /// Core of a `next` method returning a dying KV handle, + /// invalidated by further calls to this function and some others. + fn dying_next(&mut self) -> Option, marker::KV>> { + if self.length == 0 { + self.range.deallocating_end(); + None + } else { + self.length -= 1; + Some(unsafe { self.range.deallocating_next_unchecked() }) + } + } +} + +impl Iterator for IntoIter { + type Item = (); + + fn next(&mut self) -> Option<()> { + None + } +} + +/// An owning iterator over the entries of a `BTreeMap`. +/// +/// This `struct` is created by the [`into_iter`] method on [`BTreeMap`] +/// (provided by the [`IntoIterator`] trait). See its documentation for more. +/// +/// [`into_iter`]: IntoIterator::into_iter +struct IntoIter { + range: LazyLeafRange, + length: usize, +} + +#[cfg_attr(kani, kani::proof, kani::unwind(3))] +fn check_storagemarker_btreemap() { + let mut f = Foo { root: None, length: 0 }; + let mut root: NodeRef = NodeRef::new_leaf(); + root.borrow_mut().push(); + f.root = Some(root.forget_type()); + f.length = 1; +} From 6253ae7604ddfa0476b9e18b725c3904d3123889 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 05:45:30 -0700 Subject: [PATCH 045/225] Automatic cargo update to 2024-03-25 (#3103) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 91 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0b86eaf9206..aa3c4e089392 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,9 +26,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -101,9 +101,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bookrunner" @@ -140,9 +140,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] @@ -198,7 +198,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.55", ] [[package]] @@ -301,7 +301,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "crossterm_winapi", "libc", "parking_lot", @@ -347,9 +347,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "getopts" @@ -409,9 +409,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", @@ -504,7 +504,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.55", ] [[package]] @@ -676,9 +676,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "os_info" -version = "3.8.1" +version = "3.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbb46d5d01695d7a1fb8be5f0d1968bd2b2b8ba1d1b3e7062ce2a0593e57af1" +checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" dependencies = [ "log", "windows-sys", @@ -770,7 +770,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dce76ce678ffc8e5675b22aa1405de0b7037e2fdf8913fea40d1926c6fe1e6e7" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "memchr", "unicase", ] @@ -822,9 +822,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -851,9 +851,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -908,11 +908,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", @@ -972,7 +972,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.55", ] [[package]] @@ -1006,9 +1006,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.33" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0623d197252096520c6f2a5e1171ee436e5af99a5d7caa2891e55e61950e6d9" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ "indexmap", "itoa", @@ -1034,9 +1034,9 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "std" @@ -1084,7 +1084,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.53", + "syn 2.0.55", ] [[package]] @@ -1097,7 +1097,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.53", + "syn 2.0.55", ] [[package]] @@ -1112,9 +1112,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.53" +version = "2.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" dependencies = [ "proc-macro2", "quote", @@ -1150,7 +1150,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.55", ] [[package]] @@ -1165,9 +1165,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af06656561d28735e9c1cd63dfd57132c8155426aa6af24f36a00a351f88c48e" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", @@ -1186,9 +1186,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.7" +version = "0.22.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18769cd1cec395d70860ceb4d932812a0b4d06b1a4bb336745a4d21b9496e992" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" dependencies = [ "indexmap", "serde", @@ -1216,7 +1216,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.55", ] [[package]] @@ -1344,15 +1344,14 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "which" -version = "6.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fa5e0c10bf77f44aac573e498d1a82d5fbd5e91f6fc0a99e7be4b38e85e101c" +checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7" dependencies = [ "either", "home", - "once_cell", "rustix", - "windows-sys", + "winsafe", ] [[package]] @@ -1518,6 +1517,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "zerocopy" version = "0.7.32" @@ -1535,5 +1540,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.55", ] From 9de72012f5a288fc318e1cf4ddca4d24041d7d7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 10:47:31 -0700 Subject: [PATCH 046/225] Bump tests/perf/s2n-quic from `1a7faa8` to `0a60ec1` (#3104) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `1a7faa8` to `0a60ec1`.
Commits
  • 0a60ec1 test: generate script for testing certs and keys (#2161)
  • 15f234c feat(s2n-quic-rustls): update rustls from 0.21 to 0.23 (#2143)
  • aebd430 ci: update rust nightly version (#2159)
  • 26874c2 build: fix clippy warnings for 1.77 (#2158)
  • d9c6bd6 ci: add musl tests (#2102)
  • ac7bf90 fix(s2n-quic-platform): don't reduce rx MTU when tx MTU is reduced (#2156)
  • 9e39ca0 build(deps): update heck requirement from 0.4 to 0.5 (#2152)
  • 2d4df73 feat(s2n-quic-xdp): add umem hugepage option (#2145)
  • 440f4f4 build(deps): bump docker/setup-buildx-action from 3.1.0 to 3.2.0 (#2153)
  • d73c648 build(deps): bump docker/login-action from 3.0.0 to 3.1.0 (#2154)
  • 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 1a7faa87ebcf..0a60ec19ea9a 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 1a7faa87ebcf2b84a068f21af3306a6f42eb8c74 +Subproject commit 0a60ec19ea9a8cd1bef98ad9798327521d2121ee From e4a90e9aa366142e18d1ba01ea42136700da8f2f Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Mon, 25 Mar 2024 11:29:03 -0700 Subject: [PATCH 047/225] Implement validity checks (#3085) This is still incomplete, but hopefully it can be merged as an unstable feature. I'll publish an RFC shortly. This instruments the function body with assertion checks to see if users are generating invalid values. This covers: - Union access - Raw pointer dereference - Transmute value - Field assignment of struct with invalid values - Aggregate assignment Things not covered today should trigger ICE or a delayed verification failure due to unsupported feature. ## Design This change has two main design changes which are inside the new `kani_compiler::kani_middle::transform` module: 1- Instance body should now be retrieved from the `BodyTransformation` structure. This structure will run transformation passes on instance bodies (i.e.: monomorphic instances) and cache the result. 2- Create a new transformation pass that instruments the body of a function for every potential invalid value generation. 3- Create a body builder which contains all elements of a function body and mutable functions to modify them accordingly. Related to #2998 Fixes #301 --------- Co-authored-by: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> --- kani-compiler/src/args.rs | 11 + .../codegen_cprover_gotoc/codegen/function.rs | 4 +- .../compiler_interface.rs | 25 +- .../codegen_cprover_gotoc/context/goto_ctx.rs | 5 + .../codegen_cprover_gotoc/overrides/hooks.rs | 26 +- kani-compiler/src/kani_middle/attributes.rs | 11 + kani-compiler/src/kani_middle/mod.rs | 18 +- kani-compiler/src/kani_middle/provide.rs | 4 +- kani-compiler/src/kani_middle/reachability.rs | 28 +- .../src/kani_middle/transform/body.rs | 275 ++++++ .../src/kani_middle/transform/check_values.rs | 924 ++++++++++++++++++ .../src/kani_middle/transform/mod.rs | 135 +++ kani-driver/src/call_single_file.rs | 5 + kani_metadata/src/unstable.rs | 3 + tests/kani/ValidValues/constants.rs | 40 + tests/kani/ValidValues/custom_niche.rs | 100 ++ tests/kani/ValidValues/non_null.rs | 26 + tests/kani/ValidValues/write_invalid.rs | 37 + 18 files changed, 1641 insertions(+), 36 deletions(-) create mode 100644 kani-compiler/src/kani_middle/transform/body.rs create mode 100644 kani-compiler/src/kani_middle/transform/check_values.rs create mode 100644 kani-compiler/src/kani_middle/transform/mod.rs create mode 100644 tests/kani/ValidValues/constants.rs create mode 100644 tests/kani/ValidValues/custom_niche.rs create mode 100644 tests/kani/ValidValues/non_null.rs create mode 100644 tests/kani/ValidValues/write_invalid.rs diff --git a/kani-compiler/src/args.rs b/kani-compiler/src/args.rs index 69a82b61e19d..fcc346bd745f 100644 --- a/kani-compiler/src/args.rs +++ b/kani-compiler/src/args.rs @@ -71,4 +71,15 @@ pub struct Arguments { #[clap(long)] /// A legacy flag that is now ignored. goto_c: bool, + /// Enable specific checks. + #[clap(long)] + pub ub_check: Vec, +} + +#[derive(Debug, Clone, Copy, AsRefStr, EnumString, VariantNames, PartialEq, Eq)] +#[strum(serialize_all = "snake_case")] +pub enum ExtraChecks { + /// Check that produced values are valid except for uninitialized values. + /// See https://github.com/model-checking/kani/issues/920. + Validity, } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs index 7cf4b979f407..d55696bfdc87 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs @@ -60,7 +60,7 @@ impl<'tcx> GotocCtx<'tcx> { debug!("Double codegen of {:?}", old_sym); } else { assert!(old_sym.is_function()); - let body = instance.body().unwrap(); + let body = self.transformer.body(self.tcx, instance); self.set_current_fn(instance, &body); self.print_instance(instance, &body); self.codegen_function_prelude(&body); @@ -201,7 +201,7 @@ impl<'tcx> GotocCtx<'tcx> { pub fn declare_function(&mut self, instance: Instance) { debug!("declaring {}; {:?}", instance.name(), instance); - let body = instance.body().unwrap(); + 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(&self.symbol_name_stable(instance), |ctx, fname| { diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index cad8bcbfb9ce..d3b4ae8473de 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -12,6 +12,7 @@ use crate::kani_middle::provide; use crate::kani_middle::reachability::{ collect_reachable_items, filter_const_crate_items, filter_crate_items, }; +use crate::kani_middle::transform::BodyTransformation; use crate::kani_middle::{check_reachable_items, dump_mir_items}; use crate::kani_queries::QueryDb; use cbmc::goto_program::Location; @@ -86,16 +87,18 @@ impl GotocCodegenBackend { symtab_goto: &Path, machine_model: &MachineModel, check_contract: Option, + mut transformer: BodyTransformation, ) -> (GotocCtx<'tcx>, Vec, Option) { let items = with_timer( - || collect_reachable_items(tcx, starting_items), + || collect_reachable_items(tcx, &mut transformer, starting_items), "codegen reachability analysis", ); dump_mir_items(tcx, &items, &symtab_goto.with_extension("kani.mir")); // Follow rustc naming convention (cx is abbrev for context). // https://rustc-dev-guide.rust-lang.org/conventions.html#naming-conventions - let mut gcx = GotocCtx::new(tcx, (*self.queries.lock().unwrap()).clone(), machine_model); + let mut gcx = + GotocCtx::new(tcx, (*self.queries.lock().unwrap()).clone(), machine_model, transformer); check_reachable_items(gcx.tcx, &gcx.queries, &items); let contract_info = with_timer( @@ -227,6 +230,7 @@ impl CodegenBackend for GotocCodegenBackend { // - None: Don't generate code. This is used to compile dependencies. let base_filename = tcx.output_filenames(()).output_path(OutputType::Object); let reachability = queries.args().reachability_analysis; + let mut transformer = BodyTransformation::new(&queries, tcx); let mut results = GotoCodegenResults::new(tcx, reachability); match reachability { ReachabilityType::Harnesses => { @@ -248,8 +252,9 @@ impl CodegenBackend for GotocCodegenBackend { model_path, &results.machine_model, contract_metadata, + transformer, ); - results.extend(gcx, items, None); + transformer = results.extend(gcx, items, None); if let Some(assigns_contract) = contract_info { self.queries.lock().unwrap().register_assigns_contract( canonical_mangled_name(harness).intern(), @@ -263,7 +268,7 @@ impl CodegenBackend for GotocCodegenBackend { // test closure that we want to execute // TODO: Refactor this code so we can guarantee that the pair (test_fn, test_desc) actually match. let mut descriptions = vec![]; - let harnesses = filter_const_crate_items(tcx, |_, item| { + let harnesses = filter_const_crate_items(tcx, &mut transformer, |_, item| { if is_test_harness_description(tcx, item.def) { descriptions.push(item.def); true @@ -282,6 +287,7 @@ impl CodegenBackend for GotocCodegenBackend { &model_path, &results.machine_model, Default::default(), + transformer, ); results.extend(gcx, items, None); @@ -319,9 +325,10 @@ impl CodegenBackend for GotocCodegenBackend { &model_path, &results.machine_model, Default::default(), + transformer, ); assert!(contract_info.is_none()); - results.extend(gcx, items, None); + let _ = results.extend(gcx, items, None); } } @@ -613,12 +620,18 @@ impl GotoCodegenResults { } } - fn extend(&mut self, gcx: GotocCtx, items: Vec, metadata: Option) { + fn extend( + &mut self, + gcx: GotocCtx, + items: Vec, + metadata: Option, + ) -> BodyTransformation { let mut items = items; self.harnesses.extend(metadata); self.concurrent_constructs.extend(gcx.concurrent_constructs); self.unsupported_constructs.extend(gcx.unsupported_constructs); self.items.append(&mut items); + gcx.transformer } /// Prints a report at the end of the compilation. 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 10ee6a876588..a25e502aa574 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs @@ -18,6 +18,7 @@ use super::vtable_ctx::VtableCtx; use crate::codegen_cprover_gotoc::overrides::{fn_hooks, GotocHooks}; use crate::codegen_cprover_gotoc::utils::full_crate_name; use crate::codegen_cprover_gotoc::UnsupportedConstructs; +use crate::kani_middle::transform::BodyTransformation; use crate::kani_queries::QueryDb; use cbmc::goto_program::{DatatypeComponent, Expr, Location, Stmt, Symbol, SymbolTable, Type}; use cbmc::utils::aggr_tag; @@ -70,6 +71,8 @@ pub struct GotocCtx<'tcx> { /// We collect them and print one warning at the end if not empty instead of printing one /// warning at each occurrence. pub concurrent_constructs: UnsupportedConstructs, + /// The body transformation agent. + pub transformer: BodyTransformation, } /// Constructor @@ -78,6 +81,7 @@ impl<'tcx> GotocCtx<'tcx> { tcx: TyCtxt<'tcx>, queries: QueryDb, machine_model: &MachineModel, + transformer: BodyTransformation, ) -> GotocCtx<'tcx> { let fhks = fn_hooks(); let symbol_table = SymbolTable::new(machine_model.clone()); @@ -99,6 +103,7 @@ impl<'tcx> GotocCtx<'tcx> { global_checks_count: 0, unsupported_constructs: FxHashMap::default(), concurrent_constructs: FxHashMap::default(), + transformer, } } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index 2fcbaa36fb44..18cc44b7b20d 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -10,6 +10,7 @@ use crate::codegen_cprover_gotoc::codegen::{bb_label, PropertyClass}; use crate::codegen_cprover_gotoc::GotocCtx; +use crate::kani_middle::attributes::matches_diagnostic as matches_function; use crate::unwrap_or_return_codegen_unimplemented_stmt; use cbmc::goto_program::{BuiltinFn, Expr, Location, Stmt, Type}; use rustc_middle::ty::TyCtxt; @@ -35,17 +36,6 @@ pub trait GotocHook { ) -> Stmt; } -fn matches_function(tcx: TyCtxt, instance: Instance, attr_name: &str) -> bool { - let attr_sym = rustc_span::symbol::Symbol::intern(attr_name); - if let Some(attr_id) = tcx.all_diagnostic_items(()).name_to_id.get(&attr_sym) { - if rustc_internal::internal(tcx, instance.def.def_id()) == *attr_id { - debug!("matched: {:?} {:?}", attr_id, attr_sym); - return true; - } - } - false -} - /// A hook for Kani's `cover` function (declared in `library/kani/src/lib.rs`). /// The function takes two arguments: a condition expression (bool) and a /// message (&'static str). @@ -57,7 +47,7 @@ fn matches_function(tcx: TyCtxt, instance: Instance, attr_name: &str) -> bool { struct Cover; impl GotocHook for Cover { fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { - matches_function(tcx, instance, "KaniCover") + matches_function(tcx, instance.def, "KaniCover") } fn handle( @@ -92,7 +82,7 @@ impl GotocHook for Cover { struct Assume; impl GotocHook for Assume { fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { - matches_function(tcx, instance, "KaniAssume") + matches_function(tcx, instance.def, "KaniAssume") } fn handle( @@ -116,7 +106,7 @@ impl GotocHook for Assume { struct Assert; impl GotocHook for Assert { fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { - matches_function(tcx, instance, "KaniAssert") + matches_function(tcx, instance.def, "KaniAssert") } fn handle( @@ -157,7 +147,7 @@ struct Nondet; impl GotocHook for Nondet { fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { - matches_function(tcx, instance, "KaniAnyRaw") + matches_function(tcx, instance.def, "KaniAnyRaw") } fn handle( @@ -201,7 +191,7 @@ impl GotocHook for Panic { || tcx.has_attr(def_id, rustc_span::sym::rustc_const_panic_str) || Some(def_id) == tcx.lang_items().panic_fmt() || Some(def_id) == tcx.lang_items().begin_panic_fn() - || matches_function(tcx, instance, "KaniPanic") + || matches_function(tcx, instance.def, "KaniPanic") } fn handle( @@ -221,7 +211,7 @@ impl GotocHook for Panic { struct IsReadOk; impl GotocHook for IsReadOk { fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { - matches_function(tcx, instance, "KaniIsReadOk") + matches_function(tcx, instance.def, "KaniIsReadOk") } fn handle( @@ -365,7 +355,7 @@ struct UntrackedDeref; impl GotocHook for UntrackedDeref { fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { - matches_function(tcx, instance, "KaniUntrackedDeref") + matches_function(tcx, instance.def, "KaniUntrackedDeref") } fn handle( diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index df494d7daa3c..979877199dde 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -1037,3 +1037,14 @@ fn attr_kind(tcx: TyCtxt, attr: &Attribute) -> Option { _ => None, } } + +pub fn matches_diagnostic(tcx: TyCtxt, def: T, attr_name: &str) -> bool { + let attr_sym = rustc_span::symbol::Symbol::intern(attr_name); + if let Some(attr_id) = tcx.all_diagnostic_items(()).name_to_id.get(&attr_sym) { + if rustc_internal::internal(tcx, def.def_id()) == *attr_id { + debug!("matched: {:?} {:?}", attr_id, attr_sym); + return true; + } + } + false +} diff --git a/kani-compiler/src/kani_middle/mod.rs b/kani-compiler/src/kani_middle/mod.rs index e65befc9624e..4cd2d4a357d7 100644 --- a/kani-compiler/src/kani_middle/mod.rs +++ b/kani-compiler/src/kani_middle/mod.rs @@ -23,7 +23,7 @@ use rustc_target::abi::call::FnAbi; use rustc_target::abi::{HasDataLayout, TargetDataLayout}; use stable_mir::mir::mono::{Instance, InstanceKind, MonoItem}; use stable_mir::mir::pretty::pretty_ty; -use stable_mir::ty::{BoundVariableKind, RigidTy, Span as SpanStable, Ty, TyKind}; +use stable_mir::ty::{BoundVariableKind, FnDef, RigidTy, Span as SpanStable, Ty, TyKind}; use stable_mir::visitor::{Visitable, Visitor as TypeVisitor}; use stable_mir::{CrateDef, DefId}; use std::fs::File; @@ -41,6 +41,7 @@ pub mod provide; pub mod reachability; pub mod resolve; pub mod stubbing; +pub mod transform; /// Check that all crate items are supported and there's no misconfiguration. /// This method will exhaustively print any error / warning and it will abort at the end if any @@ -316,3 +317,18 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for CompilerHelpers<'tcx> { } } } + +/// Find an instance of a function from the given crate that has been annotated with `diagnostic` +/// item. +fn find_fn_def(tcx: TyCtxt, diagnostic: &str) -> Option { + let attr_id = tcx + .all_diagnostic_items(()) + .name_to_id + .get(&rustc_span::symbol::Symbol::intern(diagnostic))?; + let TyKind::RigidTy(RigidTy::FnDef(def, _)) = + rustc_internal::stable(tcx.type_of(attr_id)).value.kind() + else { + return None; + }; + Some(def) +} diff --git a/kani-compiler/src/kani_middle/provide.rs b/kani-compiler/src/kani_middle/provide.rs index d5495acb67a7..d29635ecfd15 100644 --- a/kani-compiler/src/kani_middle/provide.rs +++ b/kani-compiler/src/kani_middle/provide.rs @@ -8,6 +8,7 @@ use crate::args::{Arguments, ReachabilityType}; use crate::kani_middle::intrinsics::ModelIntrinsics; use crate::kani_middle::reachability::{collect_reachable_items, filter_crate_items}; use crate::kani_middle::stubbing; +use crate::kani_middle::transform::BodyTransformation; use crate::kani_queries::QueryDb; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::util::Providers; @@ -79,8 +80,9 @@ fn collect_and_partition_mono_items( rustc_smir::rustc_internal::run(tcx, || { let local_reachable = filter_crate_items(tcx, |_, _| true).into_iter().map(MonoItem::Fn).collect::>(); + // We do not actually need the value returned here. - collect_reachable_items(tcx, &local_reachable); + collect_reachable_items(tcx, &mut BodyTransformation::dummy(), &local_reachable); }) .unwrap(); (rustc_interface::DEFAULT_QUERY_PROVIDERS.collect_and_partition_mono_items)(tcx, key) diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 2fa1bf057c1c..b46fbdccc016 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -37,12 +37,17 @@ use stable_mir::{CrateDef, ItemKind}; use crate::kani_middle::coercion; use crate::kani_middle::coercion::CoercionBase; use crate::kani_middle::stubbing::{get_stub, validate_instance}; +use crate::kani_middle::transform::BodyTransformation; /// Collect all reachable items starting from the given starting points. -pub fn collect_reachable_items(tcx: TyCtxt, starting_points: &[MonoItem]) -> Vec { +pub fn collect_reachable_items( + tcx: TyCtxt, + transformer: &mut BodyTransformation, + starting_points: &[MonoItem], +) -> Vec { // For each harness, collect items using the same collector. // I.e.: This will return any item that is reachable from one or more of the starting points. - let mut collector = MonoItemsCollector::new(tcx); + let mut collector = MonoItemsCollector::new(tcx, transformer); for item in starting_points { collector.collect(item.clone()); } @@ -92,7 +97,11 @@ where /// /// Probably only specifically useful with a predicate to find `TestDescAndFn` const declarations from /// tests and extract the closures from them. -pub fn filter_const_crate_items(tcx: TyCtxt, mut predicate: F) -> Vec +pub fn filter_const_crate_items( + tcx: TyCtxt, + transformer: &mut BodyTransformation, + mut predicate: F, +) -> Vec where F: FnMut(TyCtxt, Instance) -> bool, { @@ -103,7 +112,7 @@ where // Only collect monomorphic items. if let Ok(instance) = Instance::try_from(item) { if predicate(tcx, instance) { - let body = instance.body().unwrap(); + let body = transformer.body(tcx, instance); let mut collector = MonoItemsFnCollector { tcx, body: &body, @@ -118,9 +127,11 @@ where roots } -struct MonoItemsCollector<'tcx> { +struct MonoItemsCollector<'tcx, 'a> { /// The compiler context. tcx: TyCtxt<'tcx>, + /// The body transformation object used to retrieve a transformed body. + transformer: &'a mut BodyTransformation, /// Set of collected items used to avoid entering recursion loops. collected: FxHashSet, /// Items enqueued for visiting. @@ -129,14 +140,15 @@ struct MonoItemsCollector<'tcx> { call_graph: debug::CallGraph, } -impl<'tcx> MonoItemsCollector<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx, 'a> MonoItemsCollector<'tcx, 'a> { + pub fn new(tcx: TyCtxt<'tcx>, transformer: &'a mut BodyTransformation) -> Self { MonoItemsCollector { tcx, collected: FxHashSet::default(), queue: vec![], #[cfg(debug_assertions)] call_graph: debug::CallGraph::default(), + transformer, } } @@ -174,7 +186,7 @@ impl<'tcx> MonoItemsCollector<'tcx> { fn visit_fn(&mut self, instance: Instance) -> Vec { let _guard = debug_span!("visit_fn", function=?instance).entered(); if validate_instance(self.tcx, instance) { - let body = instance.body().unwrap(); + let body = self.transformer.body(self.tcx, instance); let mut collector = MonoItemsFnCollector { tcx: self.tcx, collected: FxHashSet::default(), diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs new file mode 100644 index 000000000000..32ffb373d767 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -0,0 +1,275 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +//! Utility functions that allow us to modify a function body. + +use crate::kani_middle::find_fn_def; +use rustc_middle::ty::TyCtxt; +use stable_mir::mir::mono::Instance; +use stable_mir::mir::{ + BasicBlock, BasicBlockIdx, BinOp, Body, CastKind, Constant, Local, LocalDecl, Mutability, + Operand, Place, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, UnwindAction, + VarDebugInfo, +}; +use stable_mir::ty::{Const, GenericArgs, Span, Ty, UintTy}; +use std::mem; + +/// This structure mimics a Body that can actually be modified. +pub struct MutableBody { + blocks: Vec, + + /// Declarations of locals within the function. + /// + /// The first local is the return value pointer, followed by `arg_count` + /// locals for the function arguments, followed by any user-declared + /// variables and temporaries. + locals: Vec, + + /// The number of arguments this function takes. + arg_count: usize, + + /// Debug information pertaining to user variables, including captures. + var_debug_info: Vec, + + /// Mark an argument (which must be a tuple) as getting passed as its individual components. + /// + /// This is used for the "rust-call" ABI such as closures. + spread_arg: Option, + + /// The span that covers the entire function body. + span: Span, +} + +impl MutableBody { + /// Get the basic blocks of this builder. + pub fn blocks(&self) -> &[BasicBlock] { + &self.blocks + } + + pub fn locals(&self) -> &[LocalDecl] { + &self.locals + } + + /// Create a mutable body from the original MIR body. + pub fn from(body: Body) -> Self { + MutableBody { + locals: body.locals().to_vec(), + arg_count: body.arg_locals().len(), + spread_arg: body.spread_arg(), + blocks: body.blocks, + var_debug_info: body.var_debug_info, + span: body.span, + } + } + + /// Create the new body consuming this mutable body. + pub fn into(self) -> Body { + Body::new( + self.blocks, + self.locals, + self.arg_count, + self.var_debug_info, + self.spread_arg, + self.span, + ) + } + + /// Add a new local to the body with the given attributes. + pub fn new_local(&mut self, ty: Ty, span: Span, mutability: Mutability) -> Local { + let decl = LocalDecl { ty, span, mutability }; + let local = self.locals.len(); + self.locals.push(decl); + local + } + + pub fn new_str_operand(&mut self, msg: &str, span: Span) -> Operand { + let literal = Const::from_str(msg); + Operand::Constant(Constant { span, user_ty: None, literal }) + } + + pub fn new_const_operand(&mut self, val: u128, uint_ty: UintTy, span: Span) -> Operand { + let literal = Const::try_from_uint(val, uint_ty).unwrap(); + Operand::Constant(Constant { span, user_ty: None, literal }) + } + + /// Create a raw pointer of `*mut type` and return a new local where that value is stored. + pub fn new_cast_ptr( + &mut self, + from: Operand, + pointee_ty: Ty, + mutability: Mutability, + before: &mut SourceInstruction, + ) -> Local { + assert!(from.ty(self.locals()).unwrap().kind().is_raw_ptr()); + let target_ty = Ty::new_ptr(pointee_ty, mutability); + let rvalue = Rvalue::Cast(CastKind::PtrToPtr, from, target_ty); + self.new_assignment(rvalue, before) + } + + /// Add a new assignment for the given binary operation. + /// + /// Return the local where the result is saved. + pub fn new_binary_op( + &mut self, + bin_op: BinOp, + lhs: Operand, + rhs: Operand, + before: &mut SourceInstruction, + ) -> Local { + let rvalue = Rvalue::BinaryOp(bin_op, lhs, rhs); + self.new_assignment(rvalue, before) + } + + /// Add a new assignment. + /// + /// Return local where the result is saved. + pub fn new_assignment(&mut self, rvalue: Rvalue, before: &mut SourceInstruction) -> Local { + let span = before.span(&self.blocks); + let ret_ty = rvalue.ty(&self.locals).unwrap(); + let result = self.new_local(ret_ty, span, Mutability::Not); + let stmt = Statement { kind: StatementKind::Assign(Place::from(result), rvalue), span }; + self.insert_stmt(stmt, before); + result + } + + /// Add a new assert to the basic block indicated by the given index. + /// + /// The new assertion will have the same span as the source instruction, and the basic block + /// will be split. The source instruction will be adjusted to point to the first instruction in + /// the new basic block. + pub fn add_check( + &mut self, + tcx: TyCtxt, + check_type: &CheckType, + source: &mut SourceInstruction, + value: Local, + 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); + match check_type { + CheckType::Assert(assert_fn) => { + 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], + destination: Place { + local: self.new_local(Ty::new_tuple(&[]), span, Mutability::Not), + projection: vec![], + }, + target: Some(new_bb), + unwind: UnwindAction::Terminate, + }; + let terminator = Terminator { kind, span }; + self.split_bb(source, terminator); + } + CheckType::Panic(..) | CheckType::NoCore => { + tcx.sess + .dcx() + .struct_err("Failed to instrument the code. Cannot find `kani::assert`") + .with_note("Kani requires `kani` library in order to verify a crate.") + .emit(); + tcx.sess.dcx().abort_if_errors(); + unreachable!(); + } + } + } + + /// Split a basic block right before the source location and use the new terminator + /// in the basic block that was split. + /// + /// The source is updated to point to the same instruction which is now in the new basic block. + pub fn split_bb(&mut self, source: &mut SourceInstruction, new_term: Terminator) { + let new_bb_idx = self.blocks.len(); + let (idx, bb) = match source { + SourceInstruction::Statement { idx, bb } => { + let (orig_idx, orig_bb) = (*idx, *bb); + *idx = 0; + *bb = new_bb_idx; + (orig_idx, orig_bb) + } + SourceInstruction::Terminator { bb } => { + let orig_bb = *bb; + *bb = new_bb_idx; + (self.blocks[orig_bb].statements.len(), orig_bb) + } + }; + let old_term = mem::replace(&mut self.blocks[bb].terminator, new_term); + let bb_stmts = &mut self.blocks[bb].statements; + let remaining = bb_stmts.split_off(idx); + let new_bb = BasicBlock { statements: remaining, terminator: old_term }; + self.blocks.push(new_bb); + } + + /// Insert statement before the source instruction and update the source as needed. + pub fn insert_stmt(&mut self, new_stmt: Statement, before: &mut SourceInstruction) { + match before { + SourceInstruction::Statement { idx, bb } => { + self.blocks[*bb].statements.insert(*idx, new_stmt); + *idx += 1; + } + SourceInstruction::Terminator { bb } => { + // Append statements at the end of the basic block. + self.blocks[*bb].statements.push(new_stmt); + } + } + } +} + +#[derive(Clone, Debug)] +pub enum CheckType { + /// This is used by default when the `kani` crate is available. + Assert(Instance), + /// When the `kani` crate is not available, we have to model the check as an `if { panic!() }`. + Panic(Instance), + /// When building non-core crate, such as `rustc-std-workspace-core`, we cannot + /// instrument code, but we can still compile them. + NoCore, +} + +impl CheckType { + /// This will create the type of check that is available in the current crate. + /// + /// 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(tcx: TyCtxt) -> CheckType { + if let Some(instance) = find_instance(tcx, "KaniAssert") { + CheckType::Assert(instance) + } else if let Some(instance) = find_instance(tcx, "panic_str") { + CheckType::Panic(instance) + } else { + CheckType::NoCore + } + } +} + +/// We store the index of an instruction to avoid borrow checker issues and unnecessary copies. +#[derive(Copy, Clone, Debug)] +pub enum SourceInstruction { + Statement { idx: usize, bb: BasicBlockIdx }, + Terminator { bb: BasicBlockIdx }, +} + +impl SourceInstruction { + pub fn span(&self, blocks: &[BasicBlock]) -> Span { + match *self { + SourceInstruction::Statement { idx, bb } => blocks[bb].statements[idx].span, + SourceInstruction::Terminator { bb } => blocks[bb].terminator.span, + } + } +} + +fn find_instance(tcx: TyCtxt, diagnostic: &str) -> Option { + Instance::resolve(find_fn_def(tcx, diagnostic)?, &GenericArgs(vec![])).ok() +} diff --git a/kani-compiler/src/kani_middle/transform/check_values.rs b/kani-compiler/src/kani_middle/transform/check_values.rs new file mode 100644 index 000000000000..aefc20b46a44 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/check_values.rs @@ -0,0 +1,924 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +//! Implement a transformation pass that instrument the code to detect possible UB due to +//! the generation of an invalid value. +//! +//! This pass highly depend on Rust type layouts. For more details, see: +//! +//! +//! For that, we traverse the function body and look for unsafe operations that may generate +//! invalid values. For each operation found, we add checks to ensure the value is valid. +//! +//! Note: There is some redundancy in the checks that could be optimized. Example: +//! 1. We could merge the invalid values by the offset. +//! 2. We could avoid checking places that have been checked before. +use crate::args::ExtraChecks; +use crate::kani_middle::transform::body::{CheckType, MutableBody, SourceInstruction}; +use crate::kani_middle::transform::check_values::SourceOp::UnsupportedCheck; +use crate::kani_middle::transform::{TransformPass, TransformationType}; +use crate::kani_queries::QueryDb; +use rustc_middle::ty::TyCtxt; +use rustc_smir::rustc_internal; +use stable_mir::abi::{FieldsShape, Scalar, TagEncoding, ValueAbi, VariantsShape, WrappingRange}; +use stable_mir::mir::mono::{Instance, InstanceKind}; +use stable_mir::mir::visit::{Location, PlaceContext, PlaceRef}; +use stable_mir::mir::{ + AggregateKind, BasicBlockIdx, BinOp, Body, CastKind, Constant, FieldIdx, Local, LocalDecl, + MirVisitor, Mutability, NonDivergingIntrinsic, Operand, Place, ProjectionElem, Rvalue, + Statement, StatementKind, Terminator, TerminatorKind, +}; +use stable_mir::target::{MachineInfo, MachineSize}; +use stable_mir::ty::{AdtKind, Const, IndexedVal, RigidTy, Ty, TyKind, UintTy}; +use stable_mir::CrateDef; +use std::fmt::{Debug, Formatter}; +use strum_macros::AsRefStr; +use tracing::{debug, trace}; + +/// Instrument the code with checks for invalid values. +pub struct ValidValuePass { + check_type: CheckType, +} + +impl ValidValuePass { + pub fn new(tcx: TyCtxt) -> Self { + ValidValuePass { check_type: CheckType::new(tcx) } + } +} + +impl TransformPass for ValidValuePass { + fn transformation_type() -> TransformationType + where + Self: Sized, + { + TransformationType::Instrumentation + } + + fn is_enabled(&self, query_db: &QueryDb) -> bool + where + Self: Sized, + { + let args = query_db.args(); + args.ub_check.contains(&ExtraChecks::Validity) + } + + /// Transform the function body by inserting checks one-by-one. + /// For every unsafe dereference or a transmute operation, we check all values are valid. + fn transform(&self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + trace!(function=?instance.name(), "transform"); + let mut new_body = MutableBody::from(body); + let orig_len = new_body.blocks().len(); + // Do not cache body.blocks().len() since it will change as we add new checks. + for bb_idx in 0..new_body.blocks().len() { + let Some(candidate) = + CheckValueVisitor::find_next(&new_body, bb_idx, bb_idx >= orig_len) + else { + continue; + }; + self.build_check(tcx, &mut new_body, candidate); + } + (orig_len != new_body.blocks().len(), new_body.into()) + } +} + +impl Debug for ValidValuePass { + /// Implement manually since MachineInfo doesn't currently derive Debug. + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + "ValidValuePass".fmt(f) + } +} + +impl ValidValuePass { + fn build_check(&self, tcx: TyCtxt, body: &mut MutableBody, instruction: UnsafeInstruction) { + debug!(?instruction, "build_check"); + let mut source = instruction.source; + for operation in instruction.operations { + match operation { + SourceOp::BytesValidity { ranges, target_ty, rvalue } => { + let value = body.new_assignment(rvalue, &mut source); + let rvalue_ptr = Rvalue::AddressOf(Mutability::Not, Place::from(value)); + for range in ranges { + let result = + self.build_limits(body, &range, rvalue_ptr.clone(), &mut source); + let msg = format!( + "Undefined Behavior: Invalid value of type `{}`", + // TODO: Fix pretty_ty + rustc_internal::internal(tcx, target_ty) + ); + body.add_check(tcx, &self.check_type, &mut source, result, &msg); + } + } + SourceOp::DerefValidity { pointee_ty, rvalue, ranges } => { + for range in ranges { + let result = self.build_limits(body, &range, rvalue.clone(), &mut source); + let msg = format!( + "Undefined Behavior: Invalid value of type `{}`", + // TODO: Fix pretty_ty + rustc_internal::internal(tcx, pointee_ty) + ); + body.add_check(tcx, &self.check_type, &mut source, result, &msg); + } + } + SourceOp::UnsupportedCheck { check, ty } => { + let reason = format!( + "Kani currently doesn't support checking validity of `{check}` for `{}` type", + rustc_internal::internal(tcx, ty) + ); + self.unsupported_check(tcx, body, &mut source, &reason); + } + } + } + } + + fn build_limits( + &self, + body: &mut MutableBody, + req: &ValidValueReq, + rvalue_ptr: Rvalue, + source: &mut SourceInstruction, + ) -> Local { + let span = source.span(body.blocks()); + debug!(?req, ?rvalue_ptr, ?span, "build_limits"); + let primitive_ty = uint_ty(req.size.bytes()); + let start_const = body.new_const_operand(req.valid_range.start, primitive_ty, span); + let end_const = body.new_const_operand(req.valid_range.end, primitive_ty, span); + let orig_ptr = if req.offset != 0 { + let start_ptr = move_local(body.new_assignment(rvalue_ptr, source)); + let byte_ptr = move_local(body.new_cast_ptr( + start_ptr, + Ty::unsigned_ty(UintTy::U8), + Mutability::Not, + source, + )); + let offset_const = body.new_const_operand(req.offset as _, UintTy::Usize, span); + let offset = move_local(body.new_assignment(Rvalue::Use(offset_const), source)); + move_local(body.new_binary_op(BinOp::Offset, byte_ptr, offset, source)) + } else { + move_local(body.new_assignment(rvalue_ptr, source)) + }; + let value_ptr = + body.new_cast_ptr(orig_ptr, Ty::unsigned_ty(primitive_ty), Mutability::Not, source); + let value = + Operand::Copy(Place { local: value_ptr, projection: vec![ProjectionElem::Deref] }); + let start_result = body.new_binary_op(BinOp::Ge, value.clone(), start_const, source); + let end_result = body.new_binary_op(BinOp::Le, value, end_const, source); + if req.valid_range.wraps_around() { + // valid >= start || valid <= end + body.new_binary_op( + BinOp::BitOr, + move_local(start_result), + move_local(end_result), + source, + ) + } else { + // valid >= start && valid <= end + body.new_binary_op( + BinOp::BitAnd, + move_local(start_result), + move_local(end_result), + source, + ) + } + } + + fn unsupported_check( + &self, + tcx: TyCtxt, + body: &mut MutableBody, + source: &mut SourceInstruction, + reason: &str, + ) { + let span = source.span(body.blocks()); + let rvalue = Rvalue::Use(Operand::Constant(Constant { + literal: Const::from_bool(false), + span, + user_ty: None, + })); + let result = body.new_assignment(rvalue, source); + body.add_check(tcx, &self.check_type, source, result, reason); + } +} + +fn move_local(local: Local) -> Operand { + Operand::Move(Place::from(local)) +} + +fn uint_ty(bytes: usize) -> UintTy { + match bytes { + 1 => UintTy::U8, + 2 => UintTy::U16, + 4 => UintTy::U32, + 8 => UintTy::U64, + 16 => UintTy::U128, + _ => unreachable!("Unexpected size: {bytes}"), + } +} + +/// Represent a requirement for the value stored in the given offset. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +struct ValidValueReq { + /// Offset in bytes. + offset: usize, + /// Size of this requirement. + size: MachineSize, + /// The range restriction is represented by a Scalar. + valid_range: WrappingRange, +} + +// TODO: Optimize checks by merging requirements whenever possible. +// There are a few cases that would need to be cover: +// 1- Ranges intersection is the same as one of the ranges (or both). +// 2- Ranges intersection is a new valid range. +// 3- Ranges intersection is a combination of two new ranges. +// 4- Intersection is empty. +impl ValidValueReq { + /// Only a type with `ValueAbi::Scalar` and `ValueAbi::ScalarPair` can be directly assigned an + /// invalid value directly. + /// + /// It's not possible to define a `rustc_layout_scalar_valid_range_*` to any other structure. + /// Note that this annotation only applies to the first scalar in the layout. + pub fn try_from_ty(machine_info: &MachineInfo, ty: Ty) -> Option { + let shape = ty.layout().unwrap().shape(); + match shape.abi { + ValueAbi::Scalar(Scalar::Initialized { value, valid_range }) + | ValueAbi::ScalarPair(Scalar::Initialized { value, valid_range }, _) => { + Some(ValidValueReq { offset: 0, size: value.size(machine_info), valid_range }) + } + ValueAbi::Scalar(_) + | ValueAbi::ScalarPair(_, _) + | ValueAbi::Uninhabited + | ValueAbi::Vector { .. } + | ValueAbi::Aggregate { .. } => None, + } + } + + /// Check if range is full. + pub fn is_full(&self) -> bool { + self.valid_range.is_full(self.size).unwrap() + } + + /// Check if this range contains `other` range. + /// + /// I.e., `scalar_2` ⊆ `scalar_1` + pub fn contains(&self, other: &ValidValueReq) -> bool { + assert_eq!(self.size, other.size); + match (self.valid_range.wraps_around(), other.valid_range.wraps_around()) { + (true, true) | (false, false) => { + self.valid_range.start <= other.valid_range.start + && self.valid_range.end >= other.valid_range.end + } + (true, false) => { + self.valid_range.start <= other.valid_range.start + || self.valid_range.end >= other.valid_range.end + } + (false, true) => self.is_full(), + } + } +} + +#[derive(AsRefStr, Clone, Debug)] +enum SourceOp { + /// Validity checks are done on a byte level when the Rvalue can generate invalid value. + /// + /// This variant tracks a location that is valid for its current type, but it may not be + /// valid for the given location in target type. This happens for: + /// - Transmute + /// - Field assignment + /// - Aggregate assignment + /// - Union Access + /// + /// Each range is a pair of offset and scalar that represents the valid values. + /// Note that the same offset may have multiple ranges that may require being joined. + BytesValidity { target_ty: Ty, rvalue: Rvalue, ranges: Vec }, + + /// Similar to BytesValidity, but it stores any dereference that may be unsafe. + /// + /// This can happen for: + /// - Raw pointer dereference + DerefValidity { pointee_ty: Ty, rvalue: Rvalue, ranges: Vec }, + + /// Represents a range check Kani currently does not support. + /// + /// This will translate into an assertion failure with an unsupported message. + /// There are many corner cases with the usage of #[rustc_layout_scalar_valid_range_*] + /// attribute. Such as valid ranges that do not intersect or enumeration with variants + /// with niche. + /// + /// Supporting all cases require significant work, and it is unlikely to exist in real world + /// code. To be on the sound side, we just emit an unsupported check, and users will need to + /// disable the check in person, and create a feature request for their case. + /// + /// TODO: Consider replacing the assertion(false) by an unsupported operation that emits a + /// compilation warning. + UnsupportedCheck { check: String, ty: Ty }, +} + +/// The unsafe instructions that may generate invalid values. +/// We need to instrument all operations to ensure the instruction is safe. +#[derive(Clone, Debug)] +struct UnsafeInstruction { + /// The instruction that depends on the potentially invalid value. + source: SourceInstruction, + /// The unsafe operations that may cause an invalid value in this instruction. + operations: Vec, +} + +/// Extract any source that may potentially trigger UB due to the generation of an invalid value. +/// +/// Generating an invalid value requires an unsafe operation, however, in MIR, it +/// may just be represented as a regular assignment. +/// +/// Thus, we have to instrument every assignment to an object that has niche and that the source +/// is an object of a different source, e.g.: +/// - Aggregate assignment +/// - Transmute +/// - MemCopy +/// - Cast +struct CheckValueVisitor<'a> { + locals: &'a [LocalDecl], + /// Whether we should skip the next instruction, since it might've been instrumented already. + /// When we instrument an instruction, we partition the basic block, and the instruction that + /// may trigger UB becomes the first instruction of the basic block, which we need to skip + /// later. + skip_next: bool, + /// The instruction being visited at a given point. + current: SourceInstruction, + /// The target instruction that should be verified. + pub target: Option, + /// The basic block being visited. + bb: BasicBlockIdx, + /// Machine information needed to calculate Niche. + machine: MachineInfo, +} + +impl<'a> CheckValueVisitor<'a> { + fn find_next( + body: &'a MutableBody, + bb: BasicBlockIdx, + skip_first: bool, + ) -> Option { + let mut visitor = CheckValueVisitor { + locals: body.locals(), + skip_next: skip_first, + current: SourceInstruction::Statement { idx: 0, bb }, + target: None, + bb, + machine: MachineInfo::target(), + }; + visitor.visit_basic_block(&body.blocks()[bb]); + visitor.target + } + + fn push_target(&mut self, op: SourceOp) { + let target = self + .target + .get_or_insert_with(|| UnsafeInstruction { source: self.current, operations: vec![] }); + target.operations.push(op); + } +} + +impl<'a> MirVisitor for CheckValueVisitor<'a> { + fn visit_statement(&mut self, stmt: &Statement, location: Location) { + if self.skip_next { + self.skip_next = false; + } else if self.target.is_none() { + // Leave it as an exhaustive match to be notified when a new kind is added. + match &stmt.kind { + StatementKind::Intrinsic(NonDivergingIntrinsic::CopyNonOverlapping(_)) => { + // Source and destination have the same type, so no invalid value cannot be + // generated. + } + StatementKind::Assign(place, rvalue) => { + // First check rvalue. + self.super_statement(stmt, location); + // Then check the destination place. + let ranges = assignment_check_points( + &self.machine, + self.locals, + place, + rvalue.ty(self.locals).unwrap(), + ); + if !ranges.is_empty() { + self.push_target(SourceOp::BytesValidity { + target_ty: self.locals[place.local].ty, + rvalue: rvalue.clone(), + ranges, + }); + } + } + StatementKind::FakeRead(_, _) + | StatementKind::SetDiscriminant { .. } + | StatementKind::Deinit(_) + | StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Retag(_, _) + | StatementKind::PlaceMention(_) + | StatementKind::AscribeUserType { .. } + | StatementKind::Coverage(_) + | StatementKind::ConstEvalCounter + | StatementKind::Intrinsic(NonDivergingIntrinsic::Assume(_)) + | StatementKind::Nop => self.super_statement(stmt, location), + } + } + + let SourceInstruction::Statement { idx, bb } = self.current else { unreachable!() }; + self.current = SourceInstruction::Statement { idx: idx + 1, bb }; + } + fn visit_terminator(&mut self, term: &Terminator, location: Location) { + if !(self.skip_next || self.target.is_some()) { + self.current = SourceInstruction::Terminator { bb: self.bb }; + // Leave it as an exhaustive match to be notified when a new kind is added. + match &term.kind { + TerminatorKind::Call { func, args, .. } => { + // Note: For transmute, both Src and Dst must be valid type. + // In this case, we need to save the Dst, and invoke super_terminator. + self.super_terminator(term, location); + let instance = expect_instance(self.locals, func); + if instance.kind == InstanceKind::Intrinsic { + match instance.intrinsic_name().unwrap().as_str() { + "write_bytes" => { + // The write bytes intrinsic may trigger UB in safe code. + // pub unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) + // + // We don't support this operation yet. + let TyKind::RigidTy(RigidTy::RawPtr(target_ty, Mutability::Mut)) = + args[0].ty(self.locals).unwrap().kind() + else { + unreachable!() + }; + let validity = ty_validity_per_offset(&self.machine, target_ty, 0); + match validity { + Ok(ranges) if ranges.is_empty() => {} + _ => self.push_target(SourceOp::UnsupportedCheck { + check: "write_bytes".to_string(), + ty: target_ty, + }), + } + } + "transmute" | "transmute_copy" => { + unreachable!("Should've been lowered") + } + _ => {} + } + } + } + TerminatorKind::Goto { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } + | TerminatorKind::Assert { .. } + | TerminatorKind::InlineAsm { .. } => self.super_terminator(term, location), + } + } + } + + fn visit_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) { + for (idx, elem) in place.projection.iter().enumerate() { + let place_ref = PlaceRef { local: place.local, projection: &place.projection[..idx] }; + match elem { + ProjectionElem::Deref => { + let ptr_ty = place_ref.ty(self.locals).unwrap(); + if ptr_ty.kind().is_raw_ptr() { + let target_ty = elem.ty(ptr_ty).unwrap(); + let validity = ty_validity_per_offset(&self.machine, target_ty, 0); + match validity { + Ok(ranges) if !ranges.is_empty() => { + self.push_target(SourceOp::DerefValidity { + pointee_ty: target_ty, + rvalue: Rvalue::Use( + Operand::Copy(Place { + local: place_ref.local, + projection: place_ref.projection.to_vec(), + }) + .clone(), + ), + ranges, + }) + } + Err(_msg) => self.push_target(SourceOp::UnsupportedCheck { + check: "raw pointer dereference".to_string(), + ty: target_ty, + }), + _ => {} + } + } + } + ProjectionElem::Field(idx, target_ty) => { + if target_ty.kind().is_union() + && (!ptx.is_mutating() || place.projection.len() > idx + 1) + { + let validity = ty_validity_per_offset(&self.machine, *target_ty, 0); + match validity { + Ok(ranges) if !ranges.is_empty() => { + self.push_target(SourceOp::BytesValidity { + target_ty: *target_ty, + rvalue: Rvalue::Use(Operand::Copy(Place { + local: place_ref.local, + projection: place_ref.projection.to_vec(), + })), + ranges, + }) + } + Err(_msg) => self.push_target(SourceOp::UnsupportedCheck { + check: "union access".to_string(), + ty: *target_ty, + }), + _ => {} + } + } + } + ProjectionElem::Downcast(_) => {} + ProjectionElem::OpaqueCast(_) => {} + ProjectionElem::Subtype(_) => {} + ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } => { /* safe */ } + } + } + self.super_place(place, ptx, location) + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue, location: Location) { + match rvalue { + Rvalue::Cast(kind, op, dest_ty) => match kind { + CastKind::PtrToPtr => { + // For mutable raw pointer, if the type we are casting to is less restrictive + // than the original type, writing to the pointer could generate UB if the + // value is ever read again using the original pointer. + let TyKind::RigidTy(RigidTy::RawPtr(dest_pointee_ty, Mutability::Mut)) = + dest_ty.kind() + else { + // We only care about *mut T as *mut U + return; + }; + let src_ty = op.ty(self.locals).unwrap(); + debug!(?src_ty, ?dest_ty, "visit_rvalue mutcast"); + let TyKind::RigidTy(RigidTy::RawPtr(src_pointee_ty, _)) = src_ty.kind() else { + unreachable!() + }; + if let Ok(src_validity) = + ty_validity_per_offset(&self.machine, src_pointee_ty, 0) + { + if !src_validity.is_empty() { + if let Ok(dest_validity) = + ty_validity_per_offset(&self.machine, dest_pointee_ty, 0) + { + if dest_validity != src_validity { + self.push_target(SourceOp::UnsupportedCheck { + check: "mutable cast".to_string(), + ty: src_ty, + }) + } + } else { + self.push_target(SourceOp::UnsupportedCheck { + check: "mutable cast".to_string(), + ty: *dest_ty, + }) + } + } + } else { + self.push_target(SourceOp::UnsupportedCheck { + check: "mutable cast".to_string(), + ty: src_ty, + }) + } + } + CastKind::Transmute => { + debug!(?dest_ty, "transmute"); + // For transmute, we care about the destination type only. + // This could be optimized to only add a check if the requirements of the + // destination type are stricter than the source. + if let Ok(dest_validity) = ty_validity_per_offset(&self.machine, *dest_ty, 0) { + trace!(?dest_validity, "transmute"); + if !dest_validity.is_empty() { + self.push_target(SourceOp::BytesValidity { + target_ty: *dest_ty, + rvalue: rvalue.clone(), + ranges: dest_validity, + }) + } + } else { + self.push_target(SourceOp::UnsupportedCheck { + check: "transmute".to_string(), + ty: *dest_ty, + }) + } + } + CastKind::DynStar => self.push_target(UnsupportedCheck { + check: "Dyn*".to_string(), + ty: (rvalue.ty(self.locals).unwrap()), + }), + CastKind::PointerExposeAddress + | CastKind::PointerFromExposedAddress + | CastKind::PointerCoercion(_) + | CastKind::IntToInt + | CastKind::FloatToInt + | CastKind::FloatToFloat + | CastKind::IntToFloat + | CastKind::FnPtrToPtr => {} + }, + Rvalue::ShallowInitBox(_, _) => { + // The contents of the box is considered uninitialized. + // This should already be covered by the Assign detection. + } + Rvalue::Aggregate(kind, operands) => match kind { + // If the aggregated structure has invalid value, this could generate invalid value. + // But only if the operands don't have the exact same restrictions. + // This happens today with the usage of `rustc_layout_scalar_valid_range_*` + // attributes. + // In this case, only the value of the first member in memory can be restricted, + // thus, we only need to check the operand used to assign to the first in memory + // field. + AggregateKind::Adt(def, _variant, args, _, _) => { + if def.kind() == AdtKind::Struct { + let dest_ty = Ty::from_rigid_kind(RigidTy::Adt(*def, args.clone())); + if let Some(req) = ValidValueReq::try_from_ty(&self.machine, dest_ty) + && !req.is_full() + { + let dest_layout = dest_ty.layout().unwrap().shape(); + let first_op = + first_aggregate_operand(dest_ty, &dest_layout.fields, operands); + let first_ty = first_op.ty(self.locals).unwrap(); + // Rvalue must have same Abi layout except for range. + if !req.contains( + &ValidValueReq::try_from_ty(&self.machine, first_ty).unwrap(), + ) { + self.push_target(SourceOp::BytesValidity { + target_ty: dest_ty, + rvalue: Rvalue::Use(first_op), + ranges: vec![req], + }) + } + } + } + } + // Only aggregate value. + AggregateKind::Array(_) + | AggregateKind::Closure(_, _) + | AggregateKind::Coroutine(_, _, _) + | AggregateKind::Tuple => {} + }, + Rvalue::AddressOf(_, _) + | Rvalue::BinaryOp(_, _, _) + | Rvalue::CheckedBinaryOp(_, _, _) + | Rvalue::CopyForDeref(_) + | Rvalue::Discriminant(_) + | Rvalue::Len(_) + | Rvalue::Ref(_, _, _) + | Rvalue::Repeat(_, _) + | Rvalue::ThreadLocalRef(_) + | Rvalue::NullaryOp(_, _) + | Rvalue::UnaryOp(_, _) + | Rvalue::Use(_) => {} + } + self.super_rvalue(rvalue, location); + } +} + +/// Gets the operand that corresponds to the assignment of the first sized field in memory. +/// +/// The first field of a structure is the only one that can have extra value restrictions imposed +/// by `rustc_layout_scalar_valid_range_*` attributes. +/// +/// Note: This requires at least one operand to be sized and there's a 1:1 match between operands +/// and field types. +fn first_aggregate_operand(dest_ty: Ty, dest_shape: &FieldsShape, operands: &[Operand]) -> Operand { + let Some(first) = first_sized_field_idx(dest_ty, dest_shape) else { unreachable!() }; + operands[first].clone() +} + +/// Index of the first non_1zst fields in memory order. +fn first_sized_field_idx(ty: Ty, shape: &FieldsShape) -> Option { + if let TyKind::RigidTy(RigidTy::Adt(adt_def, args)) = ty.kind() + && adt_def.kind() == AdtKind::Struct + { + let offset_order = shape.fields_by_offset_order(); + let fields = adt_def.variants_iter().next().unwrap().fields(); + offset_order + .into_iter() + .find(|idx| !fields[*idx].ty_with_args(&args).layout().unwrap().shape().is_1zst()) + } else { + None + } +} + +/// An assignment to a field with invalid values is unsafe, and it may trigger UB if +/// the assigned value is invalid. +/// +/// This can only happen to the first in memory sized field of a struct, and only if the field +/// type invalid range is a valid value for the rvalue type. +fn assignment_check_points( + machine_info: &MachineInfo, + locals: &[LocalDecl], + place: &Place, + rvalue_ty: Ty, +) -> Vec { + let mut ty = locals[place.local].ty; + let Some(rvalue_range) = ValidValueReq::try_from_ty(machine_info, rvalue_ty) else { + // Rvalue Abi must be Scalar / ScalarPair since destination must be Scalar / ScalarPair. + return vec![]; + }; + let mut invalid_ranges = vec![]; + for proj in &place.projection { + match proj { + ProjectionElem::Field(field_idx, field_ty) => { + let shape = ty.layout().unwrap().shape(); + if first_sized_field_idx(ty, &shape.fields) == Some(*field_idx) + && let Some(dest_valid) = ValidValueReq::try_from_ty(machine_info, ty) + && !dest_valid.is_full() + && dest_valid.size == rvalue_range.size + { + if !dest_valid.contains(&rvalue_range) { + invalid_ranges.push(dest_valid) + } + } else { + // Invalidate collected ranges so far since we are no longer in the path of + // the first element. + invalid_ranges.clear(); + } + ty = *field_ty; + } + ProjectionElem::Deref + | ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::Downcast(_) + | ProjectionElem::OpaqueCast(_) + | ProjectionElem::Subtype(_) => ty = proj.ty(ty).unwrap(), + }; + } + invalid_ranges +} + +/// Retrieve instance for the given function operand. +/// +/// This will panic if the operand is not a function or if it cannot be resolved. +fn expect_instance(locals: &[LocalDecl], func: &Operand) -> Instance { + let ty = func.ty(locals).unwrap(); + match ty.kind() { + TyKind::RigidTy(RigidTy::FnDef(def, args)) => Instance::resolve(def, &args).unwrap(), + _ => unreachable!(), + } +} + +/// Traverse the type and find all invalid values and their location in memory. +/// +/// Not all values are currently supported. For those not supported, we return Error. +fn ty_validity_per_offset( + machine_info: &MachineInfo, + ty: Ty, + current_offset: usize, +) -> Result, String> { + let layout = ty.layout().unwrap().shape(); + let ty_req = || { + if let Some(mut req) = ValidValueReq::try_from_ty(machine_info, ty) + && !req.is_full() + { + req.offset = current_offset; + vec![req] + } else { + vec![] + } + }; + match layout.fields { + FieldsShape::Primitive => Ok(ty_req()), + FieldsShape::Array { stride, count } if count > 0 => { + let TyKind::RigidTy(RigidTy::Array(elem_ty, _)) = ty.kind() else { unreachable!() }; + let elem_validity = ty_validity_per_offset(machine_info, elem_ty, current_offset)?; + let mut result = vec![]; + if !elem_validity.is_empty() { + for idx in 0..count { + let idx: usize = idx.try_into().unwrap(); + let elem_offset = idx * stride.bytes(); + let mut next_validity = elem_validity + .iter() + .cloned() + .map(|mut req| { + req.offset += elem_offset; + req + }) + .collect::>(); + result.append(&mut next_validity) + } + } + Ok(result) + } + FieldsShape::Arbitrary { ref offsets } => { + match ty.kind().rigid().unwrap() { + RigidTy::Adt(def, args) => { + match def.kind() { + AdtKind::Enum => { + // Support basic enumeration forms + let ty_variants = def.variants(); + match layout.variants { + VariantsShape::Single { index } => { + // Only one variant is reachable. This behaves like a struct. + let fields = ty_variants[index.to_index()].fields(); + 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); + fields_validity.append(&mut ty_validity_per_offset( + machine_info, + field_ty, + field_offset + current_offset, + )?); + } + Ok(fields_validity) + } + VariantsShape::Multiple { + tag_encoding: TagEncoding::Niche { .. }, + .. + } => { + Err(format!("Unsupported Enum `{}` check", def.trimmed_name()))? + } + VariantsShape::Multiple { variants, .. } => { + let enum_validity = ty_req(); + let mut fields_validity = vec![]; + for (index, variant) in variants.iter().enumerate() { + 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); + fields_validity.append(&mut ty_validity_per_offset( + machine_info, + field_ty, + field_offset + current_offset, + )?); + } + } + if fields_validity.is_empty() { + Ok(enum_validity) + } else { + Err(format!( + "Unsupported Enum `{}` check", + def.trimmed_name() + )) + } + } + } + } + AdtKind::Union => unreachable!(), + AdtKind::Struct => { + // If the struct range has niche add that. + let mut struct_validity = ty_req(); + 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); + struct_validity.append(&mut ty_validity_per_offset( + machine_info, + field_ty, + field_offset + current_offset, + )?); + } + Ok(struct_validity) + } + } + } + RigidTy::Tuple(tys) => { + let mut tuple_validity = vec![]; + for idx in layout.fields.fields_by_offset_order() { + let field_offset = offsets[idx].bytes(); + let field_ty = tys[idx]; + tuple_validity.append(&mut ty_validity_per_offset( + machine_info, + field_ty, + field_offset + current_offset, + )?); + } + Ok(tuple_validity) + } + RigidTy::Bool + | RigidTy::Char + | RigidTy::Int(_) + | RigidTy::Uint(_) + | RigidTy::Float(_) + | RigidTy::Never => { + unreachable!("Expected primitive layout for {ty:?}") + } + RigidTy::Str | RigidTy::Slice(_) | RigidTy::Array(_, _) => { + unreachable!("Expected array layout for {ty:?}") + } + RigidTy::RawPtr(_, _) | RigidTy::Ref(_, _, _) => { + // Fat pointer has arbitrary shape. + Ok(ty_req()) + } + RigidTy::FnDef(_, _) + | RigidTy::FnPtr(_) + | RigidTy::Closure(_, _) + | RigidTy::Coroutine(_, _, _) + | RigidTy::CoroutineWitness(_, _) + | RigidTy::Foreign(_) + | RigidTy::Dynamic(_, _, _) => Err(format!("Unsupported {ty:?}")), + } + } + FieldsShape::Union(_) | FieldsShape::Array { .. } => { + /* Anything is valid */ + Ok(vec![]) + } + } +} diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs new file mode 100644 index 000000000000..a6cd17e8c7db --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -0,0 +1,135 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +//! This module is responsible for optimizing and instrumenting function bodies. +//! +//! We make transformations on bodies already monomorphized, which allow us to make stronger +//! decisions based on the instance types and constants. +//! +//! The main downside is that some transformation that don't depend on the specialized type may be +//! applied multiple times, one per specialization. +//! +//! Another downside is that these modifications cannot be applied to concrete playback, since they +//! are applied on the top of StableMIR body, which cannot be propagated back to rustc's backend. +//! +//! # Warn +//! +//! For all instrumentation passes, always use exhaustive matches to ensure soundness in case a new +//! case is added. +use crate::kani_middle::transform::check_values::ValidValuePass; +use crate::kani_queries::QueryDb; +use rustc_middle::ty::TyCtxt; +use stable_mir::mir::mono::Instance; +use stable_mir::mir::Body; +use std::collections::HashMap; +use std::fmt::Debug; + +mod body; +mod check_values; + +/// Object used to retrieve a transformed instance body. +/// The transformations to be applied may be controlled by user options. +/// +/// The order however is always the same, we run optimizations first, and instrument the code +/// after. +#[derive(Debug)] +pub struct BodyTransformation { + /// The passes that may optimize the function body. + /// We store them separately from the instrumentation passes because we run the in specific order. + opt_passes: Vec>, + /// The passes that may add safety checks to the function body. + inst_passes: Vec>, + /// Cache transformation results. + cache: HashMap, +} + +impl BodyTransformation { + pub fn new(queries: &QueryDb, tcx: TyCtxt) -> Self { + let mut transformer = BodyTransformation { + opt_passes: vec![], + inst_passes: vec![], + cache: Default::default(), + }; + transformer.add_pass(queries, ValidValuePass::new(tcx)); + transformer + } + + /// Allow the creation of a dummy transformer that doesn't apply any transformation due to + /// the stubbing validation hack (see `collect_and_partition_mono_items` override. + /// Once we move the stubbing logic to a [TransformPass], we should be able to remove this. + pub fn dummy() -> Self { + BodyTransformation { opt_passes: vec![], inst_passes: vec![], cache: Default::default() } + } + + /// Retrieve the body of an instance. + /// + /// Note that this assumes that the instance does have a body since existing consumers already + /// assume that. Use `instance.has_body()` to check if an instance has a body. + pub fn body(&mut self, tcx: TyCtxt, instance: Instance) -> Body { + match self.cache.get(&instance) { + Some(TransformationResult::Modified(body)) => body.clone(), + Some(TransformationResult::NotModified) => instance.body().unwrap(), + None => { + let mut body = instance.body().unwrap(); + let mut modified = false; + for pass in self.opt_passes.iter().chain(self.inst_passes.iter()) { + let result = pass.transform(tcx, body, instance); + modified |= result.0; + body = result.1; + } + + let result = if modified { + TransformationResult::Modified(body.clone()) + } else { + TransformationResult::NotModified + }; + self.cache.insert(instance, result); + body + } + } + } + + fn add_pass(&mut self, query_db: &QueryDb, pass: P) { + if pass.is_enabled(&query_db) { + match P::transformation_type() { + TransformationType::Instrumentation => self.inst_passes.push(Box::new(pass)), + TransformationType::Optimization => { + unreachable!() + } + } + } + } +} + +/// The type of transformation that a pass may perform. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +enum TransformationType { + /// Should only add assertion checks to ensure the program is correct. + Instrumentation, + /// May replace inefficient code with more performant but equivalent code. + #[allow(dead_code)] + Optimization, +} + +/// A trait to represent transformation passes that can be used to modify the body of a function. +trait TransformPass: Debug { + /// The type of transformation that this pass implements. + fn transformation_type() -> TransformationType + where + Self: Sized; + + fn is_enabled(&self, query_db: &QueryDb) -> bool + where + Self: Sized; + + /// Run a transformation pass in the function body. + fn transform(&self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body); +} + +/// The transformation result. +/// We currently only cache the body of functions that were instrumented. +#[derive(Clone, Debug)] +enum TransformationResult { + Modified(Body), + NotModified, +} diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index 4e8086e7e37b..24691943bc0f 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use anyhow::Result; +use kani_metadata::UnstableFeature; use std::ffi::OsString; use std::path::{Path, PathBuf}; use std::process::Command; @@ -100,6 +101,10 @@ impl KaniSession { flags.push("--coverage-checks".into()); } + if self.args.common_args.unstable_features.contains(UnstableFeature::ValidValueChecks) { + flags.push("--ub-check=validity".into()) + } + flags.extend(self.args.common_args.unstable_features.as_arguments().map(str::to_string)); // This argument will select the Kani flavour of the compiler. It will be removed before diff --git a/kani_metadata/src/unstable.rs b/kani_metadata/src/unstable.rs index 3820f3f2238e..878b468dbdc3 100644 --- a/kani_metadata/src/unstable.rs +++ b/kani_metadata/src/unstable.rs @@ -84,6 +84,9 @@ pub enum UnstableFeature { FunctionContracts, /// Memory predicate APIs. MemPredicates, + /// Automatically check that no invalid value is produced which is considered UB in Rust. + /// Note that this does not include checking uninitialized value. + ValidValueChecks, } impl UnstableFeature { diff --git a/tests/kani/ValidValues/constants.rs b/tests/kani/ValidValues/constants.rs new file mode 100644 index 000000000000..5230e6e5e6cb --- /dev/null +++ b/tests/kani/ValidValues/constants.rs @@ -0,0 +1,40 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z valid-value-checks +//! Check that Kani can identify UB when it is reading from a constant. +//! Note that this UB will be removed for `-Z mir-opt-level=2` + +#[kani::proof] +fn transmute_valid_bool() { + let _b = unsafe { std::mem::transmute::(1) }; +} + +#[kani::proof] +fn cast_to_valid_char() { + let _c = unsafe { *(&100u32 as *const u32 as *const char) }; +} + +#[kani::proof] +fn cast_to_valid_offset() { + let val = [100u32, 80u32]; + let _c = unsafe { *(&val as *const [u32; 2] as *const [char; 2]) }; +} + +#[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/kani/ValidValues/custom_niche.rs b/tests/kani/ValidValues/custom_niche.rs new file mode 100644 index 000000000000..02b5b87bd092 --- /dev/null +++ b/tests/kani/ValidValues/custom_niche.rs @@ -0,0 +1,100 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z valid-value-checks +//! Check that Kani can identify UB when using niche attribute for a custom operation. +#![feature(rustc_attrs)] + +use std::mem; +use std::mem::size_of; + +/// A possible implementation for a system of rating that defines niche. +/// A Rating represents the number of stars of a given product (1..=5). +#[rustc_layout_scalar_valid_range_start(1)] +#[rustc_layout_scalar_valid_range_end(5)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Rating { + stars: u8, +} + +impl kani::Arbitrary for Rating { + fn any() -> Self { + let stars = kani::any_where(|s: &u8| *s >= 1 && *s <= 5); + unsafe { Rating { stars } } + } +} + +impl Rating { + /// Buggy version of new. Note that this still creates an invalid Rating. + /// + /// This is because `then_some` eagerly create the Rating value before assessing the condition. + /// Even though the value is never used, it is still considered UB. + pub fn new(value: u8) -> Option { + (value > 0 && value <= 5).then_some(unsafe { Rating { stars: value } }) + } + + pub unsafe fn new_unchecked(stars: u8) -> Rating { + Rating { stars } + } +} + +#[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) }; +} + +#[kani::proof] +#[kani::should_panic] +pub fn check_invalid_increment() { + let mut orig: Rating = kani::any(); + unsafe { orig.stars += 1 }; +} + +#[kani::proof] +pub fn check_valid_increment() { + let mut orig: Rating = kani::any(); + kani::assume(orig.stars < 5); + unsafe { orig.stars += 1 }; +} + +/// Check that the compiler relies on valid value range of Rating to implement niche optimization. +#[kani::proof] +pub fn check_niche() { + assert_eq!(size_of::(), size_of::>()); + assert_eq!(size_of::(), size_of::>>()); +} diff --git a/tests/kani/ValidValues/non_null.rs b/tests/kani/ValidValues/non_null.rs new file mode 100644 index 000000000000..4874b61bf2d0 --- /dev/null +++ b/tests/kani/ValidValues/non_null.rs @@ -0,0 +1,26 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z valid-value-checks +//! Check that Kani can identify UB when unsafely writing to NonNull. + +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. + assert_ne!(unsafe { nn.as_ref() }, &10); +} + +#[kani::proof] +pub fn check_valid_dangling() { + let _ = unsafe { NonNull::new_unchecked(4 as *mut u32) }; +} diff --git a/tests/kani/ValidValues/write_invalid.rs b/tests/kani/ValidValues/write_invalid.rs new file mode 100644 index 000000000000..05d3705bd69a --- /dev/null +++ b/tests/kani/ValidValues/write_invalid.rs @@ -0,0 +1,37 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z valid-value-checks + +//! Check that Kani can identify UB after writing an invalid value. +//! Writing invalid bytes is not UB as long as the incorrect value is not read. +//! However, we over-approximate for sake of simplicity and performance. + +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 _; + unsafe { std::intrinsics::write_bytes(dest, 0, 1) }; +} + +#[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(); + let dest = &mut non_zero as *mut _; + unsafe { std::intrinsics::write_bytes(dest, 0, 1) }; + unsafe { std::intrinsics::write_bytes(dest, non_zero_2.get(), 1) }; + assert_eq!(non_zero, non_zero_2) +} + +#[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 _; + unsafe { std::intrinsics::write_bytes(dest, 0, 1) }; + assert_eq!(non_zero.get(), 0) +} From 506bfc4dd693529a7b869d5b74b5ea033e1f529d Mon Sep 17 00:00:00 2001 From: Kareem Khazem Date: Tue, 26 Mar 2024 16:22:49 +0000 Subject: [PATCH 048/225] Add `benchcomp filter` command (#3105) This allows benchcomp to pass the list of results to an external program for modification before the results are visualized. This can be used, for example, to visualize only a relevant subset of results. 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/benchcomp-conf.md | 19 ++++ tools/benchcomp/benchcomp/__init__.py | 11 ++- tools/benchcomp/benchcomp/cmd_args.py | 19 +++- tools/benchcomp/benchcomp/entry/benchcomp.py | 2 + tools/benchcomp/benchcomp/entry/filter.py | 90 ++++++++++++++++++- tools/benchcomp/test/test_regression.py | 92 ++++++++++++++++++++ 6 files changed, 227 insertions(+), 6 deletions(-) diff --git a/docs/src/benchcomp-conf.md b/docs/src/benchcomp-conf.md index de57ac831221..065143a39c2c 100644 --- a/docs/src/benchcomp-conf.md +++ b/docs/src/benchcomp-conf.md @@ -24,6 +24,25 @@ variants: ``` +## Filters + +After benchcomp has finished parsing the results, it writes the results to `results.yaml` by default. +Before visualizing the results (see below), benchcomp can *filter* the results by piping them into an external program. + +To filter results before visualizing them, add `filters` to the configuration file. + +```yaml +filters: + - command_line: ./scripts/remove-redundant-results.py + - command_line: cat +``` + +The value of `filters` is a list of dicts. +Currently the only legal key for each of the dicts is `command_line`. +Benchcomp invokes each `command_line` in order, passing the results as a JSON file on stdin, and interprets the stdout as a YAML-formatted modified set of results. +Filter scripts can emit either YAML (which might be more readable while developing the script), or JSON (which benchcomp will parse as a subset of YAML). + + ## Built-in visualizations The following visualizations are available; these can be added to the `visualize` list of `benchcomp.yaml`. diff --git a/tools/benchcomp/benchcomp/__init__.py b/tools/benchcomp/benchcomp/__init__.py index eedd6ebc1f32..a82e50aae412 100644 --- a/tools/benchcomp/benchcomp/__init__.py +++ b/tools/benchcomp/benchcomp/__init__.py @@ -62,7 +62,16 @@ class ConfigFile(collections.UserDict): anyof: - schema: type: {} -filter: {} +filters: + type: list + default: [] + schema: + type: dict + keysrules: + type: string + allowed: ["command_line"] + valuesrules: + type: string visualize: {} """) diff --git a/tools/benchcomp/benchcomp/cmd_args.py b/tools/benchcomp/benchcomp/cmd_args.py index d8b7e735824f..8dd85f71080b 100644 --- a/tools/benchcomp/benchcomp/cmd_args.py +++ b/tools/benchcomp/benchcomp/cmd_args.py @@ -170,7 +170,13 @@ def _get_args_dict(): }, "filter": { "help": "transform a result by piping it through a program", - "args": [], + "args": [{ + "flags": ["--result-file"], + "metavar": "F", + "default": pathlib.Path("result.yaml"), + "type": pathlib.Path, + "help": "read result from F instead of %(default)s." + }], }, "visualize": { "help": "render a result in various formats", @@ -180,7 +186,7 @@ def _get_args_dict(): "default": pathlib.Path("result.yaml"), "type": pathlib.Path, "help": - "read result from F instead of %(default)s. " + "read result from F instead of %(default)s." }, { "flags": ["--only"], "nargs": "+", @@ -234,6 +240,11 @@ def get(): subparsers = ad["subparsers"].pop("parsers") subs = parser.add_subparsers(**ad["subparsers"]) + + # Add all subcommand-specific flags to the top-level argument parser, + # but only add them once. + flag_set = set() + for subcommand, info in subparsers.items(): args = info.pop("args") subparser = subs.add_parser(name=subcommand, **info) @@ -246,7 +257,9 @@ def get(): for arg in args: flags = arg.pop("flags") subparser.add_argument(*flags, **arg) - if arg not in global_args: + long_flag = flags[-1] + if arg not in global_args and long_flag not in flag_set: + flag_set.add(long_flag) parser.add_argument(*flags, **arg) return parser.parse_args() diff --git a/tools/benchcomp/benchcomp/entry/benchcomp.py b/tools/benchcomp/benchcomp/entry/benchcomp.py index e64f9699b3eb..9b8c1443c01d 100644 --- a/tools/benchcomp/benchcomp/entry/benchcomp.py +++ b/tools/benchcomp/benchcomp/entry/benchcomp.py @@ -16,4 +16,6 @@ def main(args): args.suites_dir = run_result.out_prefix / run_result.out_symlink results = benchcomp.entry.collate.main(args) + results = benchcomp.entry.filter.main(args) + benchcomp.entry.visualize.main(args) diff --git a/tools/benchcomp/benchcomp/entry/filter.py b/tools/benchcomp/benchcomp/entry/filter.py index 8523a198ce6a..c29a1795194b 100644 --- a/tools/benchcomp/benchcomp/entry/filter.py +++ b/tools/benchcomp/benchcomp/entry/filter.py @@ -4,5 +4,91 @@ # Entrypoint for `benchcomp filter` -def main(_): - raise NotImplementedError # TODO +import json +import logging +import pathlib +import subprocess +import sys +import tempfile + +import yaml + + +def main(args): + """Filter the results file by piping it into a list of scripts""" + + with open(args.result_file) as handle: + old_results = yaml.safe_load(handle) + + if "filters" not in args.config: + return old_results + + tmp_root = pathlib.Path(tempfile.gettempdir()) / "benchcomp" / "filter" + tmp_root.mkdir(parents=True, exist_ok=True) + tmpdir = pathlib.Path(tempfile.mkdtemp(dir=str(tmp_root))) + + for idx, filt in enumerate(args.config["filters"]): + with open(args.result_file) as handle: + old_results = yaml.safe_load(handle) + + json_results = json.dumps(old_results, indent=2) + in_file = tmpdir / f"{idx}.in.json" + out_file = tmpdir / f"{idx}.out.json" + cmd_out = _pipe( + filt["command_line"], json_results, in_file, out_file) + + try: + new_results = yaml.safe_load(cmd_out) + except yaml.YAMLError as exc: + logging.exception( + "Filter command '%s' produced invalid YAML. Stdin of" + " the command is saved in %s, stdout is saved in %s.", + filt["command_line"], in_file, out_file) + if hasattr(exc, "problem_mark"): + logging.error( + "Parse error location: line %d, column %d", + exc.problem_mark.line+1, exc.problem_mark.column+1) + sys.exit(1) + + with open(args.result_file, "w") as handle: + yaml.dump(new_results, handle, default_flow_style=False, indent=2) + + return new_results + + +def _pipe(shell_command, in_text, in_file, out_file): + """Pipe `in_text` into `shell_command` and return the output text + + Save the in and out text into files for later inspection if necessary. + """ + + with open(in_file, "w") as handle: + print(in_text, file=handle) + + logging.debug( + "Piping the contents of '%s' into '%s', saving into '%s'", + in_file, shell_command, out_file) + + timeout = 60 + with subprocess.Popen( + shell_command, shell=True, text=True, stdin=subprocess.PIPE, + stdout=subprocess.PIPE) as proc: + try: + out, _ = proc.communicate(input=in_text, timeout=timeout) + except subprocess.TimeoutExpired: + logging.error( + "Filter command failed to terminate after %ds: '%s'", + timeout, shell_command) + sys.exit(1) + + with open(out_file, "w") as handle: + print(out, file=handle) + + if proc.returncode: + logging.error( + "Filter command '%s' exited with code %d. Stdin of" + " the command is saved in %s, stdout is saved in %s.", + shell_command, proc.returncode, in_file, out_file) + sys.exit(1) + + return out diff --git a/tools/benchcomp/test/test_regression.py b/tools/benchcomp/test/test_regression.py index 55730bc0463f..ccf2259f7f0b 100644 --- a/tools/benchcomp/test/test_regression.py +++ b/tools/benchcomp/test/test_regression.py @@ -662,6 +662,98 @@ def test_return_0_on_fail(self): result = yaml.safe_load(handle) + def test_bad_filters(self): + """Ensure that bad filters terminate benchcomp""" + + with tempfile.TemporaryDirectory() as tmp: + run_bc = Benchcomp({ + "variants": { + "variant-1": { + "config": { + "command_line": "true", + "directory": tmp, + "env": {}, + } + }, + }, + "run": { + "suites": { + "suite_1": { + "parser": { + "command": textwrap.dedent("""\ + echo '{ + "benchmarks": { }, + "metrics": { } + }' + """) + }, + "variants": ["variant-1"] + } + } + }, + "filters": [{ + "command_line": "false" + }], + "visualize": [], + }) + run_bc() + self.assertEqual(run_bc.proc.returncode, 1, msg=run_bc.stderr) + + + def test_two_filters(self): + """Ensure that the output can be filtered""" + + with tempfile.TemporaryDirectory() as tmp: + run_bc = Benchcomp({ + "variants": { + "variant-1": { + "config": { + "command_line": "true", + "directory": tmp, + "env": {}, + } + }, + }, + "run": { + "suites": { + "suite_1": { + "parser": { + "command": textwrap.dedent("""\ + echo '{ + "benchmarks": { + "bench-1": { + "variants": { + "variant-1": { + "metrics": { + "runtime": 10, + "memory": 5 + } + } + } + } + }, + "metrics": { + "runtime": {}, + "memory": {}, + } + }' + """) + }, + "variants": ["variant-1"] + } + } + }, + "filters": [{ + "command_line": "sed -e 's/10/20/;s/5/10/'" + }, { + "command_line": """grep '"runtime": 20'""" + }], + "visualize": [], + }) + run_bc() + self.assertEqual(run_bc.proc.returncode, 0, msg=run_bc.stderr) + + def test_env_expansion(self): """Ensure that config parser expands '${}' in env key""" From 1c3d0f342fd3b1084794d7d21935cb78b75ddda2 Mon Sep 17 00:00:00 2001 From: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> Date: Wed, 27 Mar 2024 12:02:08 -0400 Subject: [PATCH 049/225] Add CI test for --use-local-toolchain (#3074) Adds a CI test for --use-local-toolchain. This test will run after every push to main. Additional cleanup to the setup as mentioned in #3060 in next PR's. (In interest of keeping changes small & focused). 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/release.yml | 100 ++++++++++++++++++++++++++++++++++ src/setup.rs | 51 +++++++++++++++-- tools/build-kani/src/main.rs | 10 +++- 3 files changed, 155 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bd3e145e1bce..04ac4d20280b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -101,6 +101,106 @@ jobs: os: linux arch: x86_64-unknown-linux-gnu + test-use-local-toolchain: + name: TestLocalToolchain + needs: [build_bundle_macos, build_bundle_linux] + strategy: + matrix: + os: [macos-13, ubuntu-20.04, ubuntu-22.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 }} + runs-on: ${{ matrix.os }} + steps: + - name: Download bundle + uses: actions/download-artifact@v3 + with: + name: ${{ matrix.prev_job.bundle }} + + - name: Download kani-verifier crate + uses: actions/download-artifact@v3 + with: + name: ${{ matrix.prev_job.package }} + + - name: Check download + run: | + ls -lh . + + - name: Get toolchain version used to setup kani + run: | + tar zxvf ${{ matrix.prev_job.bundle }} + DATE=$(cat ./kani-latest/rust-toolchain-version | cut -d'-' -f2,3,4) + echo "Nightly date: $DATE" + echo "DATE=$DATE" >> $GITHUB_ENV + + - name: Install Kani from path + run: | + tar zxvf ${{ matrix.prev_job.package }} + cargo install --locked --path kani-verifier-${{ matrix.prev_job.crate_version }} + + - name: Create a custom toolchain directory + run: mkdir -p ${{ github.workspace }}/../custom_toolchain + + - name: Fetch the nightly tarball + run: | + echo "Downloading Rust toolchain from rust server." + curl --proto '=https' --tlsv1.2 -O https://static.rust-lang.org/dist/$DATE/rust-nightly-${{ matrix.rust_target }}.tar.gz + tar -xzf rust-nightly-${{ matrix.rust_target }}.tar.gz + ./rust-nightly-${{ matrix.rust_target }}/install.sh --prefix=${{ github.workspace }}/../custom_toolchain + + - name: Ensure installation is correct + run: | + cargo kani setup --use-local-bundle ./${{ matrix.prev_job.bundle }} --use-local-toolchain ${{ github.workspace }}/../custom_toolchain/ + + - name: Ensure that the rustup toolchain is not present + run: | + if [ ! -e "~/.rustup/toolchains/" ]; then + echo "Default toolchain file does not exist. Proceeding with running tests." + else + echo "::error::Default toolchain exists despite not installing." + exit 1 + fi + + - name: Checkout tests + uses: actions/checkout@v4 + + - name: Move rust-toolchain file to outside kani + run: | + mkdir -p ${{ github.workspace }}/../post-setup-tests + cp -r tests/cargo-ui ${{ github.workspace }}/../post-setup-tests + cp -r tests/kani/Assert ${{ github.workspace }}/../post-setup-tests + ls ${{ github.workspace }}/../post-setup-tests + + - name: Run cargo-kani tests after moving + run: | + for dir in function multiple-harnesses verbose; do + >&2 echo "Running test $dir" + pushd ${{ github.workspace }}/../post-setup-tests/cargo-ui/$dir + cargo kani + popd + done + + - name: Check --help and --version + run: | + >&2 echo "Running cargo kani --help and --version" + pushd ${{ github.workspace }}/../post-setup-tests/Assert + cargo kani --help && cargo kani --version + popd + + - name: Run standalone kani test + run: | + >&2 echo "Running test on file bool_ref" + pushd ${{ github.workspace }}/../post-setup-tests/Assert + kani bool_ref.rs + popd + test_bundle: name: TestBundle needs: [build_bundle_macos, build_bundle_linux] diff --git a/src/setup.rs b/src/setup.rs index 050a8e166334..730679f06c10 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -141,22 +141,42 @@ pub(crate) fn get_rust_toolchain_version(kani_dir: &Path) -> Result { .context("Reading release bundle rust-toolchain-version") } +pub(crate) fn get_rustc_version_from_build(kani_dir: &Path) -> Result { + std::fs::read_to_string(kani_dir.join("rustc-version")) + .context("Reading release bundle rustc-version") +} + /// Install the Rust toolchain version we require fn setup_rust_toolchain(kani_dir: &Path, use_local_toolchain: Option) -> Result { // Currently this means we require the bundle to have been unpacked first! let toolchain_version = get_rust_toolchain_version(kani_dir)?; - println!("[3/5] Installing rust toolchain version: {}", &toolchain_version); + let rustc_version = get_rustc_version_from_build(kani_dir)?.trim().to_string(); // Symlink to a local toolchain if the user explicitly requests if let Some(local_toolchain_path) = use_local_toolchain { let toolchain_path = Path::new(&local_toolchain_path); - // TODO: match the version against which kani was built - // Issue: https://github.com/model-checking/kani/issues/3060 - symlink_rust_toolchain(toolchain_path, kani_dir)?; - return Ok(toolchain_version); + + let custom_toolchain_rustc_version = + get_rustc_version_from_local_toolchain(local_toolchain_path.clone())?; + + if rustc_version == custom_toolchain_rustc_version { + symlink_rust_toolchain(toolchain_path, kani_dir)?; + println!( + "[3/5] Installing rust toolchain from path provided: {}", + &toolchain_path.to_string_lossy() + ); + return Ok(toolchain_version); + } else { + bail!( + "The toolchain with rustc {:?} being used to setup is not the same as the one Kani used in its release bundle {:?}. Try to setup with the same version as the bundle.", + custom_toolchain_rustc_version, + rustc_version, + ); + } } // This is the default behaviour when no explicit path to a toolchain is mentioned + println!("[3/5] Installing rust toolchain version: {}", &toolchain_version); Command::new("rustup").args(["toolchain", "install", &toolchain_version]).run()?; let toolchain = home::rustup_home()?.join("toolchains").join(&toolchain_version); symlink_rust_toolchain(&toolchain, kani_dir)?; @@ -189,6 +209,27 @@ fn download_filename() -> String { format!("kani-{VERSION}-{TARGET}.tar.gz") } +/// Get the version of rustc that is being used to setup kani by the user +fn get_rustc_version_from_local_toolchain(path: OsString) -> Result { + let path = Path::new(&path); + let rustc_path = path.join("bin").join("rustc"); + + let output = Command::new(rustc_path).arg("--version").output(); + + match output { + Ok(output) => { + if output.status.success() { + Ok(String::from_utf8(output.stdout).map(|s| s.trim().to_string())?) + } else { + bail!( + "Could not parse rustc version string. Toolchain installation likely invalid. " + ); + } + } + Err(_) => bail!("Could not get rustc version. Toolchain installation likely invalid"), + } +} + /// The download URL for this version of Kani fn download_url() -> String { let tag: &str = &format!("kani-{VERSION}"); diff --git a/tools/build-kani/src/main.rs b/tools/build-kani/src/main.rs index b94e951fe178..f65d52e03fd1 100644 --- a/tools/build-kani/src/main.rs +++ b/tools/build-kani/src/main.rs @@ -97,8 +97,9 @@ fn bundle_kani(dir: &Path) -> Result<()> { cp_dir(&kani_sysroot_lib(), dir)?; cp_dir(&kani_playback_lib().parent().unwrap(), dir)?; - // 5. Record the exact toolchain we use + // 5. Record the exact toolchain and rustc version we use std::fs::write(dir.join("rust-toolchain-version"), env!("RUSTUP_TOOLCHAIN"))?; + std::fs::write(dir.join("rustc-version"), get_rustc_version()?)?; // 6. Include a licensing note cp(Path::new("tools/build-kani/license-notes.txt"), dir)?; @@ -181,6 +182,13 @@ fn cp(src: &Path, dst: &Path) -> Result<()> { Ok(()) } +/// Record version of rustc being used to build Kani +fn get_rustc_version() -> Result { + let output = Command::new("rustc").arg("--version").output(); + let rustc_version = String::from_utf8(output.unwrap().stdout)?; + Ok(rustc_version) +} + /// Copy files from `src` to `dst` that respect the given pattern. pub fn cp_files

(src: &Path, dst: &Path, predicate: P) -> Result<()> where From f5f1e94e02c02b0f0f5038ad22f5b53fbb7511e0 Mon Sep 17 00:00:00 2001 From: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Date: Wed, 27 Mar 2024 17:18:35 -0400 Subject: [PATCH 050/225] Upgrade Rust toolchain to `nightly-2024-03-21` (#3102) Upgrades the Rust toolchain to `nightly-2024-03-21`. The relevant changes in Rust are: * https://github.com/rust-lang/rust/pull/122480 * https://github.com/rust-lang/rust/pull/122748 * https://github.com/rust-lang/cargo/pull/12783 I wasn't confident that our regression testing could detect differences in the file paths being generated with the new logic, so I added code similar to the following just to check they were equivalent: ```rust let base_filename = tcx.output_filenames(()).output_path(OutputType::Object); + let binding = tcx.output_filenames(()).path(OutputType::Object); + let base_filename2 = binding.as_path(); + assert_eq!(base_filename, base_filename2); ``` Note that this was done for each instance where we used `output_path`, and completed regression testing with the previous toolchain version. I also checked manually the instance where we generate a `.dot` graph for debug purposes following the instructions [here](https://model-checking.github.io/kani/cheat-sheets.html?highlight=dot#debug) (a `libmain.dot` file was generated for the `main.rs` in one of my projects). --------- Co-authored-by: Celina G. Val --- .../compiler_interface.rs | 6 +++-- kani-compiler/src/kani_compiler.rs | 11 +++++----- kani-compiler/src/kani_middle/reachability.rs | 3 ++- kani-compiler/src/kani_middle/stubbing/mod.rs | 3 ++- .../src/kani_middle/transform/check_values.rs | 2 +- kani-driver/src/call_cargo.rs | 22 ++++++++++++++++++- rust-toolchain.toml | 2 +- tests/cargo-ui/assess-error/expected | 2 +- ...rite_invalid.rs => write_invalid_fixme.rs} | 3 +++ 9 files changed, 41 insertions(+), 13 deletions(-) rename tests/kani/ValidValues/{write_invalid.rs => write_invalid_fixme.rs} (88%) diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index d3b4ae8473de..039c50eefe18 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -228,7 +228,8 @@ impl CodegenBackend for GotocCodegenBackend { // - Tests: Generate one model per test harnesses. // - PubFns: Generate code for all reachable logic starting from the local public functions. // - None: Don't generate code. This is used to compile dependencies. - let base_filename = tcx.output_filenames(()).output_path(OutputType::Object); + let base_filepath = tcx.output_filenames(()).path(OutputType::Object); + let base_filename = base_filepath.as_path(); let reachability = queries.args().reachability_analysis; let mut transformer = BodyTransformation::new(&queries, tcx); let mut results = GotoCodegenResults::new(tcx, reachability); @@ -412,7 +413,8 @@ impl CodegenBackend for GotocCodegenBackend { builder.build(&out_path); } else { // Write the location of the kani metadata file in the requested compiler output file. - let base_filename = outputs.output_path(OutputType::Object); + let base_filepath = outputs.path(OutputType::Object); + let base_filename = base_filepath.as_path(); let content_stub = CompilerArtifactStub { metadata_path: base_filename.with_extension(ArtifactType::Metadata), }; diff --git a/kani-compiler/src/kani_compiler.rs b/kani-compiler/src/kani_compiler.rs index 48b4318db5bf..fc5f5891ecae 100644 --- a/kani-compiler/src/kani_compiler.rs +++ b/kani-compiler/src/kani_compiler.rs @@ -304,7 +304,8 @@ impl KaniCompiler { }; if self.queries.lock().unwrap().args().reachability_analysis == ReachabilityType::Harnesses { - let base_filename = tcx.output_filenames(()).output_path(OutputType::Object); + 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() @@ -376,7 +377,7 @@ impl KaniCompiler { /// Write the metadata to a file fn store_metadata(&self, metadata: &KaniMetadata, filename: &Path) { - debug!(?filename, "write_metadata"); + debug!(?filename, "store_metadata"); let out_file = File::create(filename).unwrap(); let writer = BufWriter::new(out_file); if self.queries.lock().unwrap().args().output_pretty_json { @@ -457,9 +458,9 @@ fn generate_metadata( /// Extract the filename for the metadata file. fn metadata_output_path(tcx: TyCtxt) -> PathBuf { - let mut filename = tcx.output_filenames(()).output_path(OutputType::Object); - filename.set_extension(ArtifactType::Metadata); - filename + let filepath = tcx.output_filenames(()).path(OutputType::Object); + let filename = filepath.as_path(); + filename.with_extension(ArtifactType::Metadata).to_path_buf() } #[cfg(test)] diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index b46fbdccc016..52b55d6b947c 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -584,7 +584,8 @@ mod debug { if let Ok(target) = std::env::var("KANI_REACH_DEBUG") { debug!(?target, "dump_dot"); let outputs = tcx.output_filenames(()); - let path = outputs.output_path(OutputType::Metadata).with_extension("dot"); + let base_path = outputs.path(OutputType::Metadata); + let path = base_path.as_path().with_extension("dot"); let out_file = File::create(path)?; let mut writer = BufWriter::new(out_file); writeln!(writer, "digraph ReachabilityGraph {{")?; diff --git a/kani-compiler/src/kani_middle/stubbing/mod.rs b/kani-compiler/src/kani_middle/stubbing/mod.rs index 0db518ceef9f..37426bfe0b77 100644 --- a/kani-compiler/src/kani_middle/stubbing/mod.rs +++ b/kani-compiler/src/kani_middle/stubbing/mod.rs @@ -5,6 +5,7 @@ mod annotations; mod transform; +use rustc_span::DUMMY_SP; use std::collections::BTreeMap; use tracing::{debug, trace}; @@ -93,7 +94,7 @@ impl<'tcx> MirVisitor for StubConstChecker<'tcx> { Const::Val(..) | Const::Ty(..) => {} Const::Unevaluated(un_eval, _) => { // Thread local fall into this category. - if self.tcx.const_eval_resolve(ParamEnv::reveal_all(), un_eval, None).is_err() { + if self.tcx.const_eval_resolve(ParamEnv::reveal_all(), un_eval, DUMMY_SP).is_err() { // The `monomorphize` call should have evaluated that constant already. let tcx = self.tcx; let mono_const = &un_eval; diff --git a/kani-compiler/src/kani_middle/transform/check_values.rs b/kani-compiler/src/kani_middle/transform/check_values.rs index aefc20b46a44..4888e032af4f 100644 --- a/kani-compiler/src/kani_middle/transform/check_values.rs +++ b/kani-compiler/src/kani_middle/transform/check_values.rs @@ -807,7 +807,7 @@ fn ty_validity_per_offset( Ok(result) } FieldsShape::Arbitrary { ref offsets } => { - match ty.kind().rigid().unwrap() { + match ty.kind().rigid().expect(&format!("unexpected type: {ty:?}")) { RigidTy::Adt(def, args) => { match def.kind() { AdtKind::Enum => { diff --git a/kani-driver/src/call_cargo.rs b/kani-driver/src/call_cargo.rs index b68d18c84a40..9d27e5853696 100644 --- a/kani-driver/src/call_cargo.rs +++ b/kani-driver/src/call_cargo.rs @@ -211,7 +211,27 @@ impl KaniSession { } }, Message::CompilerArtifact(rustc_artifact) => { - if rustc_artifact.target == *target { + /// Compares two targets, and falls back to a weaker + /// comparison where we avoid dashes in their names. + fn same_target(t1: &Target, t2: &Target) -> bool { + (t1 == t2) + || (t1.name.replace('-', "_") == t2.name.replace('-', "_") + && t1.kind == t2.kind + && t1.src_path == t2.src_path + && t1.edition == t2.edition + && t1.doctest == t2.doctest + && t1.test == t2.test + && t1.doc == t2.doc) + } + // This used to be `rustc_artifact == *target`, but it + // started to fail after the `cargo` change in + // + // + // We should revisit this check after a while to see if + // it's not needed anymore or it can be restricted to + // certain cases. + // TODO: + if same_target(&rustc_artifact.target, target) { debug_assert!( artifact.is_none(), "expected only one artifact for `{target:?}`", diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 7035d1aba20f..d08e3a84f0ad 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-03-15" +channel = "nightly-2024-03-21" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/cargo-ui/assess-error/expected b/tests/cargo-ui/assess-error/expected index 70754ddea192..213d811ae577 100644 --- a/tests/cargo-ui/assess-error/expected +++ b/tests/cargo-ui/assess-error/expected @@ -1,2 +1,2 @@ -error: Failed to compile lib `compilation-error` +error: Failed to compile lib `compilation_error` error: Failed to assess project: Failed to build all targets diff --git a/tests/kani/ValidValues/write_invalid.rs b/tests/kani/ValidValues/write_invalid_fixme.rs similarity index 88% rename from tests/kani/ValidValues/write_invalid.rs rename to tests/kani/ValidValues/write_invalid_fixme.rs index 05d3705bd69a..f04a667a47d1 100644 --- a/tests/kani/ValidValues/write_invalid.rs +++ b/tests/kani/ValidValues/write_invalid_fixme.rs @@ -6,6 +6,9 @@ //! Writing invalid bytes is not UB as long as the incorrect value is not read. //! However, we over-approximate for sake of simplicity and performance. +// Note: We're getting an unexpected compilation error because the type returned +// from StableMIR is `Alias`: https://github.com/model-checking/kani/issues/3113 + use std::num::NonZeroU8; #[kani::proof] From d2b8c278e8a723af5512bc109e5298e8f43022f4 Mon Sep 17 00:00:00 2001 From: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Date: Wed, 27 Mar 2024 20:09:50 -0400 Subject: [PATCH 051/225] Use `intrinsic_name` to get the intrinsic name (#3114) Let's close the loop and use `intrinsic_name`, the StableMIR API we added in https://github.com/rust-lang/rust/pull/122203, to retrieve the intrinsic name and avoid post-processing it. --- .../codegen/intrinsic.rs | 23 +++---------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 2cabc4cfd273..8fce4b309820 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -69,7 +69,7 @@ impl<'tcx> GotocCtx<'tcx> { /// Handles codegen for non returning intrinsics /// Non returning intrinsics are not associated with a destination pub fn codegen_never_return_intrinsic(&mut self, instance: Instance, span: Span) -> Stmt { - let intrinsic = instance.mangled_name(); + let intrinsic = instance.intrinsic_name().unwrap(); debug!("codegen_never_return_intrinsic:\n\tinstance {:?}\n\tspan {:?}", instance, span); @@ -112,8 +112,8 @@ impl<'tcx> GotocCtx<'tcx> { place: &Place, span: Span, ) -> Stmt { - let intrinsic_sym = instance.trimmed_name(); - let intrinsic = intrinsic_sym.as_str(); + let intrinsic_name = instance.intrinsic_name().unwrap(); + let intrinsic = intrinsic_name.as_str(); let loc = self.codegen_span_stable(span); debug!(?instance, "codegen_intrinsic"); debug!(?fargs, "codegen_intrinsic"); @@ -288,23 +288,6 @@ impl<'tcx> GotocCtx<'tcx> { }}; } - /// Gets the basename of an intrinsic given its trimmed name. - /// - /// For example, given `arith_offset::` this returns `arith_offset`. - fn intrinsic_basename(name: &str) -> &str { - let scope_sep_count = name.matches("::").count(); - // We expect at most one `::` separator from trimmed intrinsic names - debug_assert!( - scope_sep_count < 2, - "expected at most one `::` in intrinsic name, but found {scope_sep_count} in `{name}`" - ); - let name_split = name.split_once("::"); - if let Some((base_name, _type_args)) = name_split { base_name } else { name } - } - // The trimmed name includes type arguments if the intrinsic was defined - // on generic types, but we only need the basename for the match below. - let intrinsic = intrinsic_basename(intrinsic); - if let Some(stripped) = intrinsic.strip_prefix("simd_shuffle") { assert!(fargs.len() == 3, "`simd_shuffle` had unexpected arguments {fargs:?}"); let n: u64 = self.simd_shuffle_length(stripped, farg_types, span); From 8401fbb782ed86ee7c760bd71013ea356da6c3ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 10:25:51 -0700 Subject: [PATCH 052/225] Bump tests/perf/s2n-quic from `0a60ec1` to `2d5e891` (#3118) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `0a60ec1` to `2d5e891`.

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 0a60ec19ea9a..2d5e891f3fdc 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 0a60ec19ea9a8cd1bef98ad9798327521d2121ee +Subproject commit 2d5e891f3fdc8a88b2d457baceedea5751efaa0d From 946ea8084c792a25247fe5cfaf977629887ace95 Mon Sep 17 00:00:00 2001 From: "Felipe R. Monteiro" Date: Tue, 2 Apr 2024 14:25:41 -0400 Subject: [PATCH 053/225] Allow modifies clause for verification only (#3098) Allow contracts to be used for verification, even if it is not suitable for stubbing. For that, we remove the requirement that modifies and return types of a function annotated with contracts must implement `kani::Arbitrary`, since that is only needed for recursion and stubbing with contract. This is done via a new intrinsic `any_modifies` to Kani that should only be used by contract instrumentation. The `T: Arbitrary` requirement is only checked when users try to use the contract as stub or to check recursive functions. For now, we also require users to annotate their contracts with `kani::recursion` if they want to use inductive reasoning to verify a recursive function. Resolves https://github.com/model-checking/kani/issues/2997. 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_middle/attributes.rs | 35 +++--- kani-compiler/src/kani_middle/mod.rs | 104 +----------------- kani-compiler/src/kani_middle/provide.rs | 13 +++ kani-compiler/src/kani_middle/reachability.rs | 19 ++++ .../src/kani_middle/stubbing/transform.rs | 51 ++++++++- library/kani/src/lib.rs | 16 +++ library/kani_macros/Cargo.toml | 4 + library/kani_macros/src/lib.rs | 12 ++ .../src/sysroot/contracts/bootstrap.rs | 7 +- .../src/sysroot/contracts/replace.rs | 13 +-- .../src/sysroot/contracts/shared.rs | 57 +--------- .../expected/function-contract/fail_on_two.rs | 2 + .../function-contract/gcd_rec_code_fail.rs | 1 + .../gcd_rec_comparison_pass.rs | 1 + .../function-contract/gcd_rec_simple_pass.rs | 1 + .../generic_infinity_recursion.rs | 1 + .../modifies/check_invalid_modifies.expected | 3 + .../modifies/check_invalid_modifies.rs | 27 +++++ .../modifies/check_only_verification.expected | 1 + .../modifies/check_only_verification.rs | 34 ++++++ .../fail_missing_recursion_attr.expected | 1 + .../modifies/fail_missing_recursion_attr.rs | 21 ++++ .../mistake_condition_return.expected | 3 + .../modifies/mistake_condition_return.rs | 39 +++++++ .../simple_only_verification.expected | 1 + .../modifies/simple_only_verification.rs | 22 ++++ .../prohibit-pointers/hidden.expected | 2 +- .../prohibit-pointers/plain_pointer.expected | 2 +- .../prohibit-pointers/return_pointer.expected | 2 +- .../prohibit-pointers/return_pointer.rs | 24 ++-- 30 files changed, 314 insertions(+), 205 deletions(-) create mode 100644 tests/expected/function-contract/modifies/check_invalid_modifies.expected create mode 100644 tests/expected/function-contract/modifies/check_invalid_modifies.rs create mode 100644 tests/expected/function-contract/modifies/check_only_verification.expected create mode 100644 tests/expected/function-contract/modifies/check_only_verification.rs create mode 100644 tests/expected/function-contract/modifies/fail_missing_recursion_attr.expected create mode 100644 tests/expected/function-contract/modifies/fail_missing_recursion_attr.rs create mode 100644 tests/expected/function-contract/modifies/mistake_condition_return.expected create mode 100644 tests/expected/function-contract/modifies/mistake_condition_return.rs create mode 100644 tests/expected/function-contract/modifies/simple_only_verification.expected create mode 100644 tests/expected/function-contract/modifies/simple_only_verification.rs diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index 979877199dde..4f9dce49af95 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -70,6 +70,10 @@ enum KaniAttributeKind { /// expanded with additional pointer arguments that are not used in the function /// but referenced by the `modifies` annotation. InnerCheck, + /// Attribute used to mark contracts for functions with recursion. + /// We use this attribute to properly instantiate `kani::any_modifies` in + /// cases when recursion is present given our contracts instrumentation. + Recursion, } impl KaniAttributeKind { @@ -84,6 +88,7 @@ impl KaniAttributeKind { | KaniAttributeKind::StubVerified | KaniAttributeKind::Unwind => true, KaniAttributeKind::Unstable + | KaniAttributeKind::Recursion | KaniAttributeKind::ReplacedWith | KaniAttributeKind::CheckedWith | KaniAttributeKind::Modifies @@ -102,13 +107,6 @@ impl KaniAttributeKind { pub fn demands_function_contract_use(self) -> bool { matches!(self, KaniAttributeKind::ProofForContract) } - - /// Would this attribute be placed on a function as part of a function - /// contract. E.g. created by `requires`, `ensures`. - pub fn is_function_contract(self) -> bool { - use KaniAttributeKind::*; - matches!(self, CheckedWith | IsContractGenerated) - } } /// Bundles together common data used when evaluating the attributes of a given @@ -200,6 +198,14 @@ impl<'tcx> KaniAttributes<'tcx> { .collect() } + pub(crate) fn is_contract_generated(&self) -> bool { + self.map.contains_key(&KaniAttributeKind::IsContractGenerated) + } + + pub(crate) fn has_recursion(&self) -> bool { + self.map.contains_key(&KaniAttributeKind::Recursion) + } + /// Parse and extract the `proof_for_contract(TARGET)` attribute. The /// returned symbol and DefId are respectively the name and id of `TARGET`, /// the span in the span for the attribute (contents). @@ -316,6 +322,12 @@ impl<'tcx> KaniAttributes<'tcx> { expect_no_args(self.tcx, kind, attr); }) } + KaniAttributeKind::Recursion => { + 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); attrs.iter().for_each(|attr| { @@ -452,6 +464,9 @@ impl<'tcx> KaniAttributes<'tcx> { self.map.iter().fold(HarnessAttributes::default(), |mut harness, (kind, attributes)| { match kind { KaniAttributeKind::ShouldPanic => harness.should_panic = true, + KaniAttributeKind::Recursion => { + self.tcx.dcx().span_err(self.tcx.def_span(self.item), "The attribute `kani::recursion` should only be used in combination with function contracts."); + }, KaniAttributeKind::Solver => { harness.solver = parse_solver(self.tcx, attributes[0]); } @@ -661,12 +676,6 @@ fn has_kani_attribute bool>( tcx.get_attrs_unchecked(def_id).iter().filter_map(|a| attr_kind(tcx, a)).any(predicate) } -/// Test if this function was generated by expanding a contract attribute like -/// `requires` and `ensures`. -pub fn is_function_contract_generated(tcx: TyCtxt, def_id: DefId) -> bool { - has_kani_attribute(tcx, def_id, KaniAttributeKind::is_function_contract) -} - /// Same as [`KaniAttributes::is_harness`] but more efficient because less /// attribute parsing is performed. pub fn is_proof_harness(tcx: TyCtxt, instance: InstanceStable) -> bool { diff --git a/kani-compiler/src/kani_middle/mod.rs b/kani-compiler/src/kani_middle/mod.rs index 4cd2d4a357d7..ab694ceec614 100644 --- a/kani-compiler/src/kani_middle/mod.rs +++ b/kani-compiler/src/kani_middle/mod.rs @@ -21,10 +21,8 @@ use rustc_span::source_map::respan; use rustc_span::Span; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{HasDataLayout, TargetDataLayout}; -use stable_mir::mir::mono::{Instance, InstanceKind, MonoItem}; -use stable_mir::mir::pretty::pretty_ty; -use stable_mir::ty::{BoundVariableKind, FnDef, RigidTy, Span as SpanStable, Ty, TyKind}; -use stable_mir::visitor::{Visitable, Visitor as TypeVisitor}; +use stable_mir::mir::mono::{InstanceKind, MonoItem}; +use stable_mir::ty::{FnDef, RigidTy, Span as SpanStable, TyKind}; use stable_mir::{CrateDef, DefId}; use std::fs::File; use std::io::BufWriter; @@ -89,108 +87,10 @@ pub fn check_reachable_items(tcx: TyCtxt, queries: &QueryDb, items: &[MonoItem]) .check_unstable_features(&queries.args().unstable_features); def_ids.insert(def_id); } - - // We don't short circuit here since this is a type check and can shake - // out differently depending on generic parameters. - if let MonoItem::Fn(instance) = item { - if attributes::is_function_contract_generated( - tcx, - rustc_internal::internal(tcx, def_id), - ) { - check_is_contract_safe(tcx, *instance); - } - } } tcx.dcx().abort_if_errors(); } -/// A basic check that ensures a function with a contract does not receive -/// mutable pointers in its input and does not return raw pointers of any kind. -/// -/// This is a temporary safety measure because contracts cannot yet reason -/// about the heap. -fn check_is_contract_safe(tcx: TyCtxt, instance: Instance) { - struct NoMutPtr<'tcx> { - tcx: TyCtxt<'tcx>, - is_prohibited: fn(Ty) -> bool, - /// Where (top level) did the type we're analyzing come from. Used for - /// composing error messages. - r#where: &'static str, - /// Adjective to describe the kind of pointer we're prohibiting. - /// Essentially `is_prohibited` but in English. - what: &'static str, - } - - impl<'tcx> TypeVisitor for NoMutPtr<'tcx> { - type Break = (); - fn visit_ty(&mut self, ty: &Ty) -> std::ops::ControlFlow { - if (self.is_prohibited)(*ty) { - // TODO make this more user friendly - self.tcx.dcx().err(format!( - "{} contains a {}pointer ({}). This is prohibited for functions with contracts, \ - as they cannot yet reason about the pointer behavior.", self.r#where, self.what, - pretty_ty(ty.kind()))); - } - - // Rust's type visitor only recurses into type arguments, (e.g. - // `generics` in this match). This is enough for many types, but it - // won't look at the field types of structs or enums. So we override - // it here and do that ourselves. - // - // Since the field types also must contain in some form all the type - // arguments the visitor will see them as it inspects the fields and - // we don't need to call back to `super`. - if let TyKind::RigidTy(RigidTy::Adt(adt_def, generics)) = ty.kind() { - for variant in adt_def.variants() { - for field in &variant.fields() { - self.visit_ty(&field.ty_with_args(&generics))?; - } - } - std::ops::ControlFlow::Continue(()) - } else { - // For every other type. - ty.super_visit(self) - } - } - } - - fn is_raw_mutable_ptr(ty: Ty) -> bool { - let kind = ty.kind(); - kind.is_raw_ptr() && kind.is_mutable_ptr() - } - - fn is_raw_ptr(ty: Ty) -> bool { - let kind = ty.kind(); - kind.is_raw_ptr() - } - - // TODO: Replace this with fn_abi. - // https://github.com/model-checking/kani/issues/1365 - let bound_fn_sig = instance.ty().kind().fn_sig().unwrap(); - - for var in &bound_fn_sig.bound_vars { - if let BoundVariableKind::Ty(t) = var { - tcx.dcx().span_err( - rustc_internal::internal(tcx, instance.def.span()), - format!("Found a bound type variable {t:?} after monomorphization"), - ); - } - } - - let fn_typ = bound_fn_sig.skip_binder(); - - for (input_ty, (is_prohibited, r#where, what)) in fn_typ - .inputs() - .iter() - .copied() - .zip(std::iter::repeat((is_raw_mutable_ptr as fn(_) -> _, "This argument", "mutable "))) - .chain([(fn_typ.output(), (is_raw_ptr as fn(_) -> _, "The return", ""))]) - { - let mut v = NoMutPtr { tcx, is_prohibited, r#where, what }; - v.visit_ty(&input_ty); - } -} - /// Print MIR for the reachable items if the `--emit mir` option was provided to rustc. pub fn dump_mir_items(tcx: TyCtxt, items: &[MonoItem], output: &Path) { /// Convert MonoItem into a DefId. diff --git a/kani-compiler/src/kani_middle/provide.rs b/kani-compiler/src/kani_middle/provide.rs index d29635ecfd15..d9197493f4db 100644 --- a/kani-compiler/src/kani_middle/provide.rs +++ b/kani-compiler/src/kani_middle/provide.rs @@ -15,6 +15,8 @@ use rustc_middle::util::Providers; use rustc_middle::{mir::Body, query::queries, ty::TyCtxt}; use stable_mir::mir::mono::MonoItem; +use crate::kani_middle::KaniAttributes; + /// Sets up rustc's query mechanism to apply Kani's custom queries to code from /// a crate. pub fn provide(providers: &mut Providers, queries: &QueryDb) { @@ -61,6 +63,17 @@ fn run_kani_mir_passes<'tcx>( tracing::debug!(?def_id, "Run Kani transformation passes"); let mut transformed_body = stubbing::transform(tcx, def_id, body); stubbing::transform_foreign_functions(tcx, &mut transformed_body); + let item_attributes = KaniAttributes::for_item(tcx, def_id); + // If we apply `transform_any_modifies` in all contract-generated items, + // we will ended up instantiating `kani::any_modifies` for the replace function + // every time, even if we are only checking the contract, because the function + // is always included during contract instrumentation. Thus, we must only apply + // the transformation if we are using a verified stub or in the presence of recursion. + if item_attributes.is_contract_generated() + && (stubbing::get_stub_key(tcx, def_id).is_some() || item_attributes.has_recursion()) + { + stubbing::transform_any_modifies(tcx, &mut transformed_body); + } // This should be applied after stubbing so user stubs take precedence. ModelIntrinsics::run_pass(tcx, &mut transformed_body); tcx.arena.alloc(transformed_body) diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 52b55d6b947c..eb40f7ec673d 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -34,6 +34,7 @@ use stable_mir::ty::{Allocation, ClosureKind, ConstantKind, RigidTy, Ty, TyKind} use stable_mir::CrateItem; use stable_mir::{CrateDef, ItemKind}; +use crate::kani_middle::attributes::matches_diagnostic as matches_function; use crate::kani_middle::coercion; use crate::kani_middle::coercion::CoercionBase; use crate::kani_middle::stubbing::{get_stub, validate_instance}; @@ -440,6 +441,24 @@ impl<'a, 'tcx> MirVisitor for MonoItemsFnCollector<'a, 'tcx> { callee, ), ); + } else if matches_function(self.tcx, self.instance.def, "KaniAny") { + 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, terminator.span), + format!( + "`{}` doesn't implement \ + `{}`. Callee: `{}`\nPlease, check whether the type of all \ + objects in the modifies clause (including return types) \ + implement `{}`.\nThis is a strict condition to use \ + function contracts as verified stubs.", + pretty_ty(receiver_ty.kind()), + trait_, + callee, + trait_, + ), + ); } else { panic!("unable to resolve call to `{callee}` in `{caller}`") } diff --git a/kani-compiler/src/kani_middle/stubbing/transform.rs b/kani-compiler/src/kani_middle/stubbing/transform.rs index ff845b188dca..16c46990bc6f 100644 --- a/kani-compiler/src/kani_middle/stubbing/transform.rs +++ b/kani-compiler/src/kani_middle/stubbing/transform.rs @@ -23,8 +23,13 @@ use tracing::debug; /// Returns the `DefId` of the stub for the function/method identified by the /// parameter `def_id`, and `None` if the function/method is not stubbed. pub fn get_stub(tcx: TyCtxt, def_id: DefId) -> Option { - let mapping = get_stub_mapping(tcx)?; - mapping.get(&def_id).copied() + let stub_map = get_stub_mapping(tcx)?; + stub_map.get(&def_id).copied() +} + +pub fn get_stub_key(tcx: TyCtxt, def_id: DefId) -> Option { + let stub_map = get_stub_mapping(tcx)?; + stub_map.iter().find_map(|(&key, &val)| if val == def_id { Some(key) } else { None }) } /// Returns the new body of a function/method if it has been stubbed out; @@ -56,6 +61,48 @@ pub fn transform_foreign_functions<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx } } +/// Traverse `body` searching for calls to `kani::any_modifies` and replace these calls +/// with calls to `kani::any`. This happens as a separate step as it is only necessary +/// for contract-generated functions. +pub fn transform_any_modifies<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let mut visitor = AnyModifiesTransformer { tcx, local_decls: body.clone().local_decls }; + visitor.visit_body(body); +} + +struct AnyModifiesTransformer<'tcx> { + /// The compiler context. + tcx: TyCtxt<'tcx>, + /// Local declarations of the callee function. Kani searches here for foreign functions. + local_decls: IndexVec>, +} + +impl<'tcx> MutVisitor<'tcx> for AnyModifiesTransformer<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_operand(&mut self, operand: &mut Operand<'tcx>, _location: Location) { + let func_ty = operand.ty(&self.local_decls, self.tcx); + if let ty::FnDef(reachable_function, arguments) = *func_ty.kind() { + if let Some(any_modifies) = self.tcx.get_diagnostic_name(reachable_function) + && any_modifies.as_str() == "KaniAnyModifies" + { + let Operand::Constant(function_definition) = operand else { + return; + }; + let kani_any_symbol = self + .tcx + .get_diagnostic_item(rustc_span::symbol::Symbol::intern("KaniAny")) + .expect("We should have a `kani::any()` definition at this point."); + function_definition.const_ = Const::from_value( + ConstValue::ZeroSized, + self.tcx.type_of(kani_any_symbol).instantiate(self.tcx, arguments), + ); + } + } + } +} + struct ForeignFunctionTransformer<'tcx> { /// The compiler context. tcx: TyCtxt<'tcx>, diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index e3d27c4b1bfb..25b1b389a2b1 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -156,11 +156,27 @@ pub const fn cover(_cond: bool, _msg: &'static str) {} /// Note: This is a safe construct and can only be used with types that implement the `Arbitrary` /// trait. The Arbitrary trait is used to build a symbolic value that represents all possible /// valid values for type `T`. +#[rustc_diagnostic_item = "KaniAny"] #[inline(always)] pub fn any() -> T { T::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 +/// contracts only for verification. +#[rustc_diagnostic_item = "KaniAnyModifies"] +#[inline(never)] +#[doc(hidden)] +pub fn any_modifies() -> T { + // This function should not be reacheable. + // Users must include `#[kani::recursion]` in any function contracts for recursive functions; + // otherwise, this might not be properly instantiate. We mark this as unreachable to make + // sure Kani doesn't report any false positives. + unreachable!() +} + /// This creates a symbolic *valid* value of type `T`. /// The value is constrained to be a value accepted by the predicate passed to the filter. /// You can assign the return value of this function to a variable that you want to make symbolic. diff --git a/library/kani_macros/Cargo.toml b/library/kani_macros/Cargo.toml index b2254b5c8954..84ed56a9ef12 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -16,3 +16,7 @@ proc-macro2 = "1.0" proc-macro-error = "1.0.4" quote = "1.0.20" syn = { version = "2.0.18", features = ["full", "visit-mut", "visit"] } + +[package.metadata.rust-analyzer] +# This package uses rustc crates. +rustc_private=true diff --git a/library/kani_macros/src/lib.rs b/library/kani_macros/src/lib.rs index 32bd44d2ea38..0991730f8802 100644 --- a/library/kani_macros/src/lib.rs +++ b/library/kani_macros/src/lib.rs @@ -52,6 +52,16 @@ pub fn should_panic(attr: TokenStream, item: TokenStream) -> TokenStream { attr_impl::should_panic(attr, item) } +/// Specifies that a function contains recursion for contract instrumentation.** +/// +/// This attribute is only used for function-contract instrumentation. Kani uses +/// this annotation to identify recursive functions and properly instantiate +/// `kani::any_modifies` to check such functions using induction. +#[proc_macro_attribute] +pub fn recursion(attr: TokenStream, item: TokenStream) -> TokenStream { + attr_impl::recursion(attr, item) +} + /// Set Loop unwind limit for proof harnesses /// The attribute `#[kani::unwind(arg)]` can only be called alongside `#[kani::proof]`. /// arg - Takes in a integer value (u32) that represents the unwind value for the harness. @@ -331,6 +341,7 @@ mod sysroot { } kani_attribute!(should_panic, no_args); + kani_attribute!(recursion, no_args); kani_attribute!(solver); kani_attribute!(stub); kani_attribute!(unstable); @@ -363,6 +374,7 @@ mod regular { } no_op!(should_panic); + no_op!(recursion); no_op!(solver); no_op!(stub); no_op!(unstable); diff --git a/library/kani_macros/src/sysroot/contracts/bootstrap.rs b/library/kani_macros/src/sysroot/contracts/bootstrap.rs index 98f52c19d9e7..8d930fdebda9 100644 --- a/library/kani_macros/src/sysroot/contracts/bootstrap.rs +++ b/library/kani_macros/src/sysroot/contracts/bootstrap.rs @@ -8,11 +8,7 @@ use proc_macro2::Span; use quote::quote; use syn::ItemFn; -use super::{ - helpers::*, - shared::{attach_require_kani_any, identifier_for_generated_function}, - ContractConditionsHandler, -}; +use super::{helpers::*, shared::identifier_for_generated_function, ContractConditionsHandler}; impl<'a> ContractConditionsHandler<'a> { /// The complex case. We are the first time a contract is handled on this function, so @@ -74,7 +70,6 @@ impl<'a> ContractConditionsHandler<'a> { )); let mut wrapper_sig = sig.clone(); - attach_require_kani_any(&mut wrapper_sig); wrapper_sig.ident = recursion_wrapper_name; let args = pats_to_idents(&mut wrapper_sig.inputs).collect::>(); diff --git a/library/kani_macros/src/sysroot/contracts/replace.rs b/library/kani_macros/src/sysroot/contracts/replace.rs index 0b3e1bae5b26..28e401c22d48 100644 --- a/library/kani_macros/src/sysroot/contracts/replace.rs +++ b/library/kani_macros/src/sysroot/contracts/replace.rs @@ -8,7 +8,7 @@ use quote::quote; use super::{ helpers::*, - shared::{attach_require_kani_any, make_unsafe_argument_copies, try_as_result_assign}, + shared::{make_unsafe_argument_copies, try_as_result_assign}, ContractConditionsData, ContractConditionsHandler, }; @@ -39,7 +39,7 @@ impl<'a> ContractConditionsHandler<'a> { fn ensure_bootstrapped_replace_body(&self) -> (Vec, Vec) { if self.is_first_emit() { let return_type = return_type_to_type(&self.annotated_fn.sig.output); - (vec![syn::parse_quote!(let result : #return_type = kani::any();)], vec![]) + (vec![syn::parse_quote!(let result : #return_type = kani::any_modifies();)], vec![]) } else { let stmts = &self.annotated_fn.block.stmts; let idx = stmts @@ -91,7 +91,7 @@ impl<'a> ContractConditionsHandler<'a> { ContractConditionsData::Modifies { attr } => { quote!( #(#before)* - #(*unsafe { kani::internal::Pointer::assignable(#attr) } = kani::any();)* + #(*unsafe { kani::internal::Pointer::assignable(#attr) } = kani::any_modifies();)* #(#after)* result ) @@ -112,9 +112,6 @@ impl<'a> ContractConditionsHandler<'a> { self.output.extend(quote!(#[kanitool::is_contract_generated(replace)])); } let mut sig = self.annotated_fn.sig.clone(); - if self.is_first_emit() { - attach_require_kani_any(&mut sig); - } let body = self.make_replace_body(); if let Some(ident) = override_function_ident { sig.ident = ident; @@ -129,7 +126,7 @@ impl<'a> ContractConditionsHandler<'a> { } } -/// Is this statement `let result : <...> = kani::any();`. +/// Is this statement `let result : <...> = kani::any_modifies();`. fn is_replace_return_havoc(stmt: &syn::Stmt) -> bool { let Some(syn::LocalInit { diverge: None, expr: e, .. }) = try_as_result_assign(stmt) else { return false; @@ -152,7 +149,7 @@ fn is_replace_return_havoc(stmt: &syn::Stmt) -> bool { }) if path.segments.len() == 2 && path.segments[0].ident == "kani" - && path.segments[1].ident == "any" + && path.segments[1].ident == "any_modifies" && attrs.is_empty() ) ) diff --git a/library/kani_macros/src/sysroot/contracts/shared.rs b/library/kani_macros/src/sysroot/contracts/shared.rs index 4c52cd35a69f..bf5a8000da50 100644 --- a/library/kani_macros/src/sysroot/contracts/shared.rs +++ b/library/kani_macros/src/sysroot/contracts/shared.rs @@ -11,11 +11,9 @@ use std::collections::HashMap; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; -use syn::{ - Attribute, PredicateType, ReturnType, Signature, TraitBound, TypeParamBound, WhereClause, -}; +use syn::Attribute; -use super::{helpers::return_type_to_type, ContractConditionsHandler, ContractFunctionState}; +use super::{ContractConditionsHandler, ContractFunctionState}; impl ContractFunctionState { /// Do we need to emit the `is_contract_generated` tag attribute on the @@ -88,57 +86,6 @@ pub fn make_unsafe_argument_copies( ) } -/// Looks complicated but does something very simple: attach a bound for -/// `kani::Arbitrary` on the return type to the provided signature. Pushes it -/// onto a preexisting where condition, initializing a new `where` condition if -/// it doesn't already exist. -/// -/// Very simple example: `fn foo() -> usize { .. }` would be rewritten `fn foo() -/// -> usize where usize: kani::Arbitrary { .. }`. -/// -/// This is called when we first emit a replace function. Later we can rely on -/// this bound already being present. -pub fn attach_require_kani_any(sig: &mut Signature) { - if matches!(sig.output, ReturnType::Default) { - // It's the default return type, e.g. `()` so we can skip adding the - // constraint. - return; - } - let return_ty = return_type_to_type(&sig.output); - let where_clause = sig.generics.where_clause.get_or_insert_with(|| WhereClause { - where_token: syn::Token![where](Span::call_site()), - predicates: Default::default(), - }); - - where_clause.predicates.push(syn::WherePredicate::Type(PredicateType { - lifetimes: None, - bounded_ty: return_ty.into_owned(), - colon_token: syn::Token![:](Span::call_site()), - bounds: [TypeParamBound::Trait(TraitBound { - paren_token: None, - modifier: syn::TraitBoundModifier::None, - lifetimes: None, - path: syn::Path { - leading_colon: None, - segments: [ - syn::PathSegment { - ident: Ident::new("kani", Span::call_site()), - arguments: syn::PathArguments::None, - }, - syn::PathSegment { - ident: Ident::new("Arbitrary", Span::call_site()), - arguments: syn::PathArguments::None, - }, - ] - .into_iter() - .collect(), - }, - })] - .into_iter() - .collect(), - })) -} - /// Used as the "single source of truth" for [`try_as_result_assign`] and [`try_as_result_assign_mut`] /// since we can't abstract over mutability. Input is the object to match on and the name of the /// function used to convert an `Option` into the result type (e.g. `as_ref` and `as_mut` diff --git a/tests/expected/function-contract/fail_on_two.rs b/tests/expected/function-contract/fail_on_two.rs index 4302cdc7da33..605065449194 100644 --- a/tests/expected/function-contract/fail_on_two.rs +++ b/tests/expected/function-contract/fail_on_two.rs @@ -17,6 +17,7 @@ //! contract replacement) is used we'd expect the verification to succeed. #[kani::ensures(result < 3)] +#[kani::recursion] fn fail_on_two(i: i32) -> i32 { match i { 0 => fail_on_two(i + 1), @@ -32,6 +33,7 @@ fn harness() { } #[kani::ensures(result < 3)] +#[kani::recursion] fn fail_on_two_in_postcondition(i: i32) -> i32 { let j = i + 1; if i < 2 { fail_on_two_in_postcondition(j) } else { j } diff --git a/tests/expected/function-contract/gcd_rec_code_fail.rs b/tests/expected/function-contract/gcd_rec_code_fail.rs index 5f63cb247ebf..da38318e2a66 100644 --- a/tests/expected/function-contract/gcd_rec_code_fail.rs +++ b/tests/expected/function-contract/gcd_rec_code_fail.rs @@ -6,6 +6,7 @@ type T = u8; /// Euclid's algorithm for calculating the GCD of two numbers #[kani::requires(x != 0 && y != 0)] #[kani::ensures(result != 0 && x % result == 0 && y % result == 0)] +#[kani::recursion] fn gcd(x: T, y: T) -> T { let mut max = x; let mut min = y; diff --git a/tests/expected/function-contract/gcd_rec_comparison_pass.rs b/tests/expected/function-contract/gcd_rec_comparison_pass.rs index b08d94504bf7..1db9691ae8e4 100644 --- a/tests/expected/function-contract/gcd_rec_comparison_pass.rs +++ b/tests/expected/function-contract/gcd_rec_comparison_pass.rs @@ -6,6 +6,7 @@ type T = u8; /// Euclid's algorithm for calculating the GCD of two numbers #[kani::requires(x != 0 && y != 0)] #[kani::ensures(result != 0 && x % result == 0 && y % result == 0)] +#[kani::recursion] fn gcd(x: T, y: T) -> T { let mut max = x; let mut min = y; diff --git a/tests/expected/function-contract/gcd_rec_simple_pass.rs b/tests/expected/function-contract/gcd_rec_simple_pass.rs index ae55efa62b45..464e3d8bbea1 100644 --- a/tests/expected/function-contract/gcd_rec_simple_pass.rs +++ b/tests/expected/function-contract/gcd_rec_simple_pass.rs @@ -6,6 +6,7 @@ type T = u8; /// Euclid's algorithm for calculating the GCD of two numbers #[kani::requires(x != 0 && y != 0)] #[kani::ensures(result != 0 && x % result == 0 && y % result == 0)] +#[kani::recursion] fn gcd(x: T, y: T) -> T { let mut max = x; let mut min = y; diff --git a/tests/expected/function-contract/generic_infinity_recursion.rs b/tests/expected/function-contract/generic_infinity_recursion.rs index d40b7694dbdb..7512b4e1e461 100644 --- a/tests/expected/function-contract/generic_infinity_recursion.rs +++ b/tests/expected/function-contract/generic_infinity_recursion.rs @@ -5,6 +5,7 @@ //! Check Kani handling of generics and recursion with function contracts. #[kani::requires(x != 0)] +#[kani::recursion] fn foo>(x: T) { assert_ne!(x, 0); foo(x); diff --git a/tests/expected/function-contract/modifies/check_invalid_modifies.expected b/tests/expected/function-contract/modifies/check_invalid_modifies.expected new file mode 100644 index 000000000000..fa1d96f1fe3f --- /dev/null +++ b/tests/expected/function-contract/modifies/check_invalid_modifies.expected @@ -0,0 +1,3 @@ +error: `&str` doesn't implement `kani::Arbitrary`. Callee: `kani::Arbitrary::any` \ + Please, check whether the type of all objects in the modifies clause (including return types) implement `kani::Arbitrary`. \ + This is a strict condition to use function contracts as verified stubs. diff --git a/tests/expected/function-contract/modifies/check_invalid_modifies.rs b/tests/expected/function-contract/modifies/check_invalid_modifies.rs new file mode 100644 index 000000000000..5cd832d0f456 --- /dev/null +++ b/tests/expected/function-contract/modifies/check_invalid_modifies.rs @@ -0,0 +1,27 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +//! Check that Kani reports the correct error message when modifies clause +//! includes objects of types that do not implement `kani::Arbitrary`. +//! This restriction is only applied when using contracts as verified stubs. + +#[kani::requires(*ptr < 100)] +#[kani::modifies(ptr)] +fn modify(ptr: &mut u32) -> &'static str { + *ptr += 1; + let msg: &'static str = "done"; + msg +} + +fn use_modify(ptr: &mut u32) { + *ptr = 99; + assert!(modify(ptr) == "done"); +} + +#[kani::proof] +#[kani::stub_verified(modify)] +fn harness() { + let mut i = kani::any_where(|x| *x < 100); + use_modify(&mut i); +} diff --git a/tests/expected/function-contract/modifies/check_only_verification.expected b/tests/expected/function-contract/modifies/check_only_verification.expected new file mode 100644 index 000000000000..34c886c358cb --- /dev/null +++ b/tests/expected/function-contract/modifies/check_only_verification.expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/modifies/check_only_verification.rs b/tests/expected/function-contract/modifies/check_only_verification.rs new file mode 100644 index 000000000000..2f247a718ae9 --- /dev/null +++ b/tests/expected/function-contract/modifies/check_only_verification.rs @@ -0,0 +1,34 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +//! Check that Kani does not report any error when unused modifies clauses +//! includes objects of types that do not implement `kani::Arbitrary`. + +#[kani::requires(*ptr < 100)] +#[kani::modifies(ptr)] +#[kani::ensures(result == 100)] +fn modify(ptr: &mut u32) -> u32 { + *ptr += 1; + *ptr +} + +#[kani::requires(*ptr < 100)] +#[kani::modifies(ptr)] +fn wrong_modify(ptr: &mut u32) -> &'static str { + *ptr += 1; + let msg: &'static str = "done"; + msg +} + +fn use_modify(ptr: &mut u32) { + *ptr = 99; + assert!(modify(ptr) == 100); +} + +#[kani::proof] +#[kani::stub_verified(modify)] +fn harness() { + let mut i = kani::any_where(|x| *x < 100); + use_modify(&mut i); +} diff --git a/tests/expected/function-contract/modifies/fail_missing_recursion_attr.expected b/tests/expected/function-contract/modifies/fail_missing_recursion_attr.expected new file mode 100644 index 000000000000..6591cf27a8db --- /dev/null +++ b/tests/expected/function-contract/modifies/fail_missing_recursion_attr.expected @@ -0,0 +1 @@ +VERIFICATION:- FAILED diff --git a/tests/expected/function-contract/modifies/fail_missing_recursion_attr.rs b/tests/expected/function-contract/modifies/fail_missing_recursion_attr.rs new file mode 100644 index 000000000000..644b641e731e --- /dev/null +++ b/tests/expected/function-contract/modifies/fail_missing_recursion_attr.rs @@ -0,0 +1,21 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +//! Check whether Kani fails if users forgot to annotate recursive +//! functions with `#[kani::recursion]` when using function contracts. + +#[kani::ensures(result < 3)] +fn fail_on_two(i: i32) -> i32 { + match i { + 0 => fail_on_two(i + 1), + 1 => 2, + _ => unreachable!("fail on two"), + } +} + +#[kani::proof_for_contract(fail_on_two)] +fn harness() { + let first = fail_on_two(0); + let _ = fail_on_two(first); +} diff --git a/tests/expected/function-contract/modifies/mistake_condition_return.expected b/tests/expected/function-contract/modifies/mistake_condition_return.expected new file mode 100644 index 000000000000..ad1cbf4f72d2 --- /dev/null +++ b/tests/expected/function-contract/modifies/mistake_condition_return.expected @@ -0,0 +1,3 @@ +Failed Checks: assertion failed: res == 100 + +VERIFICATION:- FAILED diff --git a/tests/expected/function-contract/modifies/mistake_condition_return.rs b/tests/expected/function-contract/modifies/mistake_condition_return.rs new file mode 100644 index 000000000000..484819af4248 --- /dev/null +++ b/tests/expected/function-contract/modifies/mistake_condition_return.rs @@ -0,0 +1,39 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +//! Provide an example where users might get confuse on how to constrain +//! the return value of functions when writing function contracts. +//! In this case, users must remember that when using contracts as +//! verified stubs, the return value will be havoced. To retrict the return +//! value of a function, users may use the `result` keyword in their +//! ensures clauses. + +#[kani::requires(*ptr < 100)] +#[kani::modifies(ptr)] +// In this case, one may think that by assuming `*ptr == 100`, automatically +// we can assume the return value of this function will also be equal to 100. +// However, contract instrumentation will create a separate non-deterministic +// value to return in this function that can only be constrained by using the +// `result` keyword. Thus the correct condition would be +// `#[kani::ensures(result == 100)]`. +#[kani::ensures(*ptr == 100)] +fn modify(ptr: &mut u32) -> u32 { + *ptr += 1; + *ptr +} + +fn use_modify(ptr: &mut u32) { + *ptr = 99; + let res = modify(ptr); + // This assertion won't hold because the return + // value of `modify` is unconstrained. + assert!(res == 100); +} + +#[kani::proof] +#[kani::stub_verified(modify)] +fn harness() { + let mut i = kani::any(); + use_modify(&mut i); +} diff --git a/tests/expected/function-contract/modifies/simple_only_verification.expected b/tests/expected/function-contract/modifies/simple_only_verification.expected new file mode 100644 index 000000000000..34c886c358cb --- /dev/null +++ b/tests/expected/function-contract/modifies/simple_only_verification.expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/modifies/simple_only_verification.rs b/tests/expected/function-contract/modifies/simple_only_verification.rs new file mode 100644 index 000000000000..34d625485817 --- /dev/null +++ b/tests/expected/function-contract/modifies/simple_only_verification.rs @@ -0,0 +1,22 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +//! Check that is possible to use `modifies` clause for verifciation, but not stubbing. +//! Using contracts as verified stubs require users to ensure the type of any object in +//! the modifies clause (including return types) to implement `kani::Arbitrary`. +//! This requirement is not necessary if the contract is only used for verification. + +#[kani::requires(*ptr < 100)] +#[kani::modifies(ptr)] +fn modify(ptr: &mut u32) -> &'static str { + *ptr += 1; + let msg: &'static str = "done"; + msg +} + +#[kani::proof_for_contract(modify)] +fn harness() { + let mut i = kani::any_where(|x| *x < 100); + modify(&mut i); +} diff --git a/tests/expected/function-contract/prohibit-pointers/hidden.expected b/tests/expected/function-contract/prohibit-pointers/hidden.expected index 59e40d5aadc8..34c886c358cb 100644 --- a/tests/expected/function-contract/prohibit-pointers/hidden.expected +++ b/tests/expected/function-contract/prohibit-pointers/hidden.expected @@ -1 +1 @@ -error: This argument contains a mutable pointer (*mut u32). This is prohibited for functions with contracts, as they cannot yet reason about the pointer behavior. +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/prohibit-pointers/plain_pointer.expected b/tests/expected/function-contract/prohibit-pointers/plain_pointer.expected index 916854aa3131..34c886c358cb 100644 --- a/tests/expected/function-contract/prohibit-pointers/plain_pointer.expected +++ b/tests/expected/function-contract/prohibit-pointers/plain_pointer.expected @@ -1 +1 @@ -error: This argument contains a mutable pointer (*mut i32). This is prohibited for functions with contracts, as they cannot yet reason about the pointer behavior. +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/prohibit-pointers/return_pointer.expected b/tests/expected/function-contract/prohibit-pointers/return_pointer.expected index 8f3689888d92..34c886c358cb 100644 --- a/tests/expected/function-contract/prohibit-pointers/return_pointer.expected +++ b/tests/expected/function-contract/prohibit-pointers/return_pointer.expected @@ -1 +1 @@ -error: The return contains a pointer (*const usize). This is prohibited for functions with contracts, as they cannot yet reason about the pointer behavior. +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/prohibit-pointers/return_pointer.rs b/tests/expected/function-contract/prohibit-pointers/return_pointer.rs index fc279667ad13..2bacdca9bbb6 100644 --- a/tests/expected/function-contract/prohibit-pointers/return_pointer.rs +++ b/tests/expected/function-contract/prohibit-pointers/return_pointer.rs @@ -2,24 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zfunction-contracts -#![allow(unreachable_code, unused_variables)] - -/// This only exists so I can fake a [`kani::Arbitrary`] instance for `*const -/// usize`. -struct ArbitraryPointer

(P); - -impl kani::Arbitrary for ArbitraryPointer<*const usize> { - fn any() -> Self { - unreachable!() - } -} - -#[kani::ensures(true)] -fn return_pointer() -> ArbitraryPointer<*const usize> { - unreachable!() +#[kani::ensures(unsafe{ *result == *input })] +fn return_pointer(input: *const usize) -> *const usize { + input } #[kani::proof_for_contract(return_pointer)] fn return_ptr_harness() { - return_pointer(); + let input: usize = 10; + let input_ptr = &input as *const usize; + unsafe { + assert!(*(return_pointer(input_ptr)) == input); + } } From 2d002fd58dd35fa36f4b8caa9c2abd7b26e4812c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 4 Apr 2024 12:08:10 +0200 Subject: [PATCH 054/225] Automatic cargo update to 2024-04-01 (#3117) Dependency upgrade resulting from `cargo update`. --- Cargo.lock | 56 +++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa3c4e089392..e87e205714b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,9 +89,9 @@ checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "bitflags" @@ -169,9 +169,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.3" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -191,14 +191,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.3" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -428,9 +428,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "kani" @@ -504,7 +504,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -573,9 +573,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memuse" @@ -721,9 +721,9 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "ppv-lite86" @@ -858,7 +858,7 @@ dependencies = [ "aho-corasick", "memchr", "regex-automata 0.4.6", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -878,7 +878,7 @@ checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -889,9 +889,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustc-demangle" @@ -972,14 +972,14 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ "itoa", "ryu", @@ -1084,7 +1084,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -1097,7 +1097,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -1112,9 +1112,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.55" +version = "2.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" +checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" dependencies = [ "proc-macro2", "quote", @@ -1150,7 +1150,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -1216,7 +1216,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -1540,5 +1540,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] From dc0978043c52492112e4ad37a617fd3c8100ef1f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 4 Apr 2024 16:31:27 +0000 Subject: [PATCH 055/225] Automatic cargo update to 2024-04-04 (#3122) Dependency upgrade resulting from `cargo update`. --- Cargo.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e87e205714b2..b6bc0a370675 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -198,7 +198,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -504,7 +504,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -766,9 +766,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce76ce678ffc8e5675b22aa1405de0b7037e2fdf8913fea40d1926c6fe1e6e7" +checksum = "5f0530d13d87d1f549b66a3e8d0c688952abe5994e204ed62615baaf25dc029c" dependencies = [ "bitflags 2.5.0", "memchr", @@ -972,7 +972,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1058,9 +1058,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" @@ -1084,7 +1084,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1097,7 +1097,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1112,9 +1112,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.57" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -1150,7 +1150,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1216,7 +1216,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1540,5 +1540,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] From 36bf7c82225452f18c6e812a80ff3653f839b5f3 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Fri, 5 Apr 2024 12:11:00 +0200 Subject: [PATCH 056/225] Remove bookrunner (#3123) We never updated the books and do not actually require the evaluation data anymore. --- .github/workflows/kani.yml | 27 +- .gitignore | 2 - .gitmodules | 9 - Cargo.lock | 57 -- Cargo.toml | 3 - docs/src/SUMMARY.md | 1 - docs/src/bookrunner.md | 81 --- docs/src/cheat-sheets.md | 8 +- docs/src/testing.md | 3 +- rustfmt.toml | 1 - scripts/build-docs.sh | 21 - scripts/ci/bookrunner_failures_by_stage.py | 36 -- scripts/ci/detect_bookrunner_failures.sh | 38 -- scripts/ci/update_bookrunner_report.py | 65 -- scripts/setup/install_bookrunner_deps.sh | 21 - tools/bookrunner/Cargo.toml | 23 - tools/bookrunner/README.md | 9 - .../Enums/Testcase: linked-list/5.props | 1 - .../Iterating over Results/22.props | 1 - .../Iterating over Results/39.props | 1 - .../Iterating over Results/5.props | 1 - .../Iterating over Results/54.props | 1 - .../Iterating over Results/69.props | 1 - .../Flow of Control/for and range/102.props | 1 - .../Flow of Control/for and range/63.props | 1 - .../Functions/Closures/Capturing/89.props | 1 - .../Examples in std/Iterator::any/22.props | 1 - .../Searching through iterators/22.props | 1 - .../Searching through iterators/52.props | 1 - .../Formatted print/Formatting/17.props | 1 - .../Std library types/HashMap/14.props | 1 - .../Alternate_custom key types/29.props | 1 - .../Std library types/Strings/12.props | 1 - .../Rust by Example/Std misc/Channels/7.props | 1 - .../Std misc/File I_O/read lines/9.props | 1 - .../Std misc/Program arguments/8.props | 1 - .../Argument parsing/5.props | 1 - .../Rust by Example/Std misc/Threads/6.props | 1 - .../Threads/Testcase: map-reduce/24.props | 1 - .../11.props | 1 - .../Rust by Example/Traits/Iterators/12.props | 1 - .../Traits/impl Trait/115.props | 1 - .../Appendices/Glossary/263.diff | 4 - .../Appendices/Glossary/263.props | 1 - .../Attributes/Limits/45.props | 1 - .../The Rust Reference/Linkage/190.props | 1 - .../The Rust Reference/Patterns/367.props | 1 - .../Expressions/Loop expressions/133.props | 1 - .../Method call expressions/10.props | 1 - .../Type system/Types/113.props | 1 - .../Concurrency/Atomics/198.props | 1 - .../Language Features/generators/165.props | 1 - .../Language Features/generators/28.props | 1 - tools/bookrunner/librustdoc/Cargo.toml | 26 - tools/bookrunner/librustdoc/README.md | 3 - tools/bookrunner/librustdoc/build.rs | 8 - tools/bookrunner/librustdoc/doctest.rs | 322 ---------- tools/bookrunner/librustdoc/html/markdown.rs | 338 ---------- tools/bookrunner/librustdoc/html/mod.rs | 6 - tools/bookrunner/librustdoc/lib.rs | 65 -- tools/bookrunner/print.sh | 13 - tools/bookrunner/rust-doc/nomicon | 1 - tools/bookrunner/rust-doc/reference | 1 - tools/bookrunner/rust-doc/rust-by-example | 1 - .../rust-doc/unstable-book/.gitignore | 1 - .../rust-doc/unstable-book/book.toml | 10 - .../unstable-book/src/compiler-flags.md | 1 - .../src/compiler-flags/branch-protection.md | 18 - .../src/compiler-flags/codegen-backend.md | 31 - .../src/compiler-flags/control-flow-guard.md | 59 -- .../debug_info_for_profiling.md | 35 -- .../src/compiler-flags/emit-stack-sizes.md | 167 ----- .../src/compiler-flags/extern-location.md | 31 - .../compiler-flags/img/llvm-cov-show-01.png | Bin 416748 -> 0 bytes .../src/compiler-flags/instrument-coverage.md | 346 ---------- .../src/compiler-flags/location-detail.md | 43 -- .../src/compiler-flags/move-size-limit.md | 10 - .../compiler-flags/no-unique-section-names.md | 9 - .../src/compiler-flags/profile.md | 27 - .../src/compiler-flags/profile_sample_use.md | 10 - .../src/compiler-flags/remap-cwd-prefix.md | 24 - .../src/compiler-flags/report-time.md | 80 --- .../src/compiler-flags/sanitizer.md | 594 ------------------ .../src/compiler-flags/self-profile-events.md | 74 --- .../src/compiler-flags/self-profile.md | 47 -- .../source-based-code-coverage.md | 5 - .../src/compiler-flags/src-hash-algorithm.md | 11 - .../src/compiler-flags/temps-dir.md | 10 - .../src/compiler-flags/tls-model.md | 25 - .../src/compiler-flags/unsound-mir-opts.md | 8 - .../unstable-book/src/language-features.md | 1 - .../abi-c-cmse-nonsecure-call.md | 88 --- .../language-features/abi-msp430-interrupt.md | 42 -- .../src/language-features/abi-ptx.md | 60 -- .../src/language-features/abi-thiscall.md | 12 - .../language-features/allocator-internals.md | 7 - .../arbitrary-enum-discriminant.md | 37 -- .../src/language-features/asm-const.md | 11 - .../asm-experimental-arch.md | 117 ---- .../src/language-features/asm-sym.md | 13 - .../src/language-features/asm-unwind.md | 9 - .../src/language-features/auto-traits.md | 106 ---- .../src/language-features/box-patterns.md | 32 - .../src/language-features/box-syntax.md | 22 - .../src/language-features/c-unwind.md | 15 - .../src/language-features/c-variadic.md | 24 - .../src/language-features/cfg-panic.md | 38 -- .../src/language-features/cfg-sanitize.md | 34 - .../src/language-features/cfg-version.md | 35 -- .../language-features/closure-track-caller.md | 12 - .../language-features/cmse-nonsecure-entry.md | 81 --- .../language-features/compiler-builtins.md | 5 - .../src/language-features/const-eval-limit.md | 7 - .../crate-visibility-modifier.md | 20 - .../custom-test-frameworks.md | 32 - .../src/language-features/doc-cfg.md | 46 -- .../src/language-features/doc-masked.md | 24 - .../language-features/doc-notable-trait.md | 33 - .../exclusive-range-pattern.md | 26 - .../explicit-generic-args-with-impl-trait.md | 53 -- .../src/language-features/ffi-const.md | 52 -- .../src/language-features/ffi-pure.md | 56 -- .../src/language-features/generators.md | 246 -------- .../half-open-range-patterns.md | 27 - .../infer-static-outlives-requirements.md | 44 -- .../src/language-features/inline-const-pat.md | 24 - .../src/language-features/inline-const.md | 32 - .../language-features/intra-doc-pointers.md | 15 - .../src/language-features/intrinsics.md | 29 - .../src/language-features/lang-items.md | 295 --------- .../src/language-features/link-cfg.md | 5 - .../language-features/marker-trait-attr.md | 35 -- .../language-features/more-qualified-paths.md | 29 - .../native-link-modifiers-as-needed.md | 18 - .../native-link-modifiers-bundle.md | 19 - .../native-link-modifiers-verbatim.md | 20 - .../native-link-modifiers-whole-archive.md | 18 - .../native-link-modifiers.md | 11 - .../src/language-features/negative-impls.md | 57 -- .../src/language-features/no-coverage.md | 30 - .../src/language-features/no-sanitize.md | 29 - .../src/language-features/plugin.md | 116 ---- .../src/language-features/profiler-runtime.md | 5 - .../src/language-features/raw-dylib.md | 34 - .../src/language-features/repr128.md | 18 - .../src/language-features/rustc-attrs.md | 53 -- .../src/language-features/trait-alias.md | 34 - .../src/language-features/trait-upcasting.md | 27 - .../language-features/transparent-unions.md | 83 --- .../src/language-features/try-blocks.md | 30 - .../type-changing-struct-update.md | 33 - .../src/language-features/unboxed-closures.md | 25 - .../src/language-features/unsized-locals.md | 175 ------ .../unsized-tuple-coercion.md | 27 - .../unstable-book/src/library-features.md | 1 - .../src/library-features/allocator-api.md | 15 - .../src/library-features/c-variadic.md | 26 - .../src/library-features/c-void-variant.md | 5 - .../library-features/char-error-internals.md | 5 - .../src/library-features/concat-idents.md | 22 - .../src/library-features/core-intrinsics.md | 5 - .../src/library-features/core-panic.md | 5 - .../library-features/core-private-bignum.md | 5 - .../core-private-diy-float.md | 5 - .../src/library-features/dec2flt.md | 5 - .../src/library-features/default-free-fn.md | 47 -- .../src/library-features/derive-clone-copy.md | 5 - .../src/library-features/derive-eq.md | 5 - .../src/library-features/fd-read.md | 5 - .../unstable-book/src/library-features/fd.md | 5 - .../src/library-features/flt2dec.md | 5 - .../src/library-features/fmt-internals.md | 5 - .../src/library-features/fn-traits.md | 35 -- .../library-features/int-error-internals.md | 5 - .../internal-output-capture.md | 5 - .../src/library-features/is-sorted.md | 11 - .../library-features/libstd-sys-internals.md | 5 - .../libstd-thread-internals.md | 5 - .../src/library-features/print-internals.md | 5 - .../library-features/profiler-runtime-lib.md | 5 - .../unstable-book/src/library-features/rt.md | 5 - .../src/library-features/sort-internals.md | 5 - .../src/library-features/str-internals.md | 5 - .../src/library-features/test.md | 158 ----- .../thread-local-internals.md | 5 - .../src/library-features/trace-macros.md | 39 -- .../library-features/update-panic-count.md | 5 - .../src/library-features/windows-c.md | 5 - .../src/library-features/windows-handle.md | 5 - .../src/library-features/windows-net.md | 5 - .../src/library-features/windows-stdio.md | 5 - .../unstable-book/src/the-unstable-book.md | 22 - tools/bookrunner/src/bookrunner.rs | 95 --- tools/bookrunner/src/books.rs | 575 ----------------- tools/bookrunner/src/litani.rs | 249 -------- tools/bookrunner/src/main.rs | 13 - tools/bookrunner/src/util.rs | 246 -------- 197 files changed, 3 insertions(+), 7388 deletions(-) delete mode 100644 docs/src/bookrunner.md delete mode 100644 scripts/ci/bookrunner_failures_by_stage.py delete mode 100755 scripts/ci/detect_bookrunner_failures.sh delete mode 100644 scripts/ci/update_bookrunner_report.py delete mode 100755 scripts/setup/install_bookrunner_deps.sh delete mode 100644 tools/bookrunner/Cargo.toml delete mode 100644 tools/bookrunner/README.md delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Custom Types/Enums/Testcase: linked-list/5.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/22.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/39.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/5.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/54.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/69.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Flow of Control/for and range/102.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Flow of Control/for and range/63.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Capturing/89.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Examples in std/Iterator::any/22.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Examples in std/Searching through iterators/22.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Examples in std/Searching through iterators/52.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Hello World/Formatted print/Formatting/17.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Std library types/HashMap/14.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Std library types/HashMap/Alternate_custom key types/29.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Std library types/Strings/12.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Std misc/Channels/7.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Std misc/File I_O/read lines/9.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Std misc/Program arguments/8.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Std misc/Program arguments/Argument parsing/5.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Std misc/Threads/6.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Std misc/Threads/Testcase: map-reduce/24.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Traits/Disambiguating overlapping traits/11.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Traits/Iterators/12.props delete mode 100644 tools/bookrunner/configs/books/Rust by Example/Traits/impl Trait/115.props delete mode 100644 tools/bookrunner/configs/books/The Rust Reference/Appendices/Glossary/263.diff delete mode 100644 tools/bookrunner/configs/books/The Rust Reference/Appendices/Glossary/263.props delete mode 100644 tools/bookrunner/configs/books/The Rust Reference/Attributes/Limits/45.props delete mode 100644 tools/bookrunner/configs/books/The Rust Reference/Linkage/190.props delete mode 100644 tools/bookrunner/configs/books/The Rust Reference/Patterns/367.props delete mode 100644 tools/bookrunner/configs/books/The Rust Reference/Statements and expressions/Expressions/Loop expressions/133.props delete mode 100644 tools/bookrunner/configs/books/The Rust Reference/Statements and expressions/Expressions/Method call expressions/10.props delete mode 100644 tools/bookrunner/configs/books/The Rust Reference/Type system/Types/113.props delete mode 100644 tools/bookrunner/configs/books/The Rustonomicon/Concurrency/Atomics/198.props delete mode 100644 tools/bookrunner/configs/books/The Unstable Book/Language Features/generators/165.props delete mode 100644 tools/bookrunner/configs/books/The Unstable Book/Language Features/generators/28.props delete mode 100644 tools/bookrunner/librustdoc/Cargo.toml delete mode 100644 tools/bookrunner/librustdoc/README.md delete mode 100644 tools/bookrunner/librustdoc/build.rs delete mode 100644 tools/bookrunner/librustdoc/doctest.rs delete mode 100644 tools/bookrunner/librustdoc/html/markdown.rs delete mode 100644 tools/bookrunner/librustdoc/html/mod.rs delete mode 100644 tools/bookrunner/librustdoc/lib.rs delete mode 100755 tools/bookrunner/print.sh delete mode 160000 tools/bookrunner/rust-doc/nomicon delete mode 160000 tools/bookrunner/rust-doc/reference delete mode 160000 tools/bookrunner/rust-doc/rust-by-example delete mode 100644 tools/bookrunner/rust-doc/unstable-book/.gitignore delete mode 100644 tools/bookrunner/rust-doc/unstable-book/book.toml delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/branch-protection.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/codegen-backend.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/control-flow-guard.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/debug_info_for_profiling.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/emit-stack-sizes.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/extern-location.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/img/llvm-cov-show-01.png delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/instrument-coverage.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/location-detail.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/move-size-limit.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/no-unique-section-names.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/profile.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/profile_sample_use.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/report-time.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/sanitizer.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/self-profile-events.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/self-profile.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/source-based-code-coverage.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/src-hash-algorithm.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/temps-dir.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/tls-model.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/unsound-mir-opts.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-c-cmse-nonsecure-call.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-msp430-interrupt.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-ptx.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-thiscall.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/allocator-internals.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/arbitrary-enum-discriminant.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-const.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-experimental-arch.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-sym.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-unwind.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/auto-traits.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/box-patterns.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/box-syntax.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/c-unwind.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/c-variadic.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/cfg-panic.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/cfg-sanitize.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/cfg-version.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/closure-track-caller.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/cmse-nonsecure-entry.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/compiler-builtins.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/const-eval-limit.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/crate-visibility-modifier.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/custom-test-frameworks.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/doc-cfg.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/doc-masked.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/doc-notable-trait.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/exclusive-range-pattern.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/explicit-generic-args-with-impl-trait.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/ffi-const.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/ffi-pure.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/generators.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/half-open-range-patterns.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/infer-static-outlives-requirements.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/inline-const-pat.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/inline-const.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/intra-doc-pointers.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/intrinsics.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/lang-items.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/link-cfg.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/marker-trait-attr.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/more-qualified-paths.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-as-needed.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-bundle.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-verbatim.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-whole-archive.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/negative-impls.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/no-coverage.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/no-sanitize.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/plugin.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/profiler-runtime.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/raw-dylib.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/repr128.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/rustc-attrs.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/trait-alias.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/trait-upcasting.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/transparent-unions.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/try-blocks.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/type-changing-struct-update.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/unboxed-closures.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/unsized-locals.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/language-features/unsized-tuple-coercion.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/allocator-api.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/c-variadic.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/c-void-variant.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/char-error-internals.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/concat-idents.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/core-intrinsics.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/core-panic.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/core-private-bignum.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/core-private-diy-float.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/dec2flt.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/default-free-fn.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/derive-clone-copy.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/derive-eq.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/fd-read.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/fd.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/flt2dec.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/fmt-internals.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/fn-traits.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/int-error-internals.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/internal-output-capture.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/is-sorted.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/libstd-sys-internals.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/libstd-thread-internals.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/print-internals.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/profiler-runtime-lib.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/rt.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/sort-internals.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/str-internals.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/test.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/thread-local-internals.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/trace-macros.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/update-panic-count.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-c.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-handle.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-net.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-stdio.md delete mode 100644 tools/bookrunner/rust-doc/unstable-book/src/the-unstable-book.md delete mode 100644 tools/bookrunner/src/bookrunner.rs delete mode 100644 tools/bookrunner/src/books.rs delete mode 100644 tools/bookrunner/src/litani.rs delete mode 100644 tools/bookrunner/src/main.rs delete mode 100644 tools/bookrunner/src/util.rs diff --git a/.github/workflows/kani.yml b/.github/workflows/kani.yml index abdc4ee46216..12b4ac5fb6d2 100644 --- a/.github/workflows/kani.yml +++ b/.github/workflows/kani.yml @@ -96,7 +96,7 @@ jobs: env: RUST_TEST_THREADS: 1 - bookrunner: + documentation: runs-on: ubuntu-20.04 permissions: contents: write @@ -104,31 +104,6 @@ jobs: - name: Checkout Kani uses: actions/checkout@v4 - - name: Setup Kani Dependencies - uses: ./.github/actions/setup - with: - os: ubuntu-20.04 - - - name: Build Kani - run: cargo build-dev - - - name: Install book runner dependencies - run: ./scripts/setup/install_bookrunner_deps.sh - - - name: Generate book runner report - run: cargo run -p bookrunner - env: - DOC_RUST_LANG_ORG_CHANNEL: nightly - - - name: Print book runner text results - run: cat build/output/latest/html/bookrunner.txt - - - name: Print book runner failures grouped by stage - run: python3 scripts/ci/bookrunner_failures_by_stage.py build/output/latest/html/index.html - - - name: Detect unexpected book runner failures - run: ./scripts/ci/detect_bookrunner_failures.sh build/output/latest/html/bookrunner.txt - - name: Install book dependencies run: ./scripts/setup/ubuntu/install_doc_deps.sh diff --git a/.gitignore b/.gitignore index a4cfe4ff4e2b..aae8f479aac9 100644 --- a/.gitignore +++ b/.gitignore @@ -74,10 +74,8 @@ package-lock.json tests/rustdoc-gui/src/**.lock # Before adding new lines, see the comment at the top. -/.litani_cache_dir /.ninja_deps /.ninja_log -/tests/bookrunner *Cargo.lock tests/kani-dependency-test/diamond-dependency/build tests/kani-multicrate/type-mismatch/mismatch/target diff --git a/.gitmodules b/.gitmodules index 7246d4c1e60b..b02c263a898e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,3 @@ -[submodule "src/doc/nomicon"] - path = tools/bookrunner/rust-doc/nomicon - url = https://github.com/rust-lang/nomicon.git -[submodule "src/doc/reference"] - path = tools/bookrunner/rust-doc/reference - url = https://github.com/rust-lang/reference.git -[submodule "src/doc/rust-by-example"] - path = tools/bookrunner/rust-doc/rust-by-example - url = https://github.com/rust-lang/rust-by-example.git [submodule "firecracker"] path = firecracker url = https://github.com/firecracker-microvm/firecracker.git diff --git a/Cargo.lock b/Cargo.lock index b6bc0a370675..2a7cbf2d7028 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,16 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - [[package]] name = "ahash" version = "0.8.11" @@ -105,20 +95,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" -[[package]] -name = "bookrunner" -version = "0.1.0" -dependencies = [ - "Inflector", - "pulldown-cmark", - "pulldown-cmark-escape", - "rustdoc", - "serde", - "serde_json", - "toml", - "walkdir", -] - [[package]] name = "build-kani" version = "0.48.0" @@ -764,23 +740,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "pulldown-cmark" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0530d13d87d1f549b66a3e8d0c688952abe5994e204ed62615baaf25dc029c" -dependencies = [ - "bitflags 2.5.0", - "memchr", - "unicase", -] - -[[package]] -name = "pulldown-cmark-escape" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d8f9aa0e3cbcfaf8bf00300004ee3b72f74770f9cbac93f6928771f613276b" - [[package]] name = "quote" version = "1.0.35" @@ -899,13 +858,6 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -[[package]] -name = "rustdoc" -version = "0.0.0" -dependencies = [ - "pulldown-cmark", -] - [[package]] name = "rustix" version = "0.38.32" @@ -1272,15 +1224,6 @@ dependencies = [ "tracing-serde", ] -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/Cargo.toml b/Cargo.toml index 8d397f2b0bd4..810b4e2bb5eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,14 +38,11 @@ strip = "debuginfo" members = [ "library/kani", "library/std", - "tools/bookrunner", "tools/compiletest", "tools/build-kani", "kani-driver", "kani-compiler", "kani_metadata", - # `librustdoc` is still needed by bookrunner. - "tools/bookrunner/librustdoc", ] # This indicates what package to e.g. build with 'cargo build' without --workspace diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 69be21a07ff5..ff7914c1a07a 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -31,7 +31,6 @@ - [cargo kani assess](./dev-assess.md) - [Testing](./testing.md) - [Regression testing](./regression-testing.md) - - [Book runner](./bookrunner.md) - [(Experimental) Testing with a Large Number of Repositories](./repo-crawl.md) - [Performance comparisons](./performance-comparisons.md) - [`benchcomp` command line](./benchcomp-cli.md) diff --git a/docs/src/bookrunner.md b/docs/src/bookrunner.md deleted file mode 100644 index c9e647dd6552..000000000000 --- a/docs/src/bookrunner.md +++ /dev/null @@ -1,81 +0,0 @@ -# Book runner - -The [book runner](./bookrunner/index.html) is a testing tool based on [Litani](https://github.com/awslabs/aws-build-accumulator). - -The purpose of the book runner is to get data about feature coverage in Kani. -To this end, we use Rust code snippet examples from the following general Rust documentation books: - * The Rust Reference - * The Rustonomicon - * The Rust Unstable Book - * Rust By Example - -However, not all examples from these books are suited for verification. -For instance, some of them are only included to show what is valid Rust code (or what is not). - -Because of that, we run up to three different types of jobs when generating the report: - * `check` jobs: This check uses the Rust front-end to detect if the example is valid Rust code. - * `codegen` jobs: This check uses the Kani back-end to determine if we can generate GotoC code. - * `verification` jobs: This check uses CBMC to obtain a verification result. - -Note that these are incremental: A `verification` job depends on a previous `codegen` job. -Similary, a `codegen` job depends on a `check` job. - -> **NOTE**: [Litani](https://github.com/awslabs/aws-build-accumulator) does not -> support hierarchical views at the moment. For this reason, we are publishing a -> [text version of the book runner report](./bookrunner/bookrunner.txt) which -> displays the same results in a hierarchical way while we work on [improvements -> for the visualization and navigation of book runner -> results](https://github.com/model-checking/kani/issues/699). - -Before running the above mentioned jobs, we pre-process the examples to: - 1. Set the expected output according to flags present in the code snippet. - 2. Add any required compiler/Kani flags (e.g., unwinding). - -Finally, we run all jobs, collect their outputs and compare them against the expected outputs. -The results are summarized as follows: If the obtained and expected outputs differ, -the color of the stage bar will be red. Otherwise, it will be blue. -If an example shows one red bar, it's considered a failed example that cannot be handled by Kani. - -The [book runner report](./bookrunner/index.html) and [its text version](./bookrunner/bookrunner.txt) are -automatically updated whenever a PR gets merged into Kani. - -## The book running procedure - -This section describes how the book runner operates at a high level. - -To kick off the book runner process use: - -```bash -cargo run -p bookrunner -``` - -The main function of the bookrunner is `generate_run()` (code available -[here](https://github.com/model-checking/kani/blob/main/tools/bookrunner/src/books.rs)) -which follows these steps: - 1. Sets up all the books, including data about their summaries. - 2. Then, for each book: - * Calls the `parse_hierarchy()` method to parse its summary - files. - * Calls the `extract_examples()` method to extract all - examples from the book. Note that `extract_examples()` uses `rustdoc` - functions to ensure the extracted examples are runnable. - * Checks if there is a corresponding `.props` file - in `src/tools/bookrunner/configs/`. If there is, prepends the contents of these files - ([testing options](./regression-testing.md#testing-options)) to the example. - * The resulting examples are written to the `src/test/bookrunner/books/` folder. - -> In general, the path to a given example is -> `src/test/bookrunner/books///

//.rs` -> where `` is the line number where the example appears in the markdown -> file where it's written. The `.props` files mentioned above follow the same -> naming scheme in order to match them and detect conflicts. - - 3. Runs all examples using - [Litani](https://github.com/awslabs/aws-build-accumulator) with the - `litani_run_tests()` function. - 4. Parses the Litani log file with `parse_litani_output(...)`. - 5. Generates the [text version of the bookrunner](./bookrunner/bookrunner.txt) - with `generate_text_bookrunner(...)`. - -> **NOTE**: Any changes done to the examples in `src/test/bookrunner/books/` may -> be overwritten if the bookrunner is executed. diff --git a/docs/src/cheat-sheets.md b/docs/src/cheat-sheets.md index 95cc9991e46f..9b42d313ede2 100644 --- a/docs/src/cheat-sheets.md +++ b/docs/src/cheat-sheets.md @@ -19,7 +19,7 @@ cargo build-dev ### Test ```bash -# Full regression suite (does not run bookrunner) +# Full regression suite ./scripts/kani-regression.sh ``` @@ -39,12 +39,6 @@ rm -r build/x86_64-apple-darwin/tests/ cargo run -p compiletest -- --suite kani --mode kani ``` -```bash -# Run bookrunner -./scripts/setup/install_bookrunner_deps.sh -cargo run -p bookrunner -``` - ```bash # Build documentation cd docs diff --git a/docs/src/testing.md b/docs/src/testing.md index 21438e861444..85e1a46838bf 100644 --- a/docs/src/testing.md +++ b/docs/src/testing.md @@ -15,6 +15,5 @@ two very good reasons to do it: We recommend reading our section on [Regression Testing](./regression-testing.md) if you're interested in Kani -development. At present, we obtain metrics based on the [book -runner](./bookrunner.md). To run kani on a large number of remotely +development. To run kani on a large number of remotely hosted crates, please see [Repository Crawl](./repo-crawl.md). diff --git a/rustfmt.toml b/rustfmt.toml index 32ff2d638c56..95988ef7f52a 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -15,5 +15,4 @@ ignore = [ # For some reason, this is not working without the directory wildcard. "**/firecracker", "**/tests/perf/s2n-quic/", - "**/tools/bookrunner/rust-doc/", ] diff --git a/scripts/build-docs.sh b/scripts/build-docs.sh index 3400e2849b53..cd40a2edabad 100755 --- a/scripts/build-docs.sh +++ b/scripts/build-docs.sh @@ -32,33 +32,12 @@ else MDBOOK=${SCRIPT_DIR}/mdbook fi -# Publish bookrunner report into our documentation KANI_DIR=$SCRIPT_DIR/.. DOCS_DIR=$KANI_DIR/docs RFC_DIR=$KANI_DIR/rfc -HTML_DIR=$KANI_DIR/build/output/latest/html/ cd $DOCS_DIR -if [ -d $HTML_DIR ]; then - # Litani run is copied into `src` to avoid deletion by `mdbook` - cp -r $HTML_DIR src/bookrunner/ - # Replace artifacts by examples under test - BOOKS_DIR=$KANI_DIR/tests/bookrunner/books - rm -r src/bookrunner/artifacts - # Remove any json files that Kani might've left behind due to crash or timeout. - find $BOOKS_DIR -name '*.json' -exec rm {} \; - find $BOOKS_DIR -name '*.out' -exec rm {} \; - cp -r $BOOKS_DIR src/bookrunner/artifacts - # Update paths in HTML report - python $KANI_DIR/scripts/ci/update_bookrunner_report.py src/bookrunner/index.html new_index.html - mv new_index.html src/bookrunner/index.html - - # rm src/bookrunner/run.json -else - echo "WARNING: Could not find the latest bookrunner run." -fi - echo "Building user documentation..." # Generate benchcomp documentation from source code mkdir -p gen_src diff --git a/scripts/ci/bookrunner_failures_by_stage.py b/scripts/ci/bookrunner_failures_by_stage.py deleted file mode 100644 index 941cc62ae2e5..000000000000 --- a/scripts/ci/bookrunner_failures_by_stage.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/python3 -# Copyright Kani Contributors -# SPDX-License-Identifier: Apache-2.0 OR MIT - -import argparse -from bs4 import BeautifulSoup - -def main(): - parser = argparse.ArgumentParser( - description='Scans an HTML dashboard file and prints' - 'the number of failures grouped by stage') - parser.add_argument('input') - args = parser.parse_args() - - with open(args.input) as fp: - run = BeautifulSoup(fp, 'html.parser') - - failures = [0] * 3 - - for row in run.find_all('div', attrs={'class': 'pipeline-row'}): - stages = row.find_all('div', attrs={'class': 'pipeline-stage'}) - i = 0 - for stage in stages: - if stage.a['class'][1] == 'fail': - failures[i] += 1 - break - i += 1 - - print('bookrunner failures grouped by stage:') - print(' * rustc-compilation: ' + str(failures[0])) - print(' * kani-codegen: ' + str(failures[1])) - print(' * cbmc-verification: ' + str(failures[2])) - - -if __name__ == "__main__": - main() diff --git a/scripts/ci/detect_bookrunner_failures.sh b/scripts/ci/detect_bookrunner_failures.sh deleted file mode 100755 index 027ea8174b5d..000000000000 --- a/scripts/ci/detect_bookrunner_failures.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -# Copyright Kani Contributors -# SPDX-License-Identifier: Apache-2.0 OR MIT - -set -eu - -# This script checks that the number of failures in a bookrunner run are below -# the threshold computed below. -# -# The threshold is roughly computed as: `1.05 * ` -# The extra 5% allows us to account for occasional timeouts. It is reviewed and -# updated whenever the Rust toolchain version is updated. -EXPECTED=82 -THRESHOLD=$(expr ${EXPECTED} \* 105 / 100) # Add 5% threshold - -if [[ $# -ne 1 ]]; then - echo "$0: Error: Specify the bookrunner text report" - exit 1 -fi - -# Get the summary line, which looks like: -# `# of tests: ✔️ ` -SUMMARY_LINE=`head -n 1 $1` - -# Parse the summary line and extract the number of failures -read -a strarr <<< $SUMMARY_LINE -NUM_FAILURES=${strarr[-1]} - -# Print a message and return a nonzero code if the threshold is exceeded -if [[ $NUM_FAILURES -ge $THRESHOLD ]]; then - echo "Error: The number of failures from bookrunner is higher than expected!" - echo - echo "Found $NUM_FAILURES which is higher than the threshold of $THRESHOLD" - echo "This means that your changes are causing at least 5% more failures than in previous bookrunner runs." - echo "To check these failures locally, run \`cargo run -p bookrunner\` and inspect the report in \`build/output/latest/html/index.html\`." - echo "For more details on bookrunner, go to https://model-checking.github.io/kani/bookrunner.html" - exit 1 -fi diff --git a/scripts/ci/update_bookrunner_report.py b/scripts/ci/update_bookrunner_report.py deleted file mode 100644 index fc1c47ad78c4..000000000000 --- a/scripts/ci/update_bookrunner_report.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/python3 -# Copyright Kani Contributors -# SPDX-License-Identifier: Apache-2.0 OR MIT - -import argparse -from bs4 import BeautifulSoup - -def update_path(run, path): - ''' - Shortens a path referring to an example and adds a link to the file. - - By default, the path to an example follows this pattern: - - `tests/bookrunner/books///
//.rs` - - However, only the first part is shown since these paths are enclosed - in paragraph markers (`

` and `

`). So they are often rendered as: - - `tests/bookrunner/books///... - - This update removes `tests/bookrunner/books/` from the path (common to - all examples) and transforms them into anchor elements with a link to - the example, so the path to the example is shown as: - - `//
//.rs` - ''' - orig_path = path.p.string - new_string = '/'.join(orig_path.split('/')[4:]) - new_tag = run.new_tag('a') - new_tag.string = new_string - # Add link to the example - new_tag['href'] = "artifacts/" + new_string - path.p.replace_with(new_tag) - -def main(): - parser = argparse.ArgumentParser( - description='Produces an updated HTML report file from the ' - 'contents of an HTML file generated with `litani`') - parser.add_argument('input') - parser.add_argument('output') - args = parser.parse_args() - - with open(args.input) as fp: - run = BeautifulSoup(fp, 'html.parser') - - # Update pipeline names to link to the example under test - for row in run.find_all(lambda tag: tag.name == 'div' and - tag.get('class') == ['pipeline-row']): - path = row.find('div', attrs={'class': 'pipeline-name'}) - # Some paths here may be `None` - skip them - if path.p: - update_path(run, path) - - # Delete links to empty artifacts folder from progress bars - for bar in run.find_all('a', attrs={'class': 'stage-artifacts-link fail'}): - del bar['href'] - for bar in run.find_all('a', attrs={'class': 'stage-artifacts-link success'}): - del bar['href'] - - with open(args.output, "w") as file: - file.write(str(run)) - - -if __name__ == "__main__": - main() diff --git a/scripts/setup/install_bookrunner_deps.sh b/scripts/setup/install_bookrunner_deps.sh deleted file mode 100755 index f1457c1e9c4d..000000000000 --- a/scripts/setup/install_bookrunner_deps.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -# Copyright Kani Contributors -# SPDX-License-Identifier: Apache-2.0 OR MIT - -set -eu - -# The book runner report is generated using [Litani](https://github.com/awslabs/aws-build-accumulator) -FILE="litani-1.22.0.deb" -URL="https://github.com/awslabs/aws-build-accumulator/releases/download/1.22.0/$FILE" - -set -x - -# Install Litani -wget -O "$FILE" "$URL" -sudo DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends --yes ./"$FILE" - -PYTHON_DEPS=( - bs4 # Used for report updates -) - -python3 -m pip install "${PYTHON_DEPS[@]}" \ No newline at end of file diff --git a/tools/bookrunner/Cargo.toml b/tools/bookrunner/Cargo.toml deleted file mode 100644 index 74befa4ca5b7..000000000000 --- a/tools/bookrunner/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright Kani Contributors -# SPDX-License-Identifier: Apache-2.0 OR MIT - -[package] -name = "bookrunner" -version = "0.1.0" -edition = "2018" -license = "MIT OR Apache-2.0" -publish = false - -[dependencies] -Inflector = "0.11.4" -pulldown-cmark = { version = "0.10", default-features = false } -pulldown-cmark-escape = { version = "0.10", default-features = false } -rustdoc = { path = "librustdoc" } -walkdir = "2.3.2" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -toml = "0.8" - -[package.metadata.rust-analyzer] -# This package uses rustc crates. -rustc_private=true diff --git a/tools/bookrunner/README.md b/tools/bookrunner/README.md deleted file mode 100644 index 560f2a73c3e4..000000000000 --- a/tools/bookrunner/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Book Runner - -This tool extracts examples from different Rust books, runs Kani on them, and -displays the results in a report. - -Run the following command to build this tool and generate the report: -```bash -./x.py run -i --stage 1 bookrunner -``` diff --git a/tools/bookrunner/configs/books/Rust by Example/Custom Types/Enums/Testcase: linked-list/5.props b/tools/bookrunner/configs/books/Rust by Example/Custom Types/Enums/Testcase: linked-list/5.props deleted file mode 100644 index 1fb5bcc55586..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Custom Types/Enums/Testcase: linked-list/5.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 3 diff --git a/tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/22.props b/tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/22.props deleted file mode 100644 index 55a4254aa3a2..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/22.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 4 --object-bits 9 diff --git a/tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/39.props b/tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/39.props deleted file mode 100644 index 55a4254aa3a2..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/39.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 4 --object-bits 9 diff --git a/tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/5.props b/tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/5.props deleted file mode 100644 index a12b6d6b639f..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/5.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 4 diff --git a/tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/54.props b/tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/54.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/54.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/69.props b/tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/69.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Error handling/Iterating over Results/69.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/Rust by Example/Flow of Control/for and range/102.props b/tools/bookrunner/configs/books/Rust by Example/Flow of Control/for and range/102.props deleted file mode 100644 index 2f4921c863e9..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Flow of Control/for and range/102.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 7 diff --git a/tools/bookrunner/configs/books/Rust by Example/Flow of Control/for and range/63.props b/tools/bookrunner/configs/books/Rust by Example/Flow of Control/for and range/63.props deleted file mode 100644 index 2f4921c863e9..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Flow of Control/for and range/63.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 7 diff --git a/tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Capturing/89.props b/tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Capturing/89.props deleted file mode 100644 index a12b6d6b639f..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Capturing/89.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 4 diff --git a/tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Examples in std/Iterator::any/22.props b/tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Examples in std/Iterator::any/22.props deleted file mode 100644 index a12b6d6b639f..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Examples in std/Iterator::any/22.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 4 diff --git a/tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Examples in std/Searching through iterators/22.props b/tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Examples in std/Searching through iterators/22.props deleted file mode 100644 index a12b6d6b639f..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Examples in std/Searching through iterators/22.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 4 diff --git a/tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Examples in std/Searching through iterators/52.props b/tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Examples in std/Searching through iterators/52.props deleted file mode 100644 index 2f4921c863e9..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Functions/Closures/Examples in std/Searching through iterators/52.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 7 diff --git a/tools/bookrunner/configs/books/Rust by Example/Hello World/Formatted print/Formatting/17.props b/tools/bookrunner/configs/books/Rust by Example/Hello World/Formatted print/Formatting/17.props deleted file mode 100644 index a12b6d6b639f..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Hello World/Formatted print/Formatting/17.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 4 diff --git a/tools/bookrunner/configs/books/Rust by Example/Std library types/HashMap/14.props b/tools/bookrunner/configs/books/Rust by Example/Std library types/HashMap/14.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Std library types/HashMap/14.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/Rust by Example/Std library types/HashMap/Alternate_custom key types/29.props b/tools/bookrunner/configs/books/Rust by Example/Std library types/HashMap/Alternate_custom key types/29.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Std library types/HashMap/Alternate_custom key types/29.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/Rust by Example/Std library types/Strings/12.props b/tools/bookrunner/configs/books/Rust by Example/Std library types/Strings/12.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Std library types/Strings/12.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/Rust by Example/Std misc/Channels/7.props b/tools/bookrunner/configs/books/Rust by Example/Std misc/Channels/7.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Std misc/Channels/7.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/Rust by Example/Std misc/File I_O/read lines/9.props b/tools/bookrunner/configs/books/Rust by Example/Std misc/File I_O/read lines/9.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Std misc/File I_O/read lines/9.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/Rust by Example/Std misc/Program arguments/8.props b/tools/bookrunner/configs/books/Rust by Example/Std misc/Program arguments/8.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Std misc/Program arguments/8.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/Rust by Example/Std misc/Program arguments/Argument parsing/5.props b/tools/bookrunner/configs/books/Rust by Example/Std misc/Program arguments/Argument parsing/5.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Std misc/Program arguments/Argument parsing/5.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/Rust by Example/Std misc/Threads/6.props b/tools/bookrunner/configs/books/Rust by Example/Std misc/Threads/6.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Std misc/Threads/6.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/Rust by Example/Std misc/Threads/Testcase: map-reduce/24.props b/tools/bookrunner/configs/books/Rust by Example/Std misc/Threads/Testcase: map-reduce/24.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Std misc/Threads/Testcase: map-reduce/24.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/Rust by Example/Traits/Disambiguating overlapping traits/11.props b/tools/bookrunner/configs/books/Rust by Example/Traits/Disambiguating overlapping traits/11.props deleted file mode 100644 index c8d448e9c718..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Traits/Disambiguating overlapping traits/11.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 10 diff --git a/tools/bookrunner/configs/books/Rust by Example/Traits/Iterators/12.props b/tools/bookrunner/configs/books/Rust by Example/Traits/Iterators/12.props deleted file mode 100644 index d55948277f51..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Traits/Iterators/12.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 5 diff --git a/tools/bookrunner/configs/books/Rust by Example/Traits/impl Trait/115.props b/tools/bookrunner/configs/books/Rust by Example/Traits/impl Trait/115.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/Rust by Example/Traits/impl Trait/115.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/The Rust Reference/Appendices/Glossary/263.diff b/tools/bookrunner/configs/books/The Rust Reference/Appendices/Glossary/263.diff deleted file mode 100644 index f8eff2a40536..000000000000 --- a/tools/bookrunner/configs/books/The Rust Reference/Appendices/Glossary/263.diff +++ /dev/null @@ -1,4 +0,0 @@ -- 1 -+ 1 let ok_num = Ok::<_, ()>(5); -+ 2 assert!(!ok_num.is_err()); -+ 4 assert!([2, 4, 6][..] == vec[..]); diff --git a/tools/bookrunner/configs/books/The Rust Reference/Appendices/Glossary/263.props b/tools/bookrunner/configs/books/The Rust Reference/Appendices/Glossary/263.props deleted file mode 100644 index a12b6d6b639f..000000000000 --- a/tools/bookrunner/configs/books/The Rust Reference/Appendices/Glossary/263.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 4 diff --git a/tools/bookrunner/configs/books/The Rust Reference/Attributes/Limits/45.props b/tools/bookrunner/configs/books/The Rust Reference/Attributes/Limits/45.props deleted file mode 100644 index 5fc51f2a8212..000000000000 --- a/tools/bookrunner/configs/books/The Rust Reference/Attributes/Limits/45.props +++ /dev/null @@ -1 +0,0 @@ -// kani-codegen-fail diff --git a/tools/bookrunner/configs/books/The Rust Reference/Linkage/190.props b/tools/bookrunner/configs/books/The Rust Reference/Linkage/190.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/The Rust Reference/Linkage/190.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/The Rust Reference/Patterns/367.props b/tools/bookrunner/configs/books/The Rust Reference/Patterns/367.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/The Rust Reference/Patterns/367.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/The Rust Reference/Statements and expressions/Expressions/Loop expressions/133.props b/tools/bookrunner/configs/books/The Rust Reference/Statements and expressions/Expressions/Loop expressions/133.props deleted file mode 100644 index a12b6d6b639f..000000000000 --- a/tools/bookrunner/configs/books/The Rust Reference/Statements and expressions/Expressions/Loop expressions/133.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 4 diff --git a/tools/bookrunner/configs/books/The Rust Reference/Statements and expressions/Expressions/Method call expressions/10.props b/tools/bookrunner/configs/books/The Rust Reference/Statements and expressions/Expressions/Method call expressions/10.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/The Rust Reference/Statements and expressions/Expressions/Method call expressions/10.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/The Rust Reference/Type system/Types/113.props b/tools/bookrunner/configs/books/The Rust Reference/Type system/Types/113.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/The Rust Reference/Type system/Types/113.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/The Rustonomicon/Concurrency/Atomics/198.props b/tools/bookrunner/configs/books/The Rustonomicon/Concurrency/Atomics/198.props deleted file mode 100644 index 41f1f747f90d..000000000000 --- a/tools/bookrunner/configs/books/The Rustonomicon/Concurrency/Atomics/198.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 0 diff --git a/tools/bookrunner/configs/books/The Unstable Book/Language Features/generators/165.props b/tools/bookrunner/configs/books/The Unstable Book/Language Features/generators/165.props deleted file mode 100644 index ef7a7cac2c14..000000000000 --- a/tools/bookrunner/configs/books/The Unstable Book/Language Features/generators/165.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 3 \ No newline at end of file diff --git a/tools/bookrunner/configs/books/The Unstable Book/Language Features/generators/28.props b/tools/bookrunner/configs/books/The Unstable Book/Language Features/generators/28.props deleted file mode 100644 index f69c62f168c6..000000000000 --- a/tools/bookrunner/configs/books/The Unstable Book/Language Features/generators/28.props +++ /dev/null @@ -1 +0,0 @@ -// kani-flags: --enable-unstable --cbmc-args --unwind 5 \ No newline at end of file diff --git a/tools/bookrunner/librustdoc/Cargo.toml b/tools/bookrunner/librustdoc/Cargo.toml deleted file mode 100644 index aaf3a7100c06..000000000000 --- a/tools/bookrunner/librustdoc/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# Modifications Copyright Kani Contributors -# See GitHub history for details. -[package] -name = "rustdoc" -version = "0.0.0" -edition = "2021" -license = "MIT OR Apache-2.0" -publish = false - -# From upstream librustdoc: -# https://github.com/rust-lang/rust/tree/master/src/librustdoc -# Upstream crate does not list license but Rust statues: -# Rust is primarily distributed under the terms of both the MIT -# license and the Apache License (Version 2.0), with portions -# covered by various BSD-like licenses. - -[lib] -path = "lib.rs" - -[dependencies] -pulldown-cmark = { version = "0.10", default-features = false } - -[package.metadata.rust-analyzer] -rustc_private = true diff --git a/tools/bookrunner/librustdoc/README.md b/tools/bookrunner/librustdoc/README.md deleted file mode 100644 index 5a5f547068d6..000000000000 --- a/tools/bookrunner/librustdoc/README.md +++ /dev/null @@ -1,3 +0,0 @@ -For more information about how `librustdoc` works, see the [rustc dev guide]. - -[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/rustdoc.html diff --git a/tools/bookrunner/librustdoc/build.rs b/tools/bookrunner/librustdoc/build.rs deleted file mode 100644 index bca0827fbc46..000000000000 --- a/tools/bookrunner/librustdoc/build.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT - -//! Build script that allows us to build this dependency without bootstrap script. -pub(crate) fn main() { - // Hard code nightly configuration to build librustdoc. - println!("cargo:rustc-env=DOC_RUST_LANG_ORG_CHANNEL=nightly"); -} diff --git a/tools/bookrunner/librustdoc/doctest.rs b/tools/bookrunner/librustdoc/doctest.rs deleted file mode 100644 index 8d4c8c5eda5c..000000000000 --- a/tools/bookrunner/librustdoc/doctest.rs +++ /dev/null @@ -1,322 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT -// -// Modifications Copyright Kani Contributors -// See GitHub history for details. -use rustc_ast as ast; -use rustc_data_structures::sync::Lrc; -use rustc_driver::DEFAULT_LOCALE_RESOURCES; -use rustc_errors::ColorConfig; -use rustc_span::edition::Edition; -use rustc_span::source_map::SourceMap; -use rustc_span::symbol::sym; -use rustc_span::FileName; - -use std::io::{self}; -use std::str; - -use crate::html::markdown::LangString; - -/// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`). -#[derive(Clone, Default)] -pub struct GlobalTestOptions { - /// Whether to disable the default `extern crate my_crate;` when creating doctests. - pub(crate) no_crate_inject: bool, - /// Additional crate-level attributes to add to doctests. - pub(crate) attrs: Vec, -} - -/// Transforms a test into code that can be compiled into a Rust binary, and returns the number of -/// lines before the test code begins as well as if the output stream supports colors or not. -pub fn make_test( - s: &str, - crate_name: Option<&str>, - dont_insert_main: bool, - opts: &GlobalTestOptions, - edition: Edition, - test_id: Option<&str>, -) -> (String, usize, bool) { - let (crate_attrs, everything_else, crates) = partition_source(s); - let everything_else = everything_else.trim(); - let mut line_offset = 0; - let mut prog = String::new(); - let mut supports_color = false; - - if opts.attrs.is_empty() { - // If there aren't any attributes supplied by #![doc(test(attr(...)))], then allow some - // lints that are commonly triggered in doctests. The crate-level test attributes are - // commonly used to make tests fail in case they trigger warnings, so having this there in - // that case may cause some tests to pass when they shouldn't have. - prog.push_str("#![allow(unused)]\n"); - line_offset += 1; - } - - // Next, any attributes that came from the crate root via #![doc(test(attr(...)))]. - for attr in &opts.attrs { - prog.push_str(&format!("#![{attr}]\n")); - line_offset += 1; - } - - // Now push any outer attributes from the example, assuming they - // are intended to be crate attributes. - prog.push_str(&crate_attrs); - prog.push_str(&crates); - - // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern - // crate already is included. - let result = rustc_driver::catch_fatal_errors(|| { - rustc_span::create_session_if_not_set_then(edition, |_| { - use rustc_errors::emitter::stderr_destination; - use rustc_errors::emitter::{Emitter, HumanEmitter}; - use rustc_errors::DiagCtxt; - use rustc_parse::maybe_new_parser_from_source_str; - use rustc_parse::parser::ForceCollect; - use rustc_session::parse::ParseSess; - use rustc_span::source_map::FilePathMapping; - - let filename = FileName::anon_source_code(s); - let source = crates + everything_else; - - // Any errors in parsing should also appear when the doctest is compiled for real, so just - // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - - let fallback_bundle = - rustc_errors::fallback_fluent_bundle(DEFAULT_LOCALE_RESOURCES.to_vec(), false); - supports_color = - HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone()) - .diagnostic_width(Some(80)) - .supports_color(); - - let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); - // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser - let handler = DiagCtxt::new(Box::new(emitter)); - let sess = ParseSess::with_dcx(handler, sm); - - let mut found_main = false; - let mut found_extern_crate = crate_name.is_none(); - let mut found_macro = false; - - let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source) { - Ok(p) => p, - Err(_errs) => { - return (found_main, found_extern_crate, found_macro); - } - }; - - loop { - match parser.parse_item(ForceCollect::No) { - Ok(Some(item)) => { - if !found_main { - if let ast::ItemKind::Fn(..) = item.kind { - if item.ident.name == sym::main { - found_main = true; - } - } - } - - if !found_extern_crate { - if let ast::ItemKind::ExternCrate(original) = item.kind { - // This code will never be reached if `crate_name` is none because - // `found_extern_crate` is initialized to `true` if it is none. - let crate_name = crate_name.unwrap(); - - match original { - Some(name) => found_extern_crate = name.as_str() == crate_name, - None => found_extern_crate = item.ident.as_str() == crate_name, - } - } - } - - if !found_macro { - if let ast::ItemKind::MacCall(..) = item.kind { - found_macro = true; - } - } - - if found_main && found_extern_crate { - break; - } - } - Ok(None) => break, - Err(e) => { - e.cancel(); - break; - } - } - - // The supplied slice is only used for diagnostics, - // which are swallowed here anyway. - parser.maybe_consume_incorrect_semicolon(&[]); - } - - // Reset errors so that they won't be reported as compiler bugs when dropping the - // handler. Any errors in the tests will be reported when the test file is compiled, - // Note that we still need to cancel the errors above otherwise `DiagnosticBuilder` - // will panic on drop. - sess.dcx.reset_err_count(); - - (found_main, found_extern_crate, found_macro) - }) - }); - let (already_has_main, already_has_extern_crate, found_macro) = match result { - Ok(result) => result, - Err(_) => { - // If the parser panicked due to a fatal error, pass the test code through unchanged. - // The error will be reported during compilation. - return (s.to_owned(), 0, false); - } - }; - - // If a doctest's `fn main` is being masked by a wrapper macro, the parsing loop above won't - // see it. In that case, run the old text-based scan to see if they at least have a main - // function written inside a macro invocation. See - // https://github.com/rust-lang/rust/issues/56898 - let already_has_main = if found_macro && !already_has_main { - s.lines() - .map(|line| { - let comment = line.find("//"); - if let Some(comment_begins) = comment { &line[0..comment_begins] } else { line } - }) - .any(|code| code.contains("fn main")) - } else { - already_has_main - }; - - // Don't inject `extern crate std` because it's already injected by the - // compiler. - if !already_has_extern_crate && !opts.no_crate_inject && crate_name != Some("std") { - if let Some(crate_name) = crate_name { - // Don't inject `extern crate` if the crate is never used. - // NOTE: this is terribly inaccurate because it doesn't actually - // parse the source, but only has false positives, not false - // negatives. - if s.contains(crate_name) { - prog.push_str(&format!("extern crate r#{crate_name};\n")); - line_offset += 1; - } - } - } - - // FIXME: This code cannot yet handle no_std test cases yet - if dont_insert_main || already_has_main || prog.contains("![no_std]") { - prog.push_str(everything_else); - } else { - let returns_result = everything_else.trim_end().ends_with("(())"); - // Give each doctest main function a unique name. - // This is for example needed for the tooling around `-Z instrument-coverage`. - let inner_fn_name = if let Some(test_id) = test_id { - format!("_doctest_main_{test_id}") - } else { - "_inner".into() - }; - let inner_attr = if test_id.is_some() { "#[allow(non_snake_case)] " } else { "" }; - let (main_pre, main_post) = if returns_result { - ( - format!( - "fn main() {{ {inner_attr}fn {inner_fn_name}() -> Result<(), impl core::fmt::Debug> {{\n" - ), - format!("\n}} {inner_fn_name}().unwrap() }}"), - ) - } else if test_id.is_some() { - ( - format!("fn main() {{ {inner_attr}fn {inner_fn_name}() {{\n"), - format!("\n}} {inner_fn_name}() }}"), - ) - } else { - ("fn main() {\n".into(), "\n}".into()) - }; - // Note on newlines: We insert a line/newline *before*, and *after* - // the doctest and adjust the `line_offset` accordingly. - // In the case of `-Z instrument-coverage`, this means that the generated - // inner `main` function spans from the doctest opening codeblock to the - // closing one. For example - // /// ``` <- start of the inner main - // /// <- code under doctest - // /// ``` <- end of the inner main - line_offset += 1; - - prog.extend([&main_pre, everything_else, &main_post].iter().cloned()); - } - - debug!("final doctest:\n{}", prog); - - (prog, line_offset, supports_color) -} - -// FIXME(aburka): use a real parser to deal with multiline attributes -fn partition_source(s: &str) -> (String, String, String) { - #[derive(Copy, Clone, PartialEq)] - enum PartitionState { - Attrs, - Crates, - Other, - } - let mut state = PartitionState::Attrs; - let mut before = String::new(); - let mut crates = String::new(); - let mut after = String::new(); - - for line in s.lines() { - let trimline = line.trim(); - - // FIXME(misdreavus): if a doc comment is placed on an extern crate statement, it will be - // shunted into "everything else" - match state { - PartitionState::Attrs => { - state = if trimline.starts_with("#![") - || trimline.chars().all(|c| c.is_whitespace()) - || (trimline.starts_with("//") && !trimline.starts_with("///")) - { - PartitionState::Attrs - } else if trimline.starts_with("extern crate") - || trimline.starts_with("#[macro_use] extern crate") - { - PartitionState::Crates - } else { - PartitionState::Other - }; - } - PartitionState::Crates => { - state = if trimline.starts_with("extern crate") - || trimline.starts_with("#[macro_use] extern crate") - || trimline.chars().all(|c| c.is_whitespace()) - || (trimline.starts_with("//") && !trimline.starts_with("///")) - { - PartitionState::Crates - } else { - PartitionState::Other - }; - } - PartitionState::Other => {} - } - - match state { - PartitionState::Attrs => { - before.push_str(line); - before.push('\n'); - } - PartitionState::Crates => { - crates.push_str(line); - crates.push('\n'); - } - PartitionState::Other => { - after.push_str(line); - after.push('\n'); - } - } - } - - debug!("before:\n{}", before); - debug!("crates:\n{}", crates); - debug!("after:\n{}", after); - - (before, after, crates) -} - -pub trait Tester { - fn add_test(&mut self, test: String, config: LangString, line: usize); - fn get_line(&self) -> usize { - 0 - } - fn register_header(&mut self, _name: &str, _level: u32) {} -} diff --git a/tools/bookrunner/librustdoc/html/markdown.rs b/tools/bookrunner/librustdoc/html/markdown.rs deleted file mode 100644 index 857b42bcd76a..000000000000 --- a/tools/bookrunner/librustdoc/html/markdown.rs +++ /dev/null @@ -1,338 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT -// -// Modifications Copyright Kani Contributors -// See GitHub history for details. -//! Markdown formatting for rustdoc. -//! - -use rustc_span::edition::Edition; - -use std::str; -use std::{borrow::Cow, marker::PhantomData}; - -use crate::doctest; - -use pulldown_cmark::{CodeBlockKind, Event, Parser, Tag}; - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub /* via find_testable_code */ enum ErrorCodes { - Yes, - No, -} - -impl ErrorCodes { - pub(crate) fn as_bool(self) -> bool { - match self { - ErrorCodes::Yes => true, - ErrorCodes::No => false, - } - } -} - -/// Controls whether a line will be hidden or shown in HTML output. -/// -/// All lines are used in documentation tests. -enum Line<'a> { - Hidden(&'a str), - Shown(Cow<'a, str>), -} - -impl<'a> Line<'a> { - fn for_code(self) -> Cow<'a, str> { - match self { - Line::Shown(l) => l, - Line::Hidden(l) => Cow::Borrowed(l), - } - } -} - -// FIXME: There is a minor inconsistency here. For lines that start with ##, we -// have no easy way of removing a potential single space after the hashes, which -// is done in the single # case. This inconsistency seems okay, if non-ideal. In -// order to fix it we'd have to iterate to find the first non-# character, and -// then reallocate to remove it; which would make us return a String. -fn map_line(s: &str) -> Line<'_> { - let trimmed = s.trim(); - if trimmed.starts_with("##") { - Line::Shown(Cow::Owned(s.replacen("##", "#", 1))) - } else if let Some(stripped) = trimmed.strip_prefix("# ") { - // # text - Line::Hidden(stripped) - } else if trimmed == "#" { - // We cannot handle '#text' because it could be #[attr]. - Line::Hidden("") - } else { - Line::Shown(Cow::Borrowed(s)) - } -} - -pub fn find_testable_code( - doc: &str, - tests: &mut T, - error_codes: ErrorCodes, - enable_per_target_ignores: bool, - extra_info: Option<&ExtraInfo<'_>>, -) { - let mut parser = Parser::new(doc).into_offset_iter(); - let mut prev_offset = 0; - let mut nb_lines = 0; - let mut register_header = None; - while let Some((event, offset)) = parser.next() { - match event { - Event::Start(Tag::CodeBlock(kind)) => { - let block_info = match kind { - CodeBlockKind::Fenced(ref lang) => { - if lang.is_empty() { - Default::default() - } else { - LangString::parse( - lang, - error_codes, - enable_per_target_ignores, - extra_info, - ) - } - } - CodeBlockKind::Indented => Default::default(), - }; - if !block_info.rust { - continue; - } - - let mut test_s = String::new(); - - while let Some((Event::Text(s), _)) = parser.next() { - test_s.push_str(&s); - } - let text = test_s - .lines() - .map(|l| map_line(l).for_code()) - .collect::>>() - .join("\n"); - - nb_lines += doc[prev_offset..offset.start].lines().count(); - // If there are characters between the preceding line ending and - // this code block, `str::lines` will return an additional line, - // which we subtract here. - if nb_lines != 0 && !&doc[prev_offset..offset.start].ends_with('\n') { - nb_lines -= 1; - } - let line = tests.get_line() + nb_lines + 1; - tests.add_test(text, block_info, line); - prev_offset = offset.start; - } - Event::Start(Tag::Heading { level, .. }) => { - register_header = Some(level as u32); - } - Event::Text(ref s) if register_header.is_some() => { - let level = register_header.unwrap(); - if s.is_empty() { - tests.register_header("", level); - } else { - tests.register_header(s, level); - } - register_header = None; - } - _ => {} - } - } -} - -// We never pass an actual ExtraInfo, only None for Option -pub struct ExtraInfo<'tcx> { - _unused: PhantomData<&'tcx ()>, -} - -impl<'tcx> ExtraInfo<'tcx> { - fn error_invalid_codeblock_attr(&self, msg: &str, _help: &str) { - unreachable!("{}", msg); - } -} - -#[derive(Eq, PartialEq, Clone, Debug)] -pub struct LangString { - original: String, - pub should_panic: bool, - pub(crate) no_run: bool, - pub ignore: Ignore, - pub(crate) rust: bool, - pub(crate) test_harness: bool, - pub compile_fail: bool, - pub(crate) error_codes: Vec, - pub(crate) allow_fail: bool, - pub edition: Option, -} - -#[derive(Eq, PartialEq, Clone, Debug)] -pub enum Ignore { - All, - None, - Some(Vec), -} - -impl Default for LangString { - fn default() -> Self { - Self { - original: String::new(), - should_panic: false, - no_run: false, - ignore: Ignore::None, - rust: true, - test_harness: false, - compile_fail: false, - error_codes: Vec::new(), - allow_fail: false, - edition: None, - } - } -} - -impl LangString { - fn tokens(string: &str) -> impl Iterator { - // Pandoc, which Rust once used for generating documentation, - // expects lang strings to be surrounded by `{}` and for each token - // to be proceeded by a `.`. Since some of these lang strings are still - // loose in the wild, we strip a pair of surrounding `{}` from the lang - // string and a leading `.` from each token. - - let string = string.trim(); - - let first = string.chars().next(); - let last = string.chars().last(); - - let string = if first == Some('{') && last == Some('}') { - &string[1..string.len() - 1] - } else { - string - }; - - string - .split(|c| c == ',' || c == ' ' || c == '\t') - .map(str::trim) - .map(|token| token.strip_prefix('.').unwrap_or(token)) - .filter(|token| !token.is_empty()) - } - - fn parse( - string: &str, - allow_error_code_check: ErrorCodes, - enable_per_target_ignores: bool, - extra: Option<&ExtraInfo<'_>>, - ) -> LangString { - let allow_error_code_check = allow_error_code_check.as_bool(); - let mut seen_rust_tags = false; - let mut seen_other_tags = false; - let mut data = LangString::default(); - let mut ignores = vec![]; - - string.clone_into(&mut data.original); - - for token in Self::tokens(string) { - match token { - "should_panic" => { - data.should_panic = true; - seen_rust_tags = !seen_other_tags; - } - "no_run" => { - data.no_run = true; - seen_rust_tags = !seen_other_tags; - } - "ignore" => { - data.ignore = Ignore::All; - seen_rust_tags = !seen_other_tags; - } - x if x.starts_with("ignore-") => { - if enable_per_target_ignores { - ignores.push(x.trim_start_matches("ignore-").to_owned()); - seen_rust_tags = !seen_other_tags; - } - } - "allow_fail" => { - data.allow_fail = true; - seen_rust_tags = !seen_other_tags; - } - "rust" => { - data.rust = true; - seen_rust_tags = true; - } - "test_harness" => { - data.test_harness = true; - seen_rust_tags = !seen_other_tags || seen_rust_tags; - } - "compile_fail" => { - data.compile_fail = true; - seen_rust_tags = !seen_other_tags || seen_rust_tags; - data.no_run = true; - } - x if x.starts_with("edition") => { - data.edition = x[7..].parse::().ok(); - } - x if allow_error_code_check && x.starts_with('E') && x.len() == 5 => { - if x[1..].parse::().is_ok() { - data.error_codes.push(x.to_owned()); - seen_rust_tags = !seen_other_tags || seen_rust_tags; - } else { - seen_other_tags = true; - } - } - x if extra.is_some() => { - let s = x.to_lowercase(); - if let Some((flag, help)) = if s == "compile-fail" - || s == "compile_fail" - || s == "compilefail" - { - Some(( - "compile_fail", - "the code block will either not be tested if not marked as a rust one \ - or won't fail if it compiles successfully", - )) - } else if s == "should-panic" || s == "should_panic" || s == "shouldpanic" { - Some(( - "should_panic", - "the code block will either not be tested if not marked as a rust one \ - or won't fail if it doesn't panic when running", - )) - } else if s == "no-run" || s == "no_run" || s == "norun" { - Some(( - "no_run", - "the code block will either not be tested if not marked as a rust one \ - or will be run (which you might not want)", - )) - } else if s == "allow-fail" || s == "allow_fail" || s == "allowfail" { - Some(( - "allow_fail", - "the code block will either not be tested if not marked as a rust one \ - or will be run (which you might not want)", - )) - } else if s == "test-harness" || s == "test_harness" || s == "testharness" { - Some(( - "test_harness", - "the code block will either not be tested if not marked as a rust one \ - or the code will be wrapped inside a main function", - )) - } else { - None - } { - if let Some(extra) = extra { - extra.error_invalid_codeblock_attr( - &format!("unknown attribute `{x}`. Did you mean `{flag}`?"), - help, - ); - } - } - seen_other_tags = true; - } - _ => seen_other_tags = true, - } - } - - // ignore-foo overrides ignore - if !ignores.is_empty() { - data.ignore = Ignore::Some(ignores); - } - - data.rust &= !seen_other_tags || seen_rust_tags; - - data - } -} diff --git a/tools/bookrunner/librustdoc/html/mod.rs b/tools/bookrunner/librustdoc/html/mod.rs deleted file mode 100644 index bb4c77e39854..000000000000 --- a/tools/bookrunner/librustdoc/html/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT -// -// Modifications Copyright Kani Contributors -// See GitHub history for details. -// used by the error-index generator, so it needs to be public -pub mod markdown; diff --git a/tools/bookrunner/librustdoc/lib.rs b/tools/bookrunner/librustdoc/lib.rs deleted file mode 100644 index cddc369b4dc1..000000000000 --- a/tools/bookrunner/librustdoc/lib.rs +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT -// -// Modifications Copyright Kani Contributors -// See GitHub history for details. -#![doc( - html_root_url = "https://doc.rust-lang.org/nightly/", - html_playground_url = "https://play.rust-lang.org/" -)] -#![feature(rustc_private)] -#![feature(assert_matches)] -#![feature(box_patterns)] -#![feature(control_flow_enum)] -#![feature(test)] -#![feature(never_type)] -#![feature(type_ascription)] -#![feature(iter_intersperse)] -#![recursion_limit = "256"] -#![warn(rustc::internal)] -#![allow(clippy::collapsible_if, clippy::collapsible_else_if, clippy::arc_with_non_send_sync)] - -#[macro_use] -extern crate tracing; - -// N.B. these need `extern crate` even in 2018 edition -// because they're loaded implicitly from the sysroot. -// The reason they're loaded from the sysroot is because -// the rustdoc artifacts aren't stored in rustc's cargo target directory. -// So if `rustc` was specified in Cargo.toml, this would spuriously rebuild crates. -// -// Dependencies listed in Cargo.toml do not need `extern crate`. - -extern crate rustc_ast; -extern crate rustc_ast_lowering; -extern crate rustc_ast_pretty; -extern crate rustc_attr; -extern crate rustc_const_eval; -extern crate rustc_data_structures; -extern crate rustc_driver; -extern crate rustc_errors; -extern crate rustc_expand; -extern crate rustc_feature; -extern crate rustc_hir; -extern crate rustc_hir_pretty; -extern crate rustc_index; -extern crate rustc_infer; -extern crate rustc_interface; -extern crate rustc_lexer; -extern crate rustc_lint; -extern crate rustc_lint_defs; -extern crate rustc_macros; -extern crate rustc_metadata; -extern crate rustc_middle; -extern crate rustc_parse; -extern crate rustc_passes; -extern crate rustc_resolve; -extern crate rustc_serialize; -extern crate rustc_session; -extern crate rustc_span; -extern crate rustc_target; -extern crate rustc_trait_selection; -extern crate test; - -pub mod doctest; -// used by the error-index generator, so it needs to be public -pub mod html; diff --git a/tools/bookrunner/print.sh b/tools/bookrunner/print.sh deleted file mode 100755 index 904409ee1c87..000000000000 --- a/tools/bookrunner/print.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -# Copyright Kani Contributors -# SPDX-License-Identifier: Apache-2.0 OR MIT - -# `rustdoc` treats this script as `rustc` and sends code extracted from markdown -# files to stdin of this script. Instead of compiling the code, this scripts -# simply copies the contents of stdin to the location where `rustdoc` caches the -# "compiled" output. - -FILE="$6" -BASE=`basename "$FILE"` -mkdir -p "$BASE" -cp "/dev/stdin" "$FILE" diff --git a/tools/bookrunner/rust-doc/nomicon b/tools/bookrunner/rust-doc/nomicon deleted file mode 160000 index c05c452b3635..000000000000 --- a/tools/bookrunner/rust-doc/nomicon +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c05c452b36358821bf4122f9c418674edd1d713d diff --git a/tools/bookrunner/rust-doc/reference b/tools/bookrunner/rust-doc/reference deleted file mode 160000 index f8ba2f12df60..000000000000 --- a/tools/bookrunner/rust-doc/reference +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f8ba2f12df60ee19b96de24ae5b73af3de8a446b diff --git a/tools/bookrunner/rust-doc/rust-by-example b/tools/bookrunner/rust-doc/rust-by-example deleted file mode 160000 index 43f82530210b..000000000000 --- a/tools/bookrunner/rust-doc/rust-by-example +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 43f82530210b83cf888282b207ed13d5893da9b2 diff --git a/tools/bookrunner/rust-doc/unstable-book/.gitignore b/tools/bookrunner/rust-doc/unstable-book/.gitignore deleted file mode 100644 index 7585238efedf..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/.gitignore +++ /dev/null @@ -1 +0,0 @@ -book diff --git a/tools/bookrunner/rust-doc/unstable-book/book.toml b/tools/bookrunner/rust-doc/unstable-book/book.toml deleted file mode 100644 index dfbd8311dae4..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/book.toml +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# Modifications Copyright Kani Contributors -# See GitHub history for details. -[book] -title = "The Rust Unstable Book" -author = "The Rust Community" - -[output.html] -git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/unstable-book" diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags.md deleted file mode 100644 index 43eadb351016..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags.md +++ /dev/null @@ -1 +0,0 @@ -# Compiler flags diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/branch-protection.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/branch-protection.md deleted file mode 100644 index 85403748e1dc..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/branch-protection.md +++ /dev/null @@ -1,18 +0,0 @@ -# `branch-protection` - -This option lets you enable branch authentication instructions on AArch64. -This option is ignored for non-AArch64 architectures. -It takes some combination of the following values, separated by a `,`. - -- `pac-ret` - Enable pointer authentication for non-leaf functions. -- `leaf` - Enable pointer authentication for all functions, including leaf functions. -- `b-key` - Sign return addresses with key B, instead of the default key A. -- `bti` - Enable branch target identification. - -`leaf` and `b-key` are only valid if `pac-ret` was previously specified. -For example, `-Z branch-protection=bti,pac-ret,leaf` is valid, but -`-Z branch-protection=bti,leaf,pac-ret` is not. - -Rust's standard library does not ship with BTI or pointer authentication enabled by default. -In Cargo projects the standard library can be recompiled with pointer authentication using the nightly -[build-std](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std) feature. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/codegen-backend.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/codegen-backend.md deleted file mode 100644 index 3c0cd32fae17..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/codegen-backend.md +++ /dev/null @@ -1,31 +0,0 @@ -# `codegen-backend` - -The tracking issue for this feature is: [#77933](https://github.com/rust-lang/rust/issues/77933). - ------------------------- - -This feature allows you to specify a path to a dynamic library to use as rustc's -code generation backend at runtime. - -Set the `-Zcodegen-backend=` compiler flag to specify the location of the -backend. The library must be of crate type `dylib` and must contain a function -named `__rustc_codegen_backend` with a signature of `fn() -> Box`. - -## Example -See also the [`hotplug_codegen_backend`](https://github.com/rust-lang/rust/tree/master/src/test/run-make-fulldeps/hotplug_codegen_backend) test -for a full example. - -```rust,ignore (partial-example) -use rustc_codegen_ssa::traits::CodegenBackend; - -struct MyBackend; - -impl CodegenBackend for MyBackend { - // Implement codegen methods -} - -#[no_mangle] -pub fn __rustc_codegen_backend() -> Box { - Box::new(MyBackend) -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/control-flow-guard.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/control-flow-guard.md deleted file mode 100644 index 08c16d95f467..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/control-flow-guard.md +++ /dev/null @@ -1,59 +0,0 @@ -# `control-flow-guard` - -The tracking issue for this feature is: [#68793](https://github.com/rust-lang/rust/issues/68793). - ------------------------- - -The rustc flag `-Z control-flow-guard` enables the Windows [Control Flow Guard](https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard) (CFG) platform security feature. - -CFG is an exploit mitigation designed to enforce control-flow integrity for software running on supported [Windows platforms (Windows 8.1 onwards)](https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard). Specifically, CFG uses runtime checks to validate the target address of every indirect call/jump before allowing the call to complete. - -During compilation, the compiler identifies all indirect calls/jumps and adds CFG checks. It also emits metadata containing the relative addresses of all address-taken functions. At runtime, if the binary is run on a CFG-aware operating system, the loader uses the CFG metadata to generate a bitmap of the address space and marks those addresses that contain valid targets. On each indirect call, the inserted check determines whether the target address is marked in this bitmap. If the target is not valid, the process is terminated. - -In terms of interoperability: -- Code compiled with CFG enabled can be linked with libraries and object files that are not compiled with CFG. In this case, a CFG-aware linker can identify address-taken functions in the non-CFG libraries. -- Libraries compiled with CFG can linked into non-CFG programs. In this case, the CFG runtime checks in the libraries are not used (i.e. the mitigation is completely disabled). - -CFG functionality is completely implemented in the LLVM backend and is supported for X86 (32-bit and 64-bit), ARM, and Aarch64 targets. The rustc flag adds the relevant LLVM module flags to enable the feature. This flag will be ignored for all non-Windows targets. - - -## When to use Control Flow Guard - -The primary motivation for enabling CFG in Rust is to enhance security when linking against non-Rust code, especially C/C++ code. To achieve full CFG protection, all indirect calls (including any from Rust code) must have the appropriate CFG checks, as added by this flag. CFG can also improve security for Rust code that uses the `unsafe` keyword. - -Another motivation behind CFG is to harden programs against [return-oriented programming (ROP)](https://en.wikipedia.org/wiki/Return-oriented_programming) attacks. CFG disallows an attacker from taking advantage of the program's own instructions while redirecting control flow in unexpected ways. - -## Overhead of Control Flow Guard - -The CFG checks and metadata can potentially increase binary size and runtime overhead. The magnitude of any increase depends on the number and frequency of indirect calls. For example, enabling CFG for the Rust standard library increases binary size by approximately 0.14%. Enabling CFG in the SPEC CPU 2017 Integer Speed benchmark suite (compiled with Clang/LLVM) incurs approximate runtime overheads of between 0% and 8%, with a geometric mean of 2.9%. - - -## Testing Control Flow Guard - -The rustc flag `-Z control-flow-guard=nochecks` instructs LLVM to emit the list of valid call targets without inserting runtime checks. This flag should only be used for testing purposes as it does not provide security enforcement. - - -## Control Flow Guard in libraries - -It is strongly recommended to also enable CFG checks for all linked libraries, including the standard library. - -To enable CFG in the standard library, use the [cargo `-Z build-std` functionality][build-std] to recompile the standard library with the same configuration options as the main program. - -[build-std]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std - -For example: -```cmd -rustup toolchain install --force nightly -rustup component add rust-src -SET RUSTFLAGS=-Z control-flow-guard -cargo +nightly build -Z build-std --target x86_64-pc-windows-msvc -``` - -```PowerShell -rustup toolchain install --force nightly -rustup component add rust-src -$Env:RUSTFLAGS = "-Z control-flow-guard" -cargo +nightly build -Z build-std --target x86_64-pc-windows-msvc -``` - -Alternatively, if you are building the standard library from source, you can set `control-flow-guard = true` in the config.toml file. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/debug_info_for_profiling.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/debug_info_for_profiling.md deleted file mode 100644 index 44bd3baeeedf..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/debug_info_for_profiling.md +++ /dev/null @@ -1,35 +0,0 @@ -# `debug-info-for-profiling - ---- - -## Introduction - -Automatic Feedback Directed Optimization (AFDO) is a method for using sampling -based profiles to guide optimizations. This is contrasted with other methods of -FDO or profile-guided optimization (PGO) which use instrumented profiling. - -Unlike PGO (controlled by the `rustc` flags `-Cprofile-generate` and -`-Cprofile-use`), a binary being profiled does not perform significantly worse, -and thus it's possible to profile binaries used in real workflows and not -necessary to construct artificial workflows. - -## Use - -In order to use AFDO, the target platform must be Linux running on an `x86_64` -architecture with the performance profiler `perf` available. In addition, the -external tool `create_llvm_prof` from [this repository] must be used. - -Given a Rust file `main.rs`, we can produce an optimized binary as follows: - -```shell -rustc -O -Zdebug-info-for-profiling main.rs -o main -perf record -b ./main -create_llvm_prof --binary=main --out=code.prof -rustc -O -Zprofile-sample-use=code.prof main.rs -o main2 -``` - -The `perf` command produces a profile `perf.data`, which is then used by the -`create_llvm_prof` command to create `code.prof`. This final profile is then -used by `rustc` to guide optimizations in producing the binary `main2`. - -[this repository]: https://github.com/google/autofdo diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/emit-stack-sizes.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/emit-stack-sizes.md deleted file mode 100644 index 47f45a0b91f8..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/emit-stack-sizes.md +++ /dev/null @@ -1,167 +0,0 @@ -# `emit-stack-sizes` - -The tracking issue for this feature is: [#54192] - -[#54192]: https://github.com/rust-lang/rust/issues/54192 - ------------------------- - -The rustc flag `-Z emit-stack-sizes` makes LLVM emit stack size metadata. - -> **NOTE**: This LLVM feature only supports the ELF object format as of LLVM -> 8.0. Using this flag with targets that use other object formats (e.g. macOS -> and Windows) will result in it being ignored. - -Consider this crate: - -``` -#![crate_type = "lib"] - -use std::ptr; - -pub fn foo() { - // this function doesn't use the stack -} - -pub fn bar() { - let xs = [0u32; 2]; - - // force LLVM to allocate `xs` on the stack - unsafe { ptr::read_volatile(&xs.as_ptr()); } -} -``` - -Using the `-Z emit-stack-sizes` flag produces extra linker sections in the -output *object file*. - -``` console -$ rustc -C opt-level=3 --emit=obj foo.rs - -$ size -A foo.o -foo.o : -section size addr -.text 0 0 -.text._ZN3foo3foo17he211d7b4a3a0c16eE 1 0 -.text._ZN3foo3bar17h1acb594305f70c2eE 22 0 -.note.GNU-stack 0 0 -.eh_frame 72 0 -Total 95 - -$ rustc -C opt-level=3 --emit=obj -Z emit-stack-sizes foo.rs - -$ size -A foo.o -foo.o : -section size addr -.text 0 0 -.text._ZN3foo3foo17he211d7b4a3a0c16eE 1 0 -.stack_sizes 9 0 -.text._ZN3foo3bar17h1acb594305f70c2eE 22 0 -.stack_sizes 9 0 -.note.GNU-stack 0 0 -.eh_frame 72 0 -Total 113 -``` - -As of LLVM 7.0 the data will be written into a section named `.stack_sizes` and -the format is "an array of pairs of function symbol values (pointer size) and -stack sizes (unsigned LEB128)". - -``` console -$ objdump -d foo.o - -foo.o: file format elf64-x86-64 - -Disassembly of section .text._ZN3foo3foo17he211d7b4a3a0c16eE: - -0000000000000000 <_ZN3foo3foo17he211d7b4a3a0c16eE>: - 0: c3 retq - -Disassembly of section .text._ZN3foo3bar17h1acb594305f70c2eE: - -0000000000000000 <_ZN3foo3bar17h1acb594305f70c2eE>: - 0: 48 83 ec 10 sub $0x10,%rsp - 4: 48 8d 44 24 08 lea 0x8(%rsp),%rax - 9: 48 89 04 24 mov %rax,(%rsp) - d: 48 8b 04 24 mov (%rsp),%rax - 11: 48 83 c4 10 add $0x10,%rsp - 15: c3 retq - -$ objdump -s -j .stack_sizes foo.o - -foo.o: file format elf64-x86-64 - -Contents of section .stack_sizes: - 0000 00000000 00000000 00 ......... -Contents of section .stack_sizes: - 0000 00000000 00000000 10 ......... -``` - -It's important to note that linkers will discard this linker section by default. -To preserve the section you can use a linker script like the one shown below. - -``` text -/* file: keep-stack-sizes.x */ -SECTIONS -{ - /* `INFO` makes the section not allocatable so it won't be loaded into memory */ - .stack_sizes (INFO) : - { - KEEP(*(.stack_sizes)); - } -} -``` - -The linker script must be passed to the linker using a rustc flag like `-C -link-arg`. - -``` -// file: src/main.rs -use std::ptr; - -#[inline(never)] -fn main() { - let xs = [0u32; 2]; - - // force LLVM to allocate `xs` on the stack - unsafe { ptr::read_volatile(&xs.as_ptr()); } -} -``` - -``` console -$ RUSTFLAGS="-Z emit-stack-sizes" cargo build --release - -$ size -A target/release/hello | grep stack_sizes || echo section was not found -section was not found - -$ RUSTFLAGS="-Z emit-stack-sizes" cargo rustc --release -- \ - -C link-arg=-Wl,-Tkeep-stack-sizes.x \ - -C link-arg=-N - -$ size -A target/release/hello | grep stack_sizes -.stack_sizes 90 176272 - -$ # non-allocatable section (flags don't contain the "A" (alloc) flag) -$ readelf -S target/release/hello -Section Headers: - [Nr] Name Type Address Offset - Size EntSize Flags Link Info Align -(..) - [1031] .stack_sizes PROGBITS 000000000002b090 0002b0f0 - 000000000000005a 0000000000000000 L 5 0 1 - -$ objdump -s -j .stack_sizes target/release/hello - -target/release/hello: file format elf64-x86-64 - -Contents of section .stack_sizes: - 2b090 c0040000 00000000 08f00400 00000000 ................ - 2b0a0 00080005 00000000 00000810 05000000 ................ - 2b0b0 00000000 20050000 00000000 10400500 .... ........@.. - 2b0c0 00000000 00087005 00000000 00000080 ......p......... - 2b0d0 05000000 00000000 90050000 00000000 ................ - 2b0e0 00a00500 00000000 0000 .......... -``` - -> Author note: I'm not entirely sure why, in *this* case, `-N` is required in -> addition to `-Tkeep-stack-sizes.x`. For example, it's not required when -> producing statically linked files for the ARM Cortex-M architecture. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/extern-location.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/extern-location.md deleted file mode 100644 index 1c80d5426bf7..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/extern-location.md +++ /dev/null @@ -1,31 +0,0 @@ -# `extern-location` - -MCP for this feature: [#303] - -[#303]: https://github.com/rust-lang/compiler-team/issues/303 - ------------------------- - -The `unused-extern-crates` lint reports when a crate was specified on the rustc -command-line with `--extern name=path` but no symbols were referenced in it. -This is useful to know, but it's hard to map that back to a specific place a user -or tool could fix (ie, to remove the unused dependency). - -The `--extern-location` flag allows the build system to associate a location with -the `--extern` option, which is then emitted as part of the diagnostics. This location -is abstract and just round-tripped through rustc; the compiler never attempts to -interpret it in any way. - -There are two supported forms of location: a bare string, or a blob of json: -- `--extern-location foo=raw:Makefile:123` would associate the raw string `Makefile:123` -- `--extern-location 'bar=json:{"target":"//my_project:library","dep":"//common:serde"}` would - associate the json structure with `--extern bar=`, indicating which dependency of - which rule introduced the unused extern crate. - -This primarily intended to be used with tooling - for example a linter which can automatically -remove unused dependencies - rather than being directly presented to users. - -`raw` locations are presented as part of the normal rendered diagnostics and included in -the json form. `json` locations are only included in the json form of diagnostics, -as a `tool_metadata` field. For `raw` locations `tool_metadata` is simply a json string, -whereas `json` allows the rustc invoker to fully control its form and content. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/img/llvm-cov-show-01.png b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/img/llvm-cov-show-01.png deleted file mode 100644 index 35f04594347a398ca8321faf85fa018b192fa843..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 416748 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 eb{ffNh(JNO) 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�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ℑ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%0Arl<=>f7dzSI3d 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 A5C8xG diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/instrument-coverage.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/instrument-coverage.md deleted file mode 100644 index 39eb407269c1..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/instrument-coverage.md +++ /dev/null @@ -1,346 +0,0 @@ -# `instrument-coverage` - -The tracking issue for this feature is: [#79121]. - -[#79121]: https://github.com/rust-lang/rust/issues/79121 - ---- - -## Introduction - -The Rust compiler includes two code coverage implementations: - -- A GCC-compatible, gcov-based coverage implementation, enabled with `-Z profile`, which derives coverage data based on DebugInfo. -- A source-based code coverage implementation, enabled with `-Z instrument-coverage`, which uses LLVM's native, efficient coverage instrumentation to generate very precise coverage data. - -This document describes how to enable and use the LLVM instrumentation-based coverage, via the `-Z instrument-coverage` compiler flag. - -## How it works - -When `-Z instrument-coverage` is enabled, the Rust compiler enhances rust-based libraries and binaries by: - -- Automatically injecting calls to an LLVM intrinsic ([`llvm.instrprof.increment`]), at functions and branches in compiled code, to increment counters when conditional sections of code are executed. -- Embedding additional information in the data section of each library and binary (using the [LLVM Code Coverage Mapping Format] _Version 5_, if compiling with LLVM 12, or _Version 6_, if compiling with LLVM 13 or higher), to define the code regions (start and end positions in the source code) being counted. - -When running a coverage-instrumented program, the counter values are written to a `profraw` file at program termination. LLVM bundles tools that read the counter results, combine those results with the coverage map (embedded in the program binary), and generate coverage reports in multiple formats. - -[`llvm.instrprof.increment`]: https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic -[llvm code coverage mapping format]: https://llvm.org/docs/CoverageMappingFormat.html - -> **Note**: `-Z instrument-coverage` also automatically enables `-C symbol-mangling-version=v0` (tracking issue [#60705]). The `v0` symbol mangler is strongly recommended, but be aware that this demangler is also experimental. The `v0` demangler can be overridden by explicitly adding `-Z unstable-options -C symbol-mangling-version=legacy`. - -[#60705]: https://github.com/rust-lang/rust/issues/60705 - -## Enable coverage profiling in the Rust compiler - -Rust's source-based code coverage requires the Rust "profiler runtime". Without it, compiling with `-Z instrument-coverage` generates an error that the profiler runtime is missing. - -The Rust `nightly` distribution channel includes the profiler runtime, by default. - -> **Important**: If you are building the Rust compiler from the source distribution, the profiler runtime is _not_ enabled in the default `config.toml.example`. Edit your `config.toml` file and ensure the `profiler` feature is set it to `true` (either under the `[build]` section, or under the settings for an individual `[target.]`): -> -> ```toml -> # Build the profiler runtime (required when compiling with options that depend -> # on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`). -> profiler = true -> ``` - -### Building the demangler - -LLVM coverage reporting tools generate results that can include function names and other symbol references, and the raw coverage results report symbols using the compiler's "mangled" version of the symbol names, which can be difficult to interpret. To work around this issue, LLVM coverage tools also support a user-specified symbol name demangler. - -One option for a Rust demangler is [`rustfilt`], which can be installed with: - -```shell -cargo install rustfilt -``` - -Another option, if you are building from the Rust compiler source distribution, is to use the `rust-demangler` tool included in the Rust source distribution, which can be built with: - -```shell -$ ./x.py build rust-demangler -``` - -[`rustfilt`]: https://crates.io/crates/rustfilt - -## Compiling with coverage enabled - -Set the `-Z instrument-coverage` compiler flag in order to enable LLVM source-based code coverage profiling. - -The default option generates coverage for all functions, including unused (never called) functions and generics. The compiler flag supports an optional value to tailor this behavior. (See [`-Z instrument-coverage=`](#-z-instrument-coverageoptions), below.) - -With `cargo`, you can instrument your program binary _and_ dependencies at the same time. - -For example (if your project's Cargo.toml builds a binary by default): - -```shell -$ cd your-project -$ cargo clean -$ RUSTFLAGS="-Z instrument-coverage" cargo build -``` - -If `cargo` is not configured to use your `profiler`-enabled version of `rustc`, set the path explicitly via the `RUSTC` environment variable. Here is another example, using a `stage1` build of `rustc` to compile an `example` binary (from the [`json5format`] crate): - -```shell -$ RUSTC=$HOME/rust/build/x86_64-unknown-linux-gnu/stage1/bin/rustc \ - RUSTFLAGS="-Z instrument-coverage" \ - cargo build --example formatjson5 -``` - -> **Note**: that some compiler options, combined with `-Z instrument-coverage`, can produce LLVM IR and/or linked binaries that are incompatible with LLVM coverage maps. For example, coverage requires references to actual functions in LLVM IR. If any covered function is optimized out, the coverage tools may not be able to process the coverage results. If you need to pass additional options, with coverage enabled, test them early, to confirm you will get the coverage results you expect. - -## Running the instrumented binary to generate raw coverage profiling data - -In the previous example, `cargo` generated the coverage-instrumented binary `formatjson5`: - -```shell -$ echo "{some: 'thing'}" | target/debug/examples/formatjson5 - -``` - -```json5 -{ - some: "thing", -} -``` - -After running this program, a new file, `default.profraw`, should be in the current working directory. It's often preferable to set a specific file name or path. You can change the output file using the environment variable `LLVM_PROFILE_FILE`: - -```shell -$ echo "{some: 'thing'}" \ - | LLVM_PROFILE_FILE="formatjson5.profraw" target/debug/examples/formatjson5 - -... -$ ls formatjson5.profraw -formatjson5.profraw -``` - -If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing directory structure will be created. Additionally, the following special pattern strings are rewritten: - -- `%p` - The process ID. -- `%h` - The hostname of the machine running the program. -- `%t` - The value of the TMPDIR environment variable. -- `%Nm` - the instrumented binary’s signature: The runtime creates a pool of N raw profiles, used for on-line profile merging. The runtime takes care of selecting a raw profile from the pool, locking it, and updating it before the program exits. `N` must be between `1` and `9`, and defaults to `1` if omitted (with simply `%m`). -- `%c` - Does not add anything to the filename, but enables a mode (on some platforms, including Darwin) in which profile counter updates are continuously synced to a file. This means that if the instrumented program crashes, or is killed by a signal, perfect coverage information can still be recovered. - -## Installing LLVM coverage tools - -LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process coverage data and generate reports. There are several ways to find and/or install these tools, but note that the coverage mapping data generated by the Rust compiler requires LLVM version 12 or higher. (`llvm-cov --version` typically shows the tool's LLVM version number.): - -- The LLVM tools may be installed (or installable) directly to your OS (such as via `apt-get`, for Linux). -- If you are building the Rust compiler from source, you can optionally use the bundled LLVM tools, built from source. Those tool binaries can typically be found in your build platform directory at something like: `rust/build/x86_64-unknown-linux-gnu/llvm/bin/llvm-*`. -- You can install compatible versions of these tools via `rustup`. - -The `rustup` option is guaranteed to install a compatible version of the LLVM tools, but they can be hard to find. We recommend [`cargo-binutils`], which installs Rust-specific wrappers around these and other LLVM tools, so you can invoke them via `cargo` commands! - -```shell -$ rustup component add llvm-tools-preview -$ cargo install cargo-binutils -$ cargo profdata -- --help # note the additional "--" preceding the tool-specific arguments -``` - -[`cargo-binutils`]: https://crates.io/crates/cargo-binutils - -## Creating coverage reports - -Raw profiles have to be indexed before they can be used to generate coverage reports. This is done using [`llvm-profdata merge`] (or `cargo profdata -- merge`), which can combine multiple raw profiles and index them at the same time: - -```shell -$ llvm-profdata merge -sparse formatjson5.profraw -o formatjson5.profdata -``` - -Finally, the `.profdata` file is used, in combination with the coverage map (from the program binary) to generate coverage reports using [`llvm-cov report`] (or `cargo cov -- report`), for a coverage summaries; and [`llvm-cov show`] (or `cargo cov -- show`), to see detailed coverage of lines and regions (character ranges) overlaid on the original source code. - -These commands have several display and filtering options. For example: - -```shell -$ llvm-cov show -Xdemangler=rustfilt target/debug/examples/formatjson5 \ - -instr-profile=formatjson5.profdata \ - -show-line-counts-or-regions \ - -show-instantiations \ - -name=add_quoted_string -``` - -Screenshot of sample `llvm-cov show` result, for function add_quoted_string -
-
- -Some of the more notable options in this example include: - -- `--Xdemangler=rustfilt` - the command name or path used to demangle Rust symbols (`rustfilt` in the example, but this could also be a path to the `rust-demangler` tool) -- `target/debug/examples/formatjson5` - the instrumented binary (from which to extract the coverage map) -- `--instr-profile=.profdata` - the location of the `.profdata` file created by `llvm-profdata merge` (from the `.profraw` file generated by the instrumented binary) -- `--name=` - to show coverage for a specific function (or, consider using another filter option, such as `--name-regex=`) - -[`llvm-profdata merge`]: https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-merge -[`llvm-cov report`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-report -[`llvm-cov show`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-show - -> **Note**: Coverage can also be disabled on an individual function by annotating the function with the [`no_coverage` attribute] (which requires the feature flag `#![feature(no_coverage)]`). - -[`no_coverage` attribute]: ../language-features/no-coverage.md - -## Interpreting reports - -There are four statistics tracked in a coverage summary: - -- Function coverage is the percentage of functions that have been executed at least once. A function is considered to be executed if any of its instantiations are executed. -- Instantiation coverage is the percentage of function instantiations that have been executed at least once. Generic functions and functions generated from macros are two kinds of functions that may have multiple instantiations. -- Line coverage is the percentage of code lines that have been executed at least once. Only executable lines within function bodies are considered to be code lines. -- Region coverage is the percentage of code regions that have been executed at least once. A code region may span multiple lines: for example, in a large function body with no control flow. In other cases, a single line can contain multiple code regions: `return x || (y && z)` has countable code regions for `x` (which may resolve the expression, if `x` is `true`), `|| (y && z)` (executed only if `x` was `false`), and `return` (executed in either situation). - -Of these four statistics, function coverage is usually the least granular while region coverage is the most granular. The project-wide totals for each statistic are listed in the summary. - -## Test coverage - -A typical use case for coverage analysis is test coverage. Rust's source-based coverage tools can both measure your tests' code coverage as percentage, and pinpoint functions and branches not tested. - -The following example (using the [`json5format`] crate, for demonstration purposes) show how to generate and analyze coverage results for all tests in a crate. - -Since `cargo test` both builds and runs the tests, we set both the additional `RUSTFLAGS`, to add the `-Z instrument-coverage` flag, and `LLVM_PROFILE_FILE`, to set a custom filename for the raw profiling data generated during the test runs. Since there may be more than one test binary, apply `%m` in the filename pattern. This generates unique names for each test binary. (Otherwise, each executed test binary would overwrite the coverage results from the previous binary.) - -```shell -$ RUSTFLAGS="-Z instrument-coverage" \ - LLVM_PROFILE_FILE="json5format-%m.profraw" \ - cargo test --tests -``` - -Make note of the test binary file paths, displayed after the word "`Running`" in the test output: - -```text - ... - Compiling json5format v0.1.3 ($HOME/json5format) - Finished test [unoptimized + debuginfo] target(s) in 14.60s - - Running target/debug/deps/json5format-fececd4653271682 -running 25 tests -... -test result: ok. 25 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out - - Running target/debug/deps/lib-30768f9c53506dc5 -running 31 tests -... -test result: ok. 31 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out -``` - -You should have one or more `.profraw` files now, one for each test binary. Run the `profdata` tool to merge them: - -```shell -$ cargo profdata -- merge \ - -sparse json5format-*.profraw -o json5format.profdata -``` - -Then run the `cov` tool, with the `profdata` file and all test binaries: - -```shell -$ cargo cov -- report \ - --use-color --ignore-filename-regex='/.cargo/registry' \ - --instr-profile=json5format.profdata \ - --object target/debug/deps/lib-30768f9c53506dc5 \ - --object target/debug/deps/json5format-fececd4653271682 -$ cargo cov -- show \ - --use-color --ignore-filename-regex='/.cargo/registry' \ - --instr-profile=json5format.profdata \ - --object target/debug/deps/lib-30768f9c53506dc5 \ - --object target/debug/deps/json5format-fececd4653271682 \ - --show-instantiations --show-line-counts-or-regions \ - --Xdemangler=rustfilt | less -R -``` - -> **Note**: The command line option `--ignore-filename-regex=/.cargo/registry`, which excludes the sources for dependencies from the coverage results.\_ - -### Tips for listing the binaries automatically - -For `bash` users, one suggested way to automatically complete the `cov` command with the list of binaries is with a command like: - -```bash -$ cargo cov -- report \ - $( \ - for file in \ - $( \ - RUSTFLAGS="-Z instrument-coverage" \ - cargo test --tests --no-run --message-format=json \ - | jq -r "select(.profile.test == true) | .filenames[]" \ - | grep -v dSYM - \ - ); \ - do \ - printf "%s %s " -object $file; \ - done \ - ) \ - --instr-profile=json5format.profdata --summary-only # and/or other options -``` - -Adding `--no-run --message-format=json` to the _same_ `cargo test` command used to run -the tests (including the same environment variables and flags) generates output in a JSON -format that `jq` can easily query. - -The `printf` command takes this list and generates the `--object ` arguments -for each listed test binary. - -### Including doc tests - -The previous examples run `cargo test` with `--tests`, which excludes doc tests.[^79417] - -To include doc tests in the coverage results, drop the `--tests` flag, and apply the -`-Z instrument-coverage` flag, and some doc-test-specific options in the -`RUSTDOCFLAGS` environment variable. (The `cargo profdata` command does not change.) - -```bash -$ RUSTFLAGS="-Z instrument-coverage" \ - RUSTDOCFLAGS="-Z instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \ - LLVM_PROFILE_FILE="json5format-%m.profraw" \ - cargo test -$ cargo profdata -- merge \ - -sparse json5format-*.profraw -o json5format.profdata -``` - -The `-Z unstable-options --persist-doctests` flag is required, to save the test binaries -(with their coverage maps) for `llvm-cov`. - -```bash -$ cargo cov -- report \ - $( \ - for file in \ - $( \ - RUSTFLAGS="-Z instrument-coverage" \ - RUSTDOCFLAGS="-Z instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \ - cargo test --no-run --message-format=json \ - | jq -r "select(.profile.test == true) | .filenames[]" \ - | grep -v dSYM - \ - ) \ - target/debug/doctestbins/*/rust_out; \ - do \ - [[ -x $file ]] && printf "%s %s " -object $file; \ - done \ - ) \ - --instr-profile=json5format.profdata --summary-only # and/or other options -``` - -> **Note**: The differences in this `cargo cov` command, compared with the version without -> doc tests, include: - -- The `cargo test ... --no-run` command is updated with the same environment variables - and flags used to _build_ the tests, _including_ the doc tests. (`LLVM_PROFILE_FILE` - is only used when _running_ the tests.) -- The file glob pattern `target/debug/doctestbins/*/rust_out` adds the `rust_out` - binaries generated for doc tests (note, however, that some `rust_out` files may not - be executable binaries). -- `[[ -x $file ]] &&` filters the files passed on to the `printf`, to include only - executable binaries. - -[^79417]: - There is ongoing work to resolve a known issue - [(#79417)](https://github.com/rust-lang/rust/issues/79417) that doc test coverage - generates incorrect source line numbers in `llvm-cov show` results. - -## `-Z instrument-coverage=` - -- `-Z instrument-coverage=all`: Instrument all functions, including unused functions and unused generics. (This is the same as `-Z instrument-coverage`, with no value.) -- `-Z instrument-coverage=except-unused-generics`: Instrument all functions except unused generics. -- `-Z instrument-coverage=except-unused-functions`: Instrument only used (called) functions and instantiated generic functions. -- `-Z instrument-coverage=off`: Do not instrument any functions. (This is the same as simply not including the `-Z instrument-coverage` option.) - -## Other references - -Rust's implementation and workflow for source-based code coverage is based on the same library and tools used to implement [source-based code coverage in Clang]. (This document is partially based on the Clang guide.) - -[source-based code coverage in clang]: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html -[`json5format`]: https://crates.io/crates/json5format diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/location-detail.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/location-detail.md deleted file mode 100644 index 08d937cc2820..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/location-detail.md +++ /dev/null @@ -1,43 +0,0 @@ -# `location-detail` - -The tracking issue for this feature is: [#70580](https://github.com/rust-lang/rust/issues/70580). - ------------------------- - -Option `-Z location-detail=val` controls what location details are tracked when -using `caller_location`. This allows users to control what location details -are printed as part of panic messages, by allowing them to exclude any combination -of filenames, line numbers, and column numbers. This option is intended to provide -users with a way to mitigate the size impact of `#[track_caller]`. - -This option supports a comma separated list of location details to be included. Valid options -within this list are: - -- `file` - the filename of the panic will be included in the panic output -- `line` - the source line of the panic will be included in the panic output -- `column` - the source column of the panic will be included in the panic output - -Any combination of these three options are supported. If this option is not specified, -all three are included by default. - -An example of a panic output when using `-Z location-detail=line`: -```text -panicked at 'Process blink had a fault', :323:0 -``` - -The code size savings from this option are two-fold. First, the `&'static str` values -for each path to a file containing a panic are removed from the binary. For projects -with deep directory structures and many files with panics, this can add up. This category -of savings can only be realized by excluding filenames from the panic output. Second, -savings can be realized by allowing multiple panics to be fused into a single panicking -branch. It is often the case that within a single file, multiple panics with the same -panic message exist -- e.g. two calls to `Option::unwrap()` in a single line, or -two calls to `Result::expect()` on adjacent lines. If column and line information -are included in the `Location` struct passed to the panic handler, these branches cannot -be fused, as the output is different depending on which panic occurs. However if line -and column information is identical for all panics, these branches can be fused, which -can lead to substantial code size savings, especially for small embedded binaries with -many panics. - -The savings from this option are amplified when combined with the use of `-Zbuild-std`, as -otherwise paths for panics within the standard library are still included in your binary. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/move-size-limit.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/move-size-limit.md deleted file mode 100644 index 88f022af2ecf..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/move-size-limit.md +++ /dev/null @@ -1,10 +0,0 @@ -# `move_size_limit` - --------------------- - -The `-Zmove-size-limit=N` compiler flag enables `large_assignments` lints which -will warn when moving objects whose size exceeds `N` bytes. - -Lint warns only about moves in functions that participate in code generation. -Consequently it will be ineffective for compiler invocatation that emit -metadata only, i.e., `cargo check` like workflows. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/no-unique-section-names.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/no-unique-section-names.md deleted file mode 100644 index 5c1c7cda7013..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/no-unique-section-names.md +++ /dev/null @@ -1,9 +0,0 @@ -# `no-unique-section-names` - ------------------------- - -This flag currently applies only to ELF-based targets using the LLVM codegen backend. It prevents the generation of unique ELF section names for each separate code and data item when `-Z function-sections` is also in use, which is the default for most targets. This option can reduce the size of object files, and depending on the linker, the final ELF binary as well. - -For example, a function `func` will by default generate a code section called `.text.func`. Normally this is fine because the linker will merge all those `.text.*` sections into a single one in the binary. However, starting with [LLVM 12](https://github.com/llvm/llvm-project/commit/ee5d1a04), the backend will also generate unique section names for exception handling, so you would see a section name of `.gcc_except_table.func` in the object file and potentially in the final ELF binary, which could add significant bloat to programs that contain many functions. - -This flag instructs LLVM to use the same `.text` and `.gcc_except_table` section name for each function, and it is analogous to Clang's `-fno-unique-section-names` option. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/profile.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/profile.md deleted file mode 100644 index 71303bfaff20..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/profile.md +++ /dev/null @@ -1,27 +0,0 @@ -# `profile` - -The tracking issue for this feature is: [#42524](https://github.com/rust-lang/rust/issues/42524). - ------------------------- - -This feature allows the generation of code coverage reports. - -Set the `-Zprofile` compiler flag in order to enable gcov profiling. - -For example: -```Bash -cargo new testgcov --bin -cd testgcov -export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" -export CARGO_INCREMENTAL=0 -cargo build -cargo run -``` - -Once you've built and run your program, files with the `gcno` (after build) and `gcda` (after execution) extensions will be created. -You can parse them with [llvm-cov gcov](https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-gcov) or [grcov](https://github.com/mozilla/grcov). - -Please note that `RUSTFLAGS` by default applies to everything that cargo builds and runs during a build! -When the `--target` flag is explicitly passed to cargo, the `RUSTFLAGS` no longer apply to build scripts and procedural macros. -For more fine-grained control consider passing a `RUSTC_WRAPPER` program to cargo that only adds the profiling flags to -rustc for the specific crates you want to profile. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/profile_sample_use.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/profile_sample_use.md deleted file mode 100644 index ce894ce6ac7f..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/profile_sample_use.md +++ /dev/null @@ -1,10 +0,0 @@ -# `profile-sample-use - ---- - -`-Zprofile-sample-use=code.prof` directs `rustc` to use the profile -`code.prof` as a source for Automatic Feedback Directed Optimization (AFDO). -See the documentation of [`-Zdebug-info-for-profiling`] for more information -on using AFDO. - -[`-Zdebug-info-for-profiling`]: debug_info_for_profiling.html diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md deleted file mode 100644 index 977d258529f8..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md +++ /dev/null @@ -1,24 +0,0 @@ -# `remap-cwd-prefix` - -The tracking issue for this feature is: [#87325](https://github.com/rust-lang/rust/issues/87325). - ------------------------- - -This flag will rewrite absolute paths under the current working directory, -replacing the current working directory prefix with a specified value. - -The given value may be absolute or relative, or empty. This switch takes -precidence over `--remap-path-prefix` in case they would both match a given -path. - -This flag helps to produce deterministic output, by removing the current working -directory from build output, while allowing the command line to be universally -reproducible, such that the same execution will work on all machines, regardless -of build environment. - -## Example -```sh -# This would produce an absolute path to main.rs in build outputs of -# "./main.rs". -rustc -Z remap-cwd-prefix=. main.rs -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/report-time.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/report-time.md deleted file mode 100644 index ac0093f77aec..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/report-time.md +++ /dev/null @@ -1,80 +0,0 @@ -# `report-time` - -The tracking issue for this feature is: [#64888] - -[#64888]: https://github.com/rust-lang/rust/issues/64888 - ------------------------- - -The `report-time` feature adds a possibility to report execution time of the -tests generated via `libtest`. - -This is unstable feature, so you have to provide `-Zunstable-options` to get -this feature working. - -Sample usage command: - -```sh -./test_executable -Zunstable-options --report-time -``` - -Available options: - -```sh ---report-time [plain|colored] - Show execution time of each test. Available values: - plain = do not colorize the execution time (default); - colored = colorize output according to the `color` - parameter value; - Threshold values for colorized output can be - configured via - `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` - and - `RUST_TEST_TIME_DOCTEST` environment variables. - Expected format of environment variable is - `VARIABLE=WARN_TIME,CRITICAL_TIME`. - Not available for --format=terse ---ensure-time - Treat excess of the test execution time limit as - error. - Threshold values for this option can be configured via - `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` - and - `RUST_TEST_TIME_DOCTEST` environment variables. - Expected format of environment variable is - `VARIABLE=WARN_TIME,CRITICAL_TIME`. - `CRITICAL_TIME` here means the limit that should not be - exceeded by test. -``` - -Example of the environment variable format: - -```sh -RUST_TEST_TIME_UNIT=100,200 -``` - -where 100 stands for warn time, and 200 stands for critical time. - -## Examples - -```sh -cargo test --tests -- -Zunstable-options --report-time - Finished dev [unoptimized + debuginfo] target(s) in 0.02s - Running target/debug/deps/example-27fb188025bec02c - -running 3 tests -test tests::unit_test_quick ... ok <0.000s> -test tests::unit_test_warn ... ok <0.055s> -test tests::unit_test_critical ... ok <0.110s> - -test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out - - Running target/debug/deps/tests-cedb06f6526d15d9 - -running 3 tests -test unit_test_quick ... ok <0.000s> -test unit_test_warn ... ok <0.550s> -test unit_test_critical ... ok <1.100s> - -test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/sanitizer.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/sanitizer.md deleted file mode 100644 index d630f4ecb7b2..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/sanitizer.md +++ /dev/null @@ -1,594 +0,0 @@ -# `sanitizer` - -The tracking issues for this feature are: - -* [#39699](https://github.com/rust-lang/rust/issues/39699). -* [#89653](https://github.com/rust-lang/rust/issues/89653). - ------------------------- - -This feature allows for use of one of following sanitizers: - -* [AddressSanitizer][clang-asan] a fast memory error detector. -* [ControlFlowIntegrity][clang-cfi] LLVM Control Flow Integrity (CFI) provides - forward-edge control flow protection. -* [HWAddressSanitizer][clang-hwasan] a memory error detector similar to - AddressSanitizer, but based on partial hardware assistance. -* [LeakSanitizer][clang-lsan] a run-time memory leak detector. -* [MemorySanitizer][clang-msan] a detector of uninitialized reads. -* [ThreadSanitizer][clang-tsan] a fast data race detector. - -To enable a sanitizer compile with `-Zsanitizer=address`,`-Zsanitizer=cfi`, -`-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory` or -`-Zsanitizer=thread`. - -# AddressSanitizer - -AddressSanitizer is a memory error detector. It can detect the following types -of bugs: - -* Out of bound accesses to heap, stack and globals -* Use after free -* Use after return (runtime flag `ASAN_OPTIONS=detect_stack_use_after_return=1`) -* Use after scope -* Double-free, invalid free -* Memory leaks - -The memory leak detection is enabled by default on Linux, and can be enabled -with runtime flag `ASAN_OPTIONS=detect_leaks=1` on macOS. - -AddressSanitizer is supported on the following targets: - -* `aarch64-apple-darwin` -* `aarch64-fuchsia` -* `aarch64-unknown-linux-gnu` -* `x86_64-apple-darwin` -* `x86_64-fuchsia` -* `x86_64-unknown-freebsd` -* `x86_64-unknown-linux-gnu` - -AddressSanitizer works with non-instrumented code although it will impede its -ability to detect some bugs. It is not expected to produce false positive -reports. - -## Examples - -Stack buffer overflow: - -```rust -fn main() { - let xs = [0, 1, 2, 3]; - let _y = unsafe { *xs.as_ptr().offset(4) }; -} -``` - -```shell -$ export RUSTFLAGS=-Zsanitizer=address RUSTDOCFLAGS=-Zsanitizer=address -$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu -==37882==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe400e6250 at pc 0x5609a841fb20 bp 0x7ffe400e6210 sp 0x7ffe400e6208 -READ of size 4 at 0x7ffe400e6250 thread T0 - #0 0x5609a841fb1f in example::main::h628ffc6626ed85b2 /.../src/main.rs:3:23 - ... - -Address 0x7ffe400e6250 is located in stack of thread T0 at offset 48 in frame - #0 0x5609a841f8af in example::main::h628ffc6626ed85b2 /.../src/main.rs:1 - - This frame has 1 object(s): - [32, 48) 'xs' (line 2) <== Memory access at offset 48 overflows this variable -HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork - (longjmp and C++ exceptions *are* supported) -SUMMARY: AddressSanitizer: stack-buffer-overflow /.../src/main.rs:3:23 in example::main::h628ffc6626ed85b2 -Shadow bytes around the buggy address: - 0x100048014bf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x100048014c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x100048014c10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x100048014c20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x100048014c30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -=>0x100048014c40: 00 00 00 00 f1 f1 f1 f1 00 00[f3]f3 00 00 00 00 - 0x100048014c50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x100048014c60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x100048014c70: f1 f1 f1 f1 00 00 f3 f3 00 00 00 00 00 00 00 00 - 0x100048014c80: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 - 0x100048014c90: 00 00 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 -Shadow byte legend (one shadow byte represents 8 application bytes): - Addressable: 00 - Partially addressable: 01 02 03 04 05 06 07 - Heap left redzone: fa - Freed heap region: fd - Stack left redzone: f1 - Stack mid redzone: f2 - Stack right redzone: f3 - Stack after return: f5 - Stack use after scope: f8 - Global redzone: f9 - Global init order: f6 - Poisoned by user: f7 - Container overflow: fc - Array cookie: ac - Intra object redzone: bb - ASan internal: fe - Left alloca redzone: ca - Right alloca redzone: cb - Shadow gap: cc -==37882==ABORTING -``` - -Use of a stack object after its scope has already ended: - -```rust -static mut P: *mut usize = std::ptr::null_mut(); - -fn main() { - unsafe { - { - let mut x = 0; - P = &mut x; - } - std::ptr::write_volatile(P, 123); - } -} -``` - -```shell -$ export RUSTFLAGS=-Zsanitizer=address RUSTDOCFLAGS=-Zsanitizer=address -$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu -================================================================= -==39249==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffc7ed3e1a0 at pc 0x55c98b262a8e bp 0x7ffc7ed3e050 sp 0x7ffc7ed3e048 -WRITE of size 8 at 0x7ffc7ed3e1a0 thread T0 - #0 0x55c98b262a8d in core::ptr::write_volatile::he21f1df5a82f329a /.../src/rust/src/libcore/ptr/mod.rs:1048:5 - #1 0x55c98b262cd2 in example::main::h628ffc6626ed85b2 /.../src/main.rs:9:9 - ... - -Address 0x7ffc7ed3e1a0 is located in stack of thread T0 at offset 32 in frame - #0 0x55c98b262bdf in example::main::h628ffc6626ed85b2 /.../src/main.rs:3 - - This frame has 1 object(s): - [32, 40) 'x' (line 6) <== Memory access at offset 32 is inside this variable -HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork - (longjmp and C++ exceptions *are* supported) -SUMMARY: AddressSanitizer: stack-use-after-scope /.../src/rust/src/libcore/ptr/mod.rs:1048:5 in core::ptr::write_volatile::he21f1df5a82f329a -Shadow bytes around the buggy address: - 0x10000fd9fbe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10000fd9fbf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10000fd9fc00: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 - 0x10000fd9fc10: f8 f8 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10000fd9fc20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -=>0x10000fd9fc30: f1 f1 f1 f1[f8]f3 f3 f3 00 00 00 00 00 00 00 00 - 0x10000fd9fc40: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 - 0x10000fd9fc50: 00 00 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10000fd9fc60: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 f3 f3 - 0x10000fd9fc70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0x10000fd9fc80: 00 00 00 00 f1 f1 f1 f1 00 00 f3 f3 00 00 00 00 -Shadow byte legend (one shadow byte represents 8 application bytes): - Addressable: 00 - Partially addressable: 01 02 03 04 05 06 07 - Heap left redzone: fa - Freed heap region: fd - Stack left redzone: f1 - Stack mid redzone: f2 - Stack right redzone: f3 - Stack after return: f5 - Stack use after scope: f8 - Global redzone: f9 - Global init order: f6 - Poisoned by user: f7 - Container overflow: fc - Array cookie: ac - Intra object redzone: bb - ASan internal: fe - Left alloca redzone: ca - Right alloca redzone: cb - Shadow gap: cc -==39249==ABORTING -``` - -# ControlFlowIntegrity - -The LLVM Control Flow Integrity (CFI) support in the Rust compiler initially -provides forward-edge control flow protection for Rust-compiled code only by -aggregating function pointers in groups identified by their number of arguments. - -Forward-edge control flow protection for C or C++ and Rust -compiled code "mixed -binaries" (i.e., for when C or C++ and Rust -compiled code share the same -virtual address space) will be provided in later work by defining and using -compatible type identifiers (see Type metadata in the design document in the -tracking issue [#89653](https://github.com/rust-lang/rust/issues/89653)). - -LLVM CFI can be enabled with -Zsanitizer=cfi and requires LTO (i.e., -Clto). - -## Example - -```text -#![feature(naked_functions)] - -use std::arch::asm; -use std::mem; - -fn add_one(x: i32) -> i32 { - x + 1 -} - -#[naked] -pub extern "C" fn add_two(x: i32) { - // x + 2 preceeded by a landing pad/nop block - unsafe { - asm!( - " - nop - nop - nop - nop - nop - nop - nop - nop - nop - lea rax, [rdi+2] - ret - ", - options(noreturn) - ); - } -} - -fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { - f(arg) + f(arg) -} - -fn main() { - let answer = do_twice(add_one, 5); - - println!("The answer is: {}", answer); - - println!("With CFI enabled, you should not see the next answer"); - let f: fn(i32) -> i32 = unsafe { - // Offsets 0-8 make it land in the landing pad/nop block, and offsets 1-8 are - // invalid branch/call destinations (i.e., within the body of the function). - mem::transmute::<*const u8, fn(i32) -> i32>((add_two as *const u8).offset(5)) - }; - let next_answer = do_twice(f, 5); - - println!("The next answer is: {}", next_answer); -} -``` -Fig. 1. Modified example from the [Advanced Functions and -Closures][rust-book-ch19-05] chapter of the [The Rust Programming -Language][rust-book] book. - -[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged) - -```shell -$ rustc rust_cfi.rs -o rust_cfi -$ ./rust_cfi -The answer is: 12 -With CFI enabled, you should not see the next answer -The next answer is: 14 -$ -``` -Fig. 2. Build and execution of the modified example with LLVM CFI disabled. - -[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged) - -```shell -$ rustc -Clto -Zsanitizer=cfi rust_cfi.rs -o rust_cfi -$ ./rust_cfi -The answer is: 12 -With CFI enabled, you should not see the next answer -Illegal instruction -$ -``` -Fig. 3. Build and execution of the modified example with LLVM CFI enabled. - -When LLVM CFI is enabled, if there are any attempts to change/hijack control -flow using an indirect branch/call to an invalid destination, the execution is -terminated (see Fig. 3). - -```rust -use std::mem; - -fn add_one(x: i32) -> i32 { - x + 1 -} - -fn add_two(x: i32, _y: i32) -> i32 { - x + 2 -} - -fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { - f(arg) + f(arg) -} - -fn main() { - let answer = do_twice(add_one, 5); - - println!("The answer is: {}", answer); - - println!("With CFI enabled, you should not see the next answer"); - let f: fn(i32) -> i32 = - unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) }; - let next_answer = do_twice(f, 5); - - println!("The next answer is: {}", next_answer); -} -``` -Fig. 4. Another modified example from the [Advanced Functions and -Closures][rust-book-ch19-05] chapter of the [The Rust Programming -Language][rust-book] book. - -[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged) - -```shell -$ rustc rust_cfi.rs -o rust_cfi -$ ./rust_cfi -The answer is: 12 -With CFI enabled, you should not see the next answer -The next answer is: 14 -$ -``` -Fig. 5. Build and execution of the modified example with LLVM CFI disabled. - -[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged) - -```shell -$ rustc -Clto -Zsanitizer=cfi rust_cfi.rs -o rust_cfi -$ ./rust_cfi -The answer is: 12 -With CFI enabled, you should not see the next answer -Illegal instruction -$ -``` -Fig. 6. Build and execution of the modified example with LLVM CFI enabled. - -When LLVM CFI is enabled, if there are any attempts to change/hijack control -flow using an indirect branch/call to a function with different number of -arguments than intended/passed in the call/branch site, the execution is also -terminated (see Fig. 6). - -Forward-edge control flow protection not only by aggregating function pointers -in groups identified by their number of arguments, but also their argument -types, will also be provided in later work by defining and using compatible type -identifiers (see Type metadata in the design document in the tracking -issue [#89653](https://github.com/rust-lang/rust/issues/89653)). - -[rust-book-ch19-05]: https://doc.rust-lang.org/book/ch19-05-advanced-functions-and-closures.html -[rust-book]: https://doc.rust-lang.org/book/title-page.html - -# HWAddressSanitizer - -HWAddressSanitizer is a newer variant of AddressSanitizer that consumes much -less memory. - -HWAddressSanitizer is supported on the following targets: - -* `aarch64-linux-android` -* `aarch64-unknown-linux-gnu` - -HWAddressSanitizer requires `tagged-globals` target feature to instrument -globals. To enable this target feature compile with `-C -target-feature=+tagged-globals` - -## Example - -Heap buffer overflow: - -```rust -fn main() { - let xs = vec![0, 1, 2, 3]; - let _y = unsafe { *xs.as_ptr().offset(4) }; -} -``` - -```shell -$ rustc main.rs -Zsanitizer=hwaddress -C target-feature=+tagged-globals -C -linker=aarch64-linux-gnu-gcc -C link-arg=-fuse-ld=lld --target -aarch64-unknown-linux-gnu -``` - -```shell -$ ./main -==241==ERROR: HWAddressSanitizer: tag-mismatch on address 0xefdeffff0050 at pc 0xaaaae0ae4a98 -READ of size 4 at 0xefdeffff0050 tags: 2c/00 (ptr/mem) in thread T0 - #0 0xaaaae0ae4a94 (/.../main+0x54a94) - ... - -[0xefdeffff0040,0xefdeffff0060) is a small allocated heap chunk; size: 32 offset: 16 -0xefdeffff0050 is located 0 bytes to the right of 16-byte region [0xefdeffff0040,0xefdeffff0050) -allocated here: - #0 0xaaaae0acb80c (/.../main+0x3b80c) - ... - -Thread: T0 0xeffe00002000 stack: [0xffffc28ad000,0xffffc30ad000) sz: 8388608 tls: [0xffffaa10a020,0xffffaa10a7d0) -Memory tags around the buggy address (one tag corresponds to 16 bytes): - 0xfefcefffef80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0xfefcefffef90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0xfefcefffefa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0xfefcefffefb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0xfefcefffefc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0xfefcefffefd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0xfefcefffefe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0xfefcefffeff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -=>0xfefceffff000: d7 d7 05 00 2c [00] 00 00 00 00 00 00 00 00 00 00 - 0xfefceffff010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0xfefceffff020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0xfefceffff030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0xfefceffff040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0xfefceffff050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0xfefceffff060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0xfefceffff070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 0xfefceffff080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Tags for short granules around the buggy address (one tag corresponds to 16 bytes): - 0xfefcefffeff0: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. -=>0xfefceffff000: .. .. 8c .. .. [..] .. .. .. .. .. .. .. .. .. .. - 0xfefceffff010: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. -See https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#short-granules for a description of short granule tags -Registers where the failure occurred (pc 0xaaaae0ae4a98): - x0 2c00efdeffff0050 x1 0000000000000004 x2 0000000000000004 x3 0000000000000000 - x4 0000fffefc30ac37 x5 000000000000005d x6 00000ffffc30ac37 x7 0000efff00000000 - x8 2c00efdeffff0050 x9 0200efff00000000 x10 0000000000000000 x11 0200efff00000000 - x12 0200effe00000310 x13 0200effe00000310 x14 0000000000000008 x15 5d00ffffc30ac360 - x16 0000aaaae0ad062c x17 0000000000000003 x18 0000000000000001 x19 0000ffffc30ac658 - x20 4e00ffffc30ac6e0 x21 0000aaaae0ac5e10 x22 0000000000000000 x23 0000000000000000 - x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000 - x28 0000000000000000 x29 0000ffffc30ac5a0 x30 0000aaaae0ae4a98 -SUMMARY: HWAddressSanitizer: tag-mismatch (/.../main+0x54a94) -``` - -# LeakSanitizer - -LeakSanitizer is run-time memory leak detector. - -LeakSanitizer is supported on the following targets: - -* `aarch64-apple-darwin` -* `aarch64-unknown-linux-gnu` -* `x86_64-apple-darwin` -* `x86_64-unknown-linux-gnu` - -# MemorySanitizer - -MemorySanitizer is detector of uninitialized reads. - -MemorySanitizer is supported on the following targets: - -* `aarch64-unknown-linux-gnu` -* `x86_64-unknown-freebsd` -* `x86_64-unknown-linux-gnu` - -MemorySanitizer requires all program code to be instrumented. C/C++ dependencies -need to be recompiled using Clang with `-fsanitize=memory` option. Failing to -achieve that will result in false positive reports. - -## Example - -Detecting the use of uninitialized memory. The `-Zbuild-std` flag rebuilds and -instruments the standard library, and is strictly necessary for the correct -operation of the tool. The `-Zsanitizer-memory-track-origins` enables tracking -of the origins of uninitialized memory: - -```rust -use std::mem::MaybeUninit; - -fn main() { - unsafe { - let a = MaybeUninit::<[usize; 4]>::uninit(); - let a = a.assume_init(); - println!("{}", a[2]); - } -} -``` - -```shell -$ export \ - RUSTFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins' \ - RUSTDOCFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins' -$ cargo clean -$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu -==9416==WARNING: MemorySanitizer: use-of-uninitialized-value - #0 0x560c04f7488a in core::fmt::num::imp::fmt_u64::haa293b0b098501ca $RUST/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/src/rust/src/libcore/fmt/num.rs:202:16 -... - Uninitialized value was stored to memory at - #0 0x560c04ae898a in __msan_memcpy.part.0 $RUST/src/llvm-project/compiler-rt/lib/msan/msan_interceptors.cc:1558:3 - #1 0x560c04b2bf88 in memory::main::hd2333c1899d997f5 $CWD/src/main.rs:6:16 - - Uninitialized value was created by an allocation of 'a' in the stack frame of function '_ZN6memory4main17hd2333c1899d997f5E' - #0 0x560c04b2bc50 in memory::main::hd2333c1899d997f5 $CWD/src/main.rs:3 -``` - -# ThreadSanitizer - -ThreadSanitizer is a data race detection tool. It is supported on the following -targets: - -* `aarch64-apple-darwin` -* `aarch64-unknown-linux-gnu` -* `x86_64-apple-darwin` -* `x86_64-unknown-freebsd` -* `x86_64-unknown-linux-gnu` - -To work correctly ThreadSanitizer needs to be "aware" of all synchronization -operations in a program. It generally achieves that through combination of -library interception (for example synchronization performed through -`pthread_mutex_lock` / `pthread_mutex_unlock`) and compile time instrumentation -(e.g. atomic operations). Using it without instrumenting all the program code -can lead to false positive reports. - -ThreadSanitizer does not support atomic fences `std::sync::atomic::fence`, -nor synchronization performed using inline assembly code. - -## Example - -```rust -static mut A: usize = 0; - -fn main() { - let t = std::thread::spawn(|| { - unsafe { A += 1 }; - }); - unsafe { A += 1 }; - - t.join().unwrap(); -} -``` - -```shell -$ export RUSTFLAGS=-Zsanitizer=thread RUSTDOCFLAGS=-Zsanitizer=thread -$ cargo run -Zbuild-std --target x86_64-unknown-linux-gnu -================== -WARNING: ThreadSanitizer: data race (pid=10574) - Read of size 8 at 0x5632dfe3d030 by thread T1: - #0 example::main::_$u7b$$u7b$closure$u7d$$u7d$::h23f64b0b2f8c9484 ../src/main.rs:5:18 (example+0x86cec) - ... - - Previous write of size 8 at 0x5632dfe3d030 by main thread: - #0 example::main::h628ffc6626ed85b2 /.../src/main.rs:7:14 (example+0x868c8) - ... - #11 main (example+0x86a1a) - - Location is global 'example::A::h43ac149ddf992709' of size 8 at 0x5632dfe3d030 (example+0x000000bd9030) -``` - -# Instrumentation of external dependencies and std - -The sanitizers to varying degrees work correctly with partially instrumented -code. On the one extreme is LeakSanitizer that doesn't use any compile time -instrumentation, on the other is MemorySanitizer that requires that all program -code to be instrumented (failing to achieve that will inevitably result in -false positives). - -It is strongly recommended to combine sanitizers with recompiled and -instrumented standard library, for example using [cargo `-Zbuild-std` -functionality][build-std]. - -[build-std]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std - -# Build scripts and procedural macros - -Use of sanitizers together with build scripts and procedural macros is -technically possible, but in almost all cases it would be best avoided. This -is especially true for procedural macros which would require an instrumented -version of rustc. - -In more practical terms when using cargo always remember to pass `--target` -flag, so that rustflags will not be applied to build scripts and procedural -macros. - -# Symbolizing the Reports - -Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PATH`. - -# Additional Information - -* [Sanitizers project page](https://github.com/google/sanitizers/wiki/) -* [AddressSanitizer in Clang][clang-asan] -* [ControlFlowIntegrity in Clang][clang-cfi] -* [HWAddressSanitizer in Clang][clang-hwasan] -* [LeakSanitizer in Clang][clang-lsan] -* [MemorySanitizer in Clang][clang-msan] -* [ThreadSanitizer in Clang][clang-tsan] - -[clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html -[clang-cfi]: https://clang.llvm.org/docs/ControlFlowIntegrity.html -[clang-hwasan]: https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html -[clang-lsan]: https://clang.llvm.org/docs/LeakSanitizer.html -[clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html -[clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/self-profile-events.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/self-profile-events.md deleted file mode 100644 index 3ce18743be50..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/self-profile-events.md +++ /dev/null @@ -1,74 +0,0 @@ -# `self-profile-events` - ---------------------- - -The `-Zself-profile-events` compiler flag controls what events are recorded by the self-profiler when it is enabled via the `-Zself-profile` flag. - -This flag takes a comma delimited list of event types to record. - -For example: - -```console -$ rustc -Zself-profile -Zself-profile-events=default,args -``` - -## Event types - -- `query-provider` - - Traces each query used internally by the compiler. - -- `generic-activity` - - Traces other parts of the compiler not covered by the query system. - -- `query-cache-hit` - - Adds tracing information that records when the in-memory query cache is "hit" and does not need to re-execute a query which has been cached. - - Disabled by default because this significantly increases the trace file size. - -- `query-blocked` - - Tracks time that a query tries to run but is blocked waiting on another thread executing the same query to finish executing. - - Query blocking only occurs when the compiler is built with parallel mode support. - -- `incr-cache-load` - - Tracks time that is spent loading and deserializing query results from the incremental compilation on-disk cache. - -- `query-keys` - - Adds a serialized representation of each query's query key to the tracing data. - - Disabled by default because this significantly increases the trace file size. - -- `function-args` - - Adds additional tracing data to some `generic-activity` events. - - Disabled by default for parity with `query-keys`. - -- `llvm` - - Adds tracing information about LLVM passes and codegeneration. - - Disabled by default because this only works when `-Znew-llvm-pass-manager` is enabled. - -## Event synonyms - -- `none` - - Disables all events. - Equivalent to the self-profiler being disabled. - -- `default` - - The default set of events which stikes a balance between providing detailed tracing data and adding additional overhead to the compilation. - -- `args` - - Equivalent to `query-keys` and `function-args`. - -- `all` - - Enables all events. - -## Examples - -Enable the profiler and capture the default set of events (both invocations are equivalent): - -```console -$ rustc -Zself-profile -$ rustc -Zself-profile -Zself-profile-events=default -``` - -Enable the profiler and capture the default events and their arguments: - -```console -$ rustc -Zself-profile -Zself-profile-events=default,args -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/self-profile.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/self-profile.md deleted file mode 100644 index 7305141a4271..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/self-profile.md +++ /dev/null @@ -1,47 +0,0 @@ -# `self-profile` - --------------------- - -The `-Zself-profile` compiler flag enables rustc's internal profiler. -When enabled, the compiler will output three binary files in the specified directory (or the current working directory if no directory is specified). -These files can be analyzed by using the tools in the [`measureme`] repository. - -To control the data recorded in the trace files, use the `-Zself-profile-events` flag. - -For example: - -First, run a compilation session and provide the `-Zself-profile` flag: - -```console -$ rustc --crate-name foo -Zself-profile -``` - -This will generate three files in the working directory such as: - -- `foo-1234.events` -- `foo-1234.string_data` -- `foo-1234.string_index` - -Where `foo` is the name of the crate and `1234` is the process id of the rustc process. - -To get a summary of where the compiler is spending its time: - -```console -$ ../measureme/target/release/summarize summarize foo-1234 -``` - -To generate a flamegraph of the same data: - -```console -$ ../measureme/target/release/inferno foo-1234 -``` - -To dump the event data in a Chromium-profiler compatible format: - -```console -$ ../measureme/target/release/crox foo-1234 -``` - -For more information, consult the [`measureme`] documentation. - -[`measureme`]: https://github.com/rust-lang/measureme.git diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/source-based-code-coverage.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/source-based-code-coverage.md deleted file mode 100644 index cb65978e0a07..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/source-based-code-coverage.md +++ /dev/null @@ -1,5 +0,0 @@ -# `source-based-code-coverage` - -See compiler flag [`-Z instrument-coverage`]. - -[`-z instrument-coverage`]: ./instrument-coverage.html diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/src-hash-algorithm.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/src-hash-algorithm.md deleted file mode 100644 index ff776741b212..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/src-hash-algorithm.md +++ /dev/null @@ -1,11 +0,0 @@ -# `src-hash-algorithm` - -The tracking issue for this feature is: [#70401](https://github.com/rust-lang/rust/issues/70401). - ------------------------- - -The `-Z src-hash-algorithm` compiler flag controls which algorithm is used when hashing each source file. The hash is stored in the debug info and can be used by a debugger to verify the source code matches the executable. - -Supported hash algorithms are: `md5`, `sha1`, and `sha256`. Note that not all hash algorithms are supported by all debug info formats. - -By default, the compiler chooses the hash algorithm based on the target specification. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/temps-dir.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/temps-dir.md deleted file mode 100644 index e25011f71197..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/temps-dir.md +++ /dev/null @@ -1,10 +0,0 @@ -# `temps-dir` - --------------------- - -The `-Ztemps-dir` compiler flag specifies the directory to write the -intermediate files in. If not set, the output directory is used. This option is -useful if you are running more than one instance of `rustc` (e.g. with different -`--crate-type` settings), and you need to make sure they are not overwriting -each other's intermediate files. No files are kept unless `-C save-temps=yes` is -also set. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/tls-model.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/tls-model.md deleted file mode 100644 index 8b19e785c6a5..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/tls-model.md +++ /dev/null @@ -1,25 +0,0 @@ -# `tls_model` - -The tracking issue for this feature is: None. - ------------------------- - -Option `-Z tls-model` controls [TLS model](https://www.akkadia.org/drepper/tls.pdf) used to -generate code for accessing `#[thread_local]` `static` items. - -Supported values for this option are: - -- `global-dynamic` - General Dynamic TLS Model (alternatively called Global Dynamic) is the most -general option usable in all circumstances, even if the TLS data is defined in a shared library -loaded at runtime and is accessed from code outside of that library. -This is the default for most targets. -- `local-dynamic` - model usable if the TLS data is only accessed from the shared library or -executable it is defined in. The TLS data may be in a library loaded after startup (via `dlopen`). -- `initial-exec` - model usable if the TLS data is defined in the executable or in a shared library -loaded at program startup. -The TLS data must not be in a library loaded after startup (via `dlopen`). -- `local-exec` - model usable only if the TLS data is defined directly in the executable, -but not in a shared library, and is accessed only from that executable. - -`rustc` and LLVM may use a more optimized model than specified if they know that we are producing -an executable rather than a library, or that the `static` item is private enough. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/unsound-mir-opts.md b/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/unsound-mir-opts.md deleted file mode 100644 index 8e46e227c25b..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/compiler-flags/unsound-mir-opts.md +++ /dev/null @@ -1,8 +0,0 @@ -# `unsound-mir-opts` - --------------------- - -The `-Zunsound-mir-opts` compiler flag enables [MIR optimization passes] which can cause unsound behavior. -This flag should only be used by MIR optimization tests in the rustc test suite. - -[MIR optimization passes]: https://rustc-dev-guide.rust-lang.org/mir/optimizations.html diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features.md deleted file mode 100644 index a27514df97d6..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features.md +++ /dev/null @@ -1 +0,0 @@ -# Language features diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-c-cmse-nonsecure-call.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-c-cmse-nonsecure-call.md deleted file mode 100644 index 79a177cb28b1..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-c-cmse-nonsecure-call.md +++ /dev/null @@ -1,88 +0,0 @@ -# `abi_c_cmse_nonsecure_call` - -The tracking issue for this feature is: [#81391] - -[#81391]: https://github.com/rust-lang/rust/issues/81391 - ------------------------- - -The [TrustZone-M -feature](https://developer.arm.com/documentation/100690/latest/) is available -for targets with the Armv8-M architecture profile (`thumbv8m` in their target -name). -LLVM, the Rust compiler and the linker are providing -[support](https://developer.arm.com/documentation/ecm0359818/latest/) for the -TrustZone-M feature. - -One of the things provided, with this unstable feature, is the -`C-cmse-nonsecure-call` function ABI. This ABI is used on function pointers to -non-secure code to mark a non-secure function call (see [section -5.5](https://developer.arm.com/documentation/ecm0359818/latest/) for details). - -With this ABI, the compiler will do the following to perform the call: -* save registers needed after the call to Secure memory -* clear all registers that might contain confidential information -* clear the Least Significant Bit of the function address -* branches using the BLXNS instruction - -To avoid using the non-secure stack, the compiler will constrain the number and -type of parameters/return value. - -The `extern "C-cmse-nonsecure-call"` ABI is otherwise equivalent to the -`extern "C"` ABI. - - - -``` rust,ignore -#![no_std] -#![feature(abi_c_cmse_nonsecure_call)] - -#[no_mangle] -pub fn call_nonsecure_function(addr: usize) -> u32 { - let non_secure_function = - unsafe { core::mem::transmute:: u32>(addr) }; - non_secure_function() -} -``` - -``` text -$ rustc --emit asm --crate-type lib --target thumbv8m.main-none-eabi function.rs - -call_nonsecure_function: - .fnstart - .save {r7, lr} - push {r7, lr} - .setfp r7, sp - mov r7, sp - .pad #16 - sub sp, #16 - str r0, [sp, #12] - ldr r0, [sp, #12] - str r0, [sp, #8] - b .LBB0_1 -.LBB0_1: - ldr r0, [sp, #8] - push.w {r4, r5, r6, r7, r8, r9, r10, r11} - bic r0, r0, #1 - mov r1, r0 - mov r2, r0 - mov r3, r0 - mov r4, r0 - mov r5, r0 - mov r6, r0 - mov r7, r0 - mov r8, r0 - mov r9, r0 - mov r10, r0 - mov r11, r0 - mov r12, r0 - msr apsr_nzcvq, r0 - blxns r0 - pop.w {r4, r5, r6, r7, r8, r9, r10, r11} - str r0, [sp, #4] - b .LBB0_2 -.LBB0_2: - ldr r0, [sp, #4] - add sp, #16 - pop {r7, pc} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-msp430-interrupt.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-msp430-interrupt.md deleted file mode 100644 index b10bc41cb143..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-msp430-interrupt.md +++ /dev/null @@ -1,42 +0,0 @@ -# `abi_msp430_interrupt` - -The tracking issue for this feature is: [#38487] - -[#38487]: https://github.com/rust-lang/rust/issues/38487 - ------------------------- - -In the MSP430 architecture, interrupt handlers have a special calling -convention. You can use the `"msp430-interrupt"` ABI to make the compiler apply -the right calling convention to the interrupt handlers you define. - - - -``` rust,ignore -#![feature(abi_msp430_interrupt)] -#![no_std] - -// Place the interrupt handler at the appropriate memory address -// (Alternatively, you can use `#[used]` and remove `pub` and `#[no_mangle]`) -#[link_section = "__interrupt_vector_10"] -#[no_mangle] -pub static TIM0_VECTOR: extern "msp430-interrupt" fn() = tim0; - -// The interrupt handler -extern "msp430-interrupt" fn tim0() { - // .. -} -``` - -``` text -$ msp430-elf-objdump -CD ./target/msp430/release/app -Disassembly of section __interrupt_vector_10: - -0000fff2 : - fff2: 00 c0 interrupt service routine at 0xc000 - -Disassembly of section .text: - -0000c000 : - c000: 00 13 reti -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-ptx.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-ptx.md deleted file mode 100644 index 0ded3ceeaef2..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-ptx.md +++ /dev/null @@ -1,60 +0,0 @@ -# `abi_ptx` - -The tracking issue for this feature is: [#38788] - -[#38788]: https://github.com/rust-lang/rust/issues/38788 - ------------------------- - -When emitting PTX code, all vanilla Rust functions (`fn`) get translated to -"device" functions. These functions are *not* callable from the host via the -CUDA API so a crate with only device functions is not too useful! - -OTOH, "global" functions *can* be called by the host; you can think of them -as the real public API of your crate. To produce a global function use the -`"ptx-kernel"` ABI. - - - -``` rust,ignore -#![feature(abi_ptx)] -#![no_std] - -pub unsafe extern "ptx-kernel" fn global_function() { - device_function(); -} - -pub fn device_function() { - // .. -} -``` - -``` text -$ xargo rustc --target nvptx64-nvidia-cuda --release -- --emit=asm - -$ cat $(find -name '*.s') -// -// Generated by LLVM NVPTX Back-End -// - -.version 3.2 -.target sm_20 -.address_size 64 - - // .globl _ZN6kernel15global_function17h46111ebe6516b382E - -.visible .entry _ZN6kernel15global_function17h46111ebe6516b382E() -{ - - - ret; -} - - // .globl _ZN6kernel15device_function17hd6a0e4993bbf3f78E -.visible .func _ZN6kernel15device_function17hd6a0e4993bbf3f78E() -{ - - - ret; -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-thiscall.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-thiscall.md deleted file mode 100644 index 73bc6eacf42c..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/abi-thiscall.md +++ /dev/null @@ -1,12 +0,0 @@ -# `abi_thiscall` - -The tracking issue for this feature is: [#42202] - -[#42202]: https://github.com/rust-lang/rust/issues/42202 - ------------------------- - -The MSVC ABI on x86 Windows uses the `thiscall` calling convention for C++ -instance methods by default; it is identical to the usual (C) calling -convention on x86 Windows except that the first parameter of the method, -the `this` pointer, is passed in the ECX register. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/allocator-internals.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/allocator-internals.md deleted file mode 100644 index 2023d758fe3d..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/allocator-internals.md +++ /dev/null @@ -1,7 +0,0 @@ -# `allocator_internals` - -This feature does not have a tracking issue, it is an unstable implementation -detail of the `global_allocator` feature not intended for use outside the -compiler. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/arbitrary-enum-discriminant.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/arbitrary-enum-discriminant.md deleted file mode 100644 index e0bb782270e2..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/arbitrary-enum-discriminant.md +++ /dev/null @@ -1,37 +0,0 @@ -# `arbitrary_enum_discriminant` - -The tracking issue for this feature is: [#60553] - -[#60553]: https://github.com/rust-lang/rust/issues/60553 - ------------------------- - -The `arbitrary_enum_discriminant` feature permits tuple-like and -struct-like enum variants with `#[repr()]` to have explicit discriminants. - -## Examples - -```rust -#![feature(arbitrary_enum_discriminant)] - -#[allow(dead_code)] -#[repr(u8)] -enum Enum { - Unit = 3, - Tuple(u16) = 2, - Struct { - a: u8, - b: u16, - } = 1, -} - -impl Enum { - fn tag(&self) -> u8 { - unsafe { *(self as *const Self as *const u8) } - } -} - -assert_eq!(3, Enum::Unit.tag()); -assert_eq!(2, Enum::Tuple(5).tag()); -assert_eq!(1, Enum::Struct{a: 7, b: 11}.tag()); -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-const.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-const.md deleted file mode 100644 index 1063c23b6dfb..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-const.md +++ /dev/null @@ -1,11 +0,0 @@ -# `asm_const` - -The tracking issue for this feature is: [#72016] - -[#72016]: https://github.com/rust-lang/rust/issues/72016 - ------------------------- - -This feature adds a `const ` operand type to `asm!` and `global_asm!`. -- `` must be an integer constant expression. -- The value of the expression is formatted as a string and substituted directly into the asm template string. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-experimental-arch.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-experimental-arch.md deleted file mode 100644 index ec97eaa8b2b5..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-experimental-arch.md +++ /dev/null @@ -1,117 +0,0 @@ -# `asm_experimental_arch` - -The tracking issue for this feature is: [#72016] - -[#72016]: https://github.com/rust-lang/rust/issues/72016 - ------------------------- - -This feature tracks `asm!` and `global_asm!` support for the following architectures: -- NVPTX -- PowerPC -- Hexagon -- MIPS32r2 and MIPS64r2 -- wasm32 -- BPF -- SPIR-V -- AVR - -## Register classes - -| Architecture | Register class | Registers | LLVM constraint code | -| ------------ | -------------- | ---------------------------------- | -------------------- | -| MIPS | `reg` | `$[2-25]` | `r` | -| MIPS | `freg` | `$f[0-31]` | `f` | -| NVPTX | `reg16` | None\* | `h` | -| NVPTX | `reg32` | None\* | `r` | -| NVPTX | `reg64` | None\* | `l` | -| Hexagon | `reg` | `r[0-28]` | `r` | -| PowerPC | `reg` | `r[0-31]` | `r` | -| PowerPC | `reg_nonzero` | `r[1-31]` | `b` | -| PowerPC | `freg` | `f[0-31]` | `f` | -| PowerPC | `cr` | `cr[0-7]`, `cr` | Only clobbers | -| PowerPC | `xer` | `xer` | Only clobbers | -| wasm32 | `local` | None\* | `r` | -| BPF | `reg` | `r[0-10]` | `r` | -| BPF | `wreg` | `w[0-10]` | `w` | -| AVR | `reg` | `r[2-25]`, `XH`, `XL`, `ZH`, `ZL` | `r` | -| AVR | `reg_upper` | `r[16-25]`, `XH`, `XL`, `ZH`, `ZL` | `d` | -| AVR | `reg_pair` | `r3r2` .. `r25r24`, `X`, `Z` | `r` | -| AVR | `reg_iw` | `r25r24`, `X`, `Z` | `w` | -| AVR | `reg_ptr` | `X`, `Z` | `e` | - -> **Notes**: -> - NVPTX doesn't have a fixed register set, so named registers are not supported. -> -> - WebAssembly doesn't have registers, so named registers are not supported. - -# Register class supported types - -| Architecture | Register class | Target feature | Allowed types | -| ------------ | ------------------------------- | -------------- | --------------------------------------- | -| MIPS32 | `reg` | None | `i8`, `i16`, `i32`, `f32` | -| MIPS32 | `freg` | None | `f32`, `f64` | -| MIPS64 | `reg` | None | `i8`, `i16`, `i32`, `i64`, `f32`, `f64` | -| MIPS64 | `freg` | None | `f32`, `f64` | -| NVPTX | `reg16` | None | `i8`, `i16` | -| NVPTX | `reg32` | None | `i8`, `i16`, `i32`, `f32` | -| NVPTX | `reg64` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | -| Hexagon | `reg` | None | `i8`, `i16`, `i32`, `f32` | -| PowerPC | `reg` | None | `i8`, `i16`, `i32` | -| PowerPC | `reg_nonzero` | None | `i8`, `i16`, `i32` | -| PowerPC | `freg` | None | `f32`, `f64` | -| PowerPC | `cr` | N/A | Only clobbers | -| PowerPC | `xer` | N/A | Only clobbers | -| wasm32 | `local` | None | `i8` `i16` `i32` `i64` `f32` `f64` | -| BPF | `reg` | None | `i8` `i16` `i32` `i64` | -| BPF | `wreg` | `alu32` | `i8` `i16` `i32` | -| AVR | `reg`, `reg_upper` | None | `i8` | -| AVR | `reg_pair`, `reg_iw`, `reg_ptr` | None | `i16` | - -## Register aliases - -| Architecture | Base register | Aliases | -| ------------ | ------------- | --------- | -| Hexagon | `r29` | `sp` | -| Hexagon | `r30` | `fr` | -| Hexagon | `r31` | `lr` | -| BPF | `r[0-10]` | `w[0-10]` | -| AVR | `XH` | `r27` | -| AVR | `XL` | `r26` | -| AVR | `ZH` | `r31` | -| AVR | `ZL` | `r30` | - -## Unsupported registers - -| Architecture | Unsupported register | Reason | -| ------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. | -| All | `fr` (Hexagon), `$fp` (MIPS), `Y` (AVR) | The frame pointer cannot be used as an input or output. | -| All | `r19` (Hexagon) | This is used internally by LLVM as a "base pointer" for functions with complex stack frames. | -| MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. | -| MIPS | `$1` or `$at` | Reserved for assembler. | -| MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. | -| MIPS | `$28`/`$gp` | Global pointer cannot be used as inputs or outputs. | -| MIPS | `$ra` | Return address cannot be used as inputs or outputs. | -| Hexagon | `lr` | This is the link register which cannot be used as an input or output. | -| AVR | `r0`, `r1`, `r1r0` | Due to an issue in LLVM, the `r0` and `r1` registers cannot be used as inputs or outputs. If modified, they must be restored to their original values before the end of the block. | - -## Template modifiers - -| Architecture | Register class | Modifier | Example output | LLVM modifier | -| ------------ | -------------- | -------- | -------------- | ------------- | -| MIPS | `reg` | None | `$2` | None | -| MIPS | `freg` | None | `$f0` | None | -| NVPTX | `reg16` | None | `rs0` | None | -| NVPTX | `reg32` | None | `r0` | None | -| NVPTX | `reg64` | None | `rd0` | None | -| Hexagon | `reg` | None | `r0` | None | -| PowerPC | `reg` | None | `0` | None | -| PowerPC | `reg_nonzero` | None | `3` | `b` | -| PowerPC | `freg` | None | `0` | None | - -# Flags covered by `preserves_flags` - -These flags registers must be restored upon exiting the asm block if the `preserves_flags` option is set: -- AVR - - The status register `SREG`. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-sym.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-sym.md deleted file mode 100644 index 7544e20807e9..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-sym.md +++ /dev/null @@ -1,13 +0,0 @@ -# `asm_sym` - -The tracking issue for this feature is: [#72016] - -[#72016]: https://github.com/rust-lang/rust/issues/72016 - ------------------------- - -This feature adds a `sym ` operand type to `asm!` and `global_asm!`. -- `` must refer to a `fn` or `static`. -- A mangled symbol name referring to the item is substituted into the asm template string. -- The substituted string does not include any modifiers (e.g. GOT, PLT, relocations, etc). -- `` is allowed to point to a `#[thread_local]` static, in which case the asm code can combine the symbol with relocations (e.g. `@plt`, `@TPOFF`) to read from thread-local data. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-unwind.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-unwind.md deleted file mode 100644 index 414193fe8017..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/asm-unwind.md +++ /dev/null @@ -1,9 +0,0 @@ -# `asm_unwind` - -The tracking issue for this feature is: [#72016] - -[#72016]: https://github.com/rust-lang/rust/issues/72016 - ------------------------- - -This feature adds a `may_unwind` option to `asm!` which allows an `asm` block to unwind stack and be part of the stack unwinding process. This option is only supported by the LLVM backend right now. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/auto-traits.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/auto-traits.md deleted file mode 100644 index f967c11fc4d0..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/auto-traits.md +++ /dev/null @@ -1,106 +0,0 @@ -# `auto_traits` - -The tracking issue for this feature is [#13231] - -[#13231]: https://github.com/rust-lang/rust/issues/13231 - ----- - -The `auto_traits` feature gate allows you to define auto traits. - -Auto traits, like [`Send`] or [`Sync`] in the standard library, are marker traits -that are automatically implemented for every type, unless the type, or a type it contains, -has explicitly opted out via a negative impl. (Negative impls are separately controlled -by the `negative_impls` feature.) - -[`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html -[`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html - -```rust,ignore (partial-example) -impl !Trait for Type {} -``` - -Example: - -```rust -#![feature(negative_impls)] -#![feature(auto_traits)] - -auto trait Valid {} - -struct True; -struct False; - -impl !Valid for False {} - -struct MaybeValid(T); - -fn must_be_valid(_t: T) { } - -fn main() { - // works - must_be_valid( MaybeValid(True) ); - - // compiler error - trait bound not satisfied - // must_be_valid( MaybeValid(False) ); -} -``` - -## Automatic trait implementations - -When a type is declared as an `auto trait`, we will automatically -create impls for every struct/enum/union, unless an explicit impl is -provided. These automatic impls contain a where clause for each field -of the form `T: AutoTrait`, where `T` is the type of the field and -`AutoTrait` is the auto trait in question. As an example, consider the -struct `List` and the auto trait `Send`: - -```rust -struct List { - data: T, - next: Option>>, -} -``` - -Presuming that there is no explicit impl of `Send` for `List`, the -compiler will supply an automatic impl of the form: - -```rust -struct List { - data: T, - next: Option>>, -} - -unsafe impl Send for List -where - T: Send, // from the field `data` - Option>>: Send, // from the field `next` -{ } -``` - -Explicit impls may be either positive or negative. They take the form: - -```rust,ignore (partial-example) -impl<...> AutoTrait for StructName<..> { } -impl<...> !AutoTrait for StructName<..> { } -``` - -## Coinduction: Auto traits permit cyclic matching - -Unlike ordinary trait matching, auto traits are **coinductive**. This -means, in short, that cycles which occur in trait matching are -considered ok. As an example, consider the recursive struct `List` -introduced in the previous section. In attempting to determine whether -`List: Send`, we would wind up in a cycle: to apply the impl, we must -show that `Option>: Send`, which will in turn require -`Box: Send` and then finally `List: Send` again. Under ordinary -trait matching, this cycle would be an error, but for an auto trait it -is considered a successful match. - -## Items - -Auto traits cannot have any trait items, such as methods or associated types. This ensures that we can generate default implementations. - -## Supertraits - -Auto traits cannot have supertraits. This is for soundness reasons, as the interaction of coinduction with implied bounds is difficult to reconcile. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/box-patterns.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/box-patterns.md deleted file mode 100644 index bf0819ec920b..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/box-patterns.md +++ /dev/null @@ -1,32 +0,0 @@ -# `box_patterns` - -The tracking issue for this feature is: [#29641] - -[#29641]: https://github.com/rust-lang/rust/issues/29641 - -See also [`box_syntax`](box-syntax.md) - ------------------------- - -Box patterns let you match on `Box`s: - - -```rust -#![feature(box_patterns)] - -fn main() { - let b = Some(Box::new(5)); - match b { - Some(box n) if n < 0 => { - println!("Box contains negative number {}", n); - }, - Some(box n) if n >= 0 => { - println!("Box contains non-negative number {}", n); - }, - None => { - println!("No box"); - }, - _ => unreachable!() - } -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/box-syntax.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/box-syntax.md deleted file mode 100644 index 9569974d22ca..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/box-syntax.md +++ /dev/null @@ -1,22 +0,0 @@ -# `box_syntax` - -The tracking issue for this feature is: [#49733] - -[#49733]: https://github.com/rust-lang/rust/issues/49733 - -See also [`box_patterns`](box-patterns.md) - ------------------------- - -Currently the only stable way to create a `Box` is via the `Box::new` method. -Also it is not possible in stable Rust to destructure a `Box` in a match -pattern. The unstable `box` keyword can be used to create a `Box`. An example -usage would be: - -```rust -#![feature(box_syntax)] - -fn main() { - let b = box 5; -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/c-unwind.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/c-unwind.md deleted file mode 100644 index 2801d9b5e777..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/c-unwind.md +++ /dev/null @@ -1,15 +0,0 @@ -# `c_unwind` - -The tracking issue for this feature is: [#74990] - -[#74990]: https://github.com/rust-lang/rust/issues/74990 - ------------------------- - -Introduces four new ABI strings: "C-unwind", "stdcall-unwind", -"thiscall-unwind", and "system-unwind". These enable unwinding from other -languages (such as C++) into Rust frames and from Rust into other languages. - -See [RFC 2945] for more information. - -[RFC 2945]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/c-variadic.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/c-variadic.md deleted file mode 100644 index 9e7968d906fb..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/c-variadic.md +++ /dev/null @@ -1,24 +0,0 @@ -# `c_variadic` - -The tracking issue for this feature is: [#44930] - -[#44930]: https://github.com/rust-lang/rust/issues/44930 - ------------------------- - -The `c_variadic` language feature enables C-variadic functions to be -defined in Rust. The may be called both from within Rust and via FFI. - -## Examples - -```rust -#![feature(c_variadic)] - -pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize { - let mut sum = 0; - for _ in 0..n { - sum += args.arg::(); - } - sum -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/cfg-panic.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/cfg-panic.md deleted file mode 100644 index f5b73128ad6c..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/cfg-panic.md +++ /dev/null @@ -1,38 +0,0 @@ -# `cfg_panic` - -The tracking issue for this feature is: [#77443] - -[#77443]: https://github.com/rust-lang/rust/issues/77443 - ------------------------- - -The `cfg_panic` feature makes it possible to execute different code -depending on the panic strategy. - -Possible values at the moment are `"unwind"` or `"abort"`, although -it is possible that new panic strategies may be added to Rust in the -future. - -## Examples - -```rust -#![feature(cfg_panic)] - -#[cfg(panic = "unwind")] -fn a() { - // ... -} - -#[cfg(not(panic = "unwind"))] -fn a() { - // ... -} - -fn b() { - if cfg!(panic = "abort") { - // ... - } else { - // ... - } -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/cfg-sanitize.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/cfg-sanitize.md deleted file mode 100644 index 3442abf46df8..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/cfg-sanitize.md +++ /dev/null @@ -1,34 +0,0 @@ -# `cfg_sanitize` - -The tracking issue for this feature is: [#39699] - -[#39699]: https://github.com/rust-lang/rust/issues/39699 - ------------------------- - -The `cfg_sanitize` feature makes it possible to execute different code -depending on whether a particular sanitizer is enabled or not. - -## Examples - -```rust -#![feature(cfg_sanitize)] - -#[cfg(sanitize = "thread")] -fn a() { - // ... -} - -#[cfg(not(sanitize = "thread"))] -fn a() { - // ... -} - -fn b() { - if cfg!(sanitize = "leak") { - // ... - } else { - // ... - } -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/cfg-version.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/cfg-version.md deleted file mode 100644 index a6ec42cecba8..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/cfg-version.md +++ /dev/null @@ -1,35 +0,0 @@ -# `cfg_version` - -The tracking issue for this feature is: [#64796] - -[#64796]: https://github.com/rust-lang/rust/issues/64796 - ------------------------- - -The `cfg_version` feature makes it possible to execute different code -depending on the compiler version. It will return true if the compiler -version is greater than or equal to the specified version. - -## Examples - -```rust -#![feature(cfg_version)] - -#[cfg(version("1.42"))] // 1.42 and above -fn a() { - // ... -} - -#[cfg(not(version("1.42")))] // 1.41 and below -fn a() { - // ... -} - -fn b() { - if cfg!(version("1.42")) { - // ... - } else { - // ... - } -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/closure-track-caller.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/closure-track-caller.md deleted file mode 100644 index c948810d3e5a..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/closure-track-caller.md +++ /dev/null @@ -1,12 +0,0 @@ -# `closure_track_caller` - -The tracking issue for this feature is: [#87417] - -[#87417]: https://github.com/rust-lang/rust/issues/87417 - ------------------------- - -Allows using the `#[track_caller]` attribute on closures and generators. -Calls made to the closure or generator will have caller information -available through `std::panic::Location::caller()`, just like using -`#[track_caller]` on a function. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/cmse-nonsecure-entry.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/cmse-nonsecure-entry.md deleted file mode 100644 index 338fbc4b2bfc..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/cmse-nonsecure-entry.md +++ /dev/null @@ -1,81 +0,0 @@ -# `cmse_nonsecure_entry` - -The tracking issue for this feature is: [#75835] - -[#75835]: https://github.com/rust-lang/rust/issues/75835 - ------------------------- - -The [TrustZone-M -feature](https://developer.arm.com/documentation/100690/latest/) is available -for targets with the Armv8-M architecture profile (`thumbv8m` in their target -name). -LLVM, the Rust compiler and the linker are providing -[support](https://developer.arm.com/documentation/ecm0359818/latest/) for the -TrustZone-M feature. - -One of the things provided, with this unstable feature, is the -`cmse_nonsecure_entry` attribute. This attribute marks a Secure function as an -entry function (see [section -5.4](https://developer.arm.com/documentation/ecm0359818/latest/) for details). -With this attribute, the compiler will do the following: -* add a special symbol on the function which is the `__acle_se_` prefix and the - standard function name -* constrain the number of parameters to avoid using the Non-Secure stack -* before returning from the function, clear registers that might contain Secure - information -* use the `BXNS` instruction to return - -Because the stack can not be used to pass parameters, there will be compilation -errors if: -* the total size of all parameters is too big (for example more than four 32 - bits integers) -* the entry function is not using a C ABI - -The special symbol `__acle_se_` will be used by the linker to generate a secure -gateway veneer. - - - -``` rust,ignore -#![feature(cmse_nonsecure_entry)] - -#[no_mangle] -#[cmse_nonsecure_entry] -pub extern "C" fn entry_function(input: u32) -> u32 { - input + 6 -} -``` - -``` text -$ rustc --emit obj --crate-type lib --target thumbv8m.main-none-eabi function.rs -$ arm-none-eabi-objdump -D function.o - -00000000 : - 0: b580 push {r7, lr} - 2: 466f mov r7, sp - 4: b082 sub sp, #8 - 6: 9001 str r0, [sp, #4] - 8: 1d81 adds r1, r0, #6 - a: 460a mov r2, r1 - c: 4281 cmp r1, r0 - e: 9200 str r2, [sp, #0] - 10: d30b bcc.n 2a - 12: e7ff b.n 14 - 14: 9800 ldr r0, [sp, #0] - 16: b002 add sp, #8 - 18: e8bd 4080 ldmia.w sp!, {r7, lr} - 1c: 4671 mov r1, lr - 1e: 4672 mov r2, lr - 20: 4673 mov r3, lr - 22: 46f4 mov ip, lr - 24: f38e 8800 msr CPSR_f, lr - 28: 4774 bxns lr - 2a: f240 0000 movw r0, #0 - 2e: f2c0 0000 movt r0, #0 - 32: f240 0200 movw r2, #0 - 36: f2c0 0200 movt r2, #0 - 3a: 211c movs r1, #28 - 3c: f7ff fffe bl 0 <_ZN4core9panicking5panic17h5c028258ca2fb3f5E> - 40: defe udf #254 ; 0xfe -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/compiler-builtins.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/compiler-builtins.md deleted file mode 100644 index 52fac575b6e8..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/compiler-builtins.md +++ /dev/null @@ -1,5 +0,0 @@ -# `compiler_builtins` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/const-eval-limit.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/const-eval-limit.md deleted file mode 100644 index df68e83bcac7..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/const-eval-limit.md +++ /dev/null @@ -1,7 +0,0 @@ -# `const_eval_limit` - -The tracking issue for this feature is: [#67217] - -[#67217]: https://github.com/rust-lang/rust/issues/67217 - -The `const_eval_limit` allows someone to limit the evaluation steps the CTFE undertakes to evaluate a `const fn`. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/crate-visibility-modifier.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/crate-visibility-modifier.md deleted file mode 100644 index b59859dd348e..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/crate-visibility-modifier.md +++ /dev/null @@ -1,20 +0,0 @@ -# `crate_visibility_modifier` - -The tracking issue for this feature is: [#53120] - -[#53120]: https://github.com/rust-lang/rust/issues/53120 - ------ - -The `crate_visibility_modifier` feature allows the `crate` keyword to be used -as a visibility modifier synonymous to `pub(crate)`, indicating that a type -(function, _&c._) is to be visible to the entire enclosing crate, but not to -other crates. - -```rust -#![feature(crate_visibility_modifier)] - -crate struct Foo { - bar: usize, -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/custom-test-frameworks.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/custom-test-frameworks.md deleted file mode 100644 index 53ecac9314d7..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/custom-test-frameworks.md +++ /dev/null @@ -1,32 +0,0 @@ -# `custom_test_frameworks` - -The tracking issue for this feature is: [#50297] - -[#50297]: https://github.com/rust-lang/rust/issues/50297 - ------------------------- - -The `custom_test_frameworks` feature allows the use of `#[test_case]` and `#![test_runner]`. -Any function, const, or static can be annotated with `#[test_case]` causing it to be aggregated (like `#[test]`) -and be passed to the test runner determined by the `#![test_runner]` crate attribute. - -```rust -#![feature(custom_test_frameworks)] -#![test_runner(my_runner)] - -fn my_runner(tests: &[&i32]) { - for t in tests { - if **t == 0 { - println!("PASSED"); - } else { - println!("FAILED"); - } - } -} - -#[test_case] -const WILL_PASS: i32 = 0; - -#[test_case] -const WILL_FAIL: i32 = 4; -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/doc-cfg.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/doc-cfg.md deleted file mode 100644 index e75f1aea9922..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/doc-cfg.md +++ /dev/null @@ -1,46 +0,0 @@ -# `doc_cfg` - -The tracking issue for this feature is: [#43781] - ------- - -The `doc_cfg` feature allows an API be documented as only available in some specific platforms. -This attribute has two effects: - -1. In the annotated item's documentation, there will be a message saying "This is supported on - (platform) only". - -2. The item's doc-tests will only run on the specific platform. - -In addition to allowing the use of the `#[doc(cfg)]` attribute, this feature enables the use of a -special conditional compilation flag, `#[cfg(doc)]`, set whenever building documentation on your -crate. - -This feature was introduced as part of PR [#43348] to allow the platform-specific parts of the -standard library be documented. - -```rust -#![feature(doc_cfg)] - -#[cfg(any(windows, doc))] -#[doc(cfg(windows))] -/// The application's icon in the notification area (a.k.a. system tray). -/// -/// # Examples -/// -/// ```no_run -/// extern crate my_awesome_ui_library; -/// use my_awesome_ui_library::current_app; -/// use my_awesome_ui_library::windows::notification; -/// -/// let icon = current_app().get::(); -/// icon.show(); -/// icon.show_message("Hello"); -/// ``` -pub struct Icon { - // ... -} -``` - -[#43781]: https://github.com/rust-lang/rust/issues/43781 -[#43348]: https://github.com/rust-lang/rust/issues/43348 diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/doc-masked.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/doc-masked.md deleted file mode 100644 index 609939bfc22f..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/doc-masked.md +++ /dev/null @@ -1,24 +0,0 @@ -# `doc_masked` - -The tracking issue for this feature is: [#44027] - ------ - -The `doc_masked` feature allows a crate to exclude types from a given crate from appearing in lists -of trait implementations. The specifics of the feature are as follows: - -1. When rustdoc encounters an `extern crate` statement annotated with a `#[doc(masked)]` attribute, - it marks the crate as being masked. - -2. When listing traits a given type implements, rustdoc ensures that traits from masked crates are - not emitted into the documentation. - -3. When listing types that implement a given trait, rustdoc ensures that types from masked crates - are not emitted into the documentation. - -This feature was introduced in PR [#44026] to ensure that compiler-internal and -implementation-specific types and traits were not included in the standard library's documentation. -Such types would introduce broken links into the documentation. - -[#44026]: https://github.com/rust-lang/rust/pull/44026 -[#44027]: https://github.com/rust-lang/rust/pull/44027 diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/doc-notable-trait.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/doc-notable-trait.md deleted file mode 100644 index dc402ed4253a..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/doc-notable-trait.md +++ /dev/null @@ -1,33 +0,0 @@ -# `doc_notable_trait` - -The tracking issue for this feature is: [#45040] - -The `doc_notable_trait` feature allows the use of the `#[doc(notable_trait)]` -attribute, which will display the trait in a "Notable traits" dialog for -functions returning types that implement the trait. For example, this attribute -is applied to the `Iterator`, `Future`, `io::Read`, and `io::Write` traits in -the standard library. - -You can do this on your own traits like so: - -``` -#![feature(doc_notable_trait)] - -#[doc(notable_trait)] -pub trait MyTrait {} - -pub struct MyStruct; -impl MyTrait for MyStruct {} - -/// The docs for this function will have a button that displays a dialog about -/// `MyStruct` implementing `MyTrait`. -pub fn my_fn() -> MyStruct { MyStruct } -``` - -This feature was originally implemented in PR [#45039]. - -See also its documentation in [the rustdoc book][rustdoc-book-notable_trait]. - -[#45040]: https://github.com/rust-lang/rust/issues/45040 -[#45039]: https://github.com/rust-lang/rust/pull/45039 -[rustdoc-book-notable_trait]: ../../rustdoc/unstable-features.html#adding-your-trait-to-the-notable-traits-dialog diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/exclusive-range-pattern.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/exclusive-range-pattern.md deleted file mode 100644 index d26512703f49..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/exclusive-range-pattern.md +++ /dev/null @@ -1,26 +0,0 @@ -# `exclusive_range_pattern` - -The tracking issue for this feature is: [#37854]. - - -[#67264]: https://github.com/rust-lang/rust/issues/67264 -[#37854]: https://github.com/rust-lang/rust/issues/37854 ------ - -The `exclusive_range_pattern` feature allows non-inclusive range -patterns (`0..10`) to be used in appropriate pattern matching -contexts. It also can be combined with `#![feature(half_open_range_patterns]` -to be able to use RangeTo patterns (`..10`). - -It also enabled RangeFrom patterns but that has since been -stabilized. - -```rust -#![feature(exclusive_range_pattern)] - let x = 5; - match x { - 0..10 => println!("single digit"), - 10 => println!("ten isn't part of the above range"), - _ => println!("nor is everything else.") - } -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/explicit-generic-args-with-impl-trait.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/explicit-generic-args-with-impl-trait.md deleted file mode 100644 index 479571d85fe0..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/explicit-generic-args-with-impl-trait.md +++ /dev/null @@ -1,53 +0,0 @@ -# `explicit_generic_args_with_impl_trait` - -The tracking issue for this feature is: [#83701] - -[#83701]: https://github.com/rust-lang/rust/issues/83701 - ------------------------- - -The `explicit_generic_args_with_impl_trait` feature gate lets you specify generic arguments even -when `impl Trait` is used in argument position. - -A simple example is: - -```rust -#![feature(explicit_generic_args_with_impl_trait)] - -fn foo(_f: impl AsRef) {} - -fn main() { - foo::("".to_string()); -} -``` - -This is currently rejected: - -```text -error[E0632]: cannot provide explicit generic arguments when `impl Trait` is used in argument position - --> src/main.rs:6:11 - | -6 | foo::("".to_string()); - | ^^^ explicit generic argument not allowed - -``` - -However it would compile if `explicit_generic_args_with_impl_trait` is enabled. - -Note that the synthetic type parameters from `impl Trait` are still implicit and you -cannot explicitly specify these: - -```rust,compile_fail -#![feature(explicit_generic_args_with_impl_trait)] - -fn foo(_f: impl AsRef) {} -fn bar>(_f: F) {} - -fn main() { - bar::("".to_string()); // Okay - bar::("".to_string()); // Okay - - foo::("".to_string()); // Okay - foo::("".to_string()); // Error, you cannot specify `impl Trait` explicitly -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/ffi-const.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/ffi-const.md deleted file mode 100644 index 24a304437542..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/ffi-const.md +++ /dev/null @@ -1,52 +0,0 @@ -# `ffi_const` - -The tracking issue for this feature is: [#58328] - ------- - -The `#[ffi_const]` attribute applies clang's `const` attribute to foreign -functions declarations. - -That is, `#[ffi_const]` functions shall have no effects except for its return -value, which can only depend on the values of the function parameters, and is -not affected by changes to the observable state of the program. - -Applying the `#[ffi_const]` attribute to a function that violates these -requirements is undefined behaviour. - -This attribute enables Rust to perform common optimizations, like sub-expression -elimination, and it can avoid emitting some calls in repeated invocations of the -function with the same argument values regardless of other operations being -performed in between these functions calls (as opposed to `#[ffi_pure]` -functions). - -## Pitfalls - -A `#[ffi_const]` function can only read global memory that would not affect -its return value for the whole execution of the program (e.g. immutable global -memory). `#[ffi_const]` functions are referentially-transparent and therefore -more strict than `#[ffi_pure]` functions. - -A common pitfall involves applying the `#[ffi_const]` attribute to a -function that reads memory through pointer arguments which do not necessarily -point to immutable global memory. - -A `#[ffi_const]` function that returns unit has no effect on the abstract -machine's state, and a `#[ffi_const]` function cannot be `#[ffi_pure]`. - -A `#[ffi_const]` function must not diverge, neither via a side effect (e.g. a -call to `abort`) nor by infinite loops. - -When translating C headers to Rust FFI, it is worth verifying for which targets -the `const` attribute is enabled in those headers, and using the appropriate -`cfg` macros in the Rust side to match those definitions. While the semantics of -`const` are implemented identically by many C and C++ compilers, e.g., clang, -[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily -implemented in this way on all of them. It is therefore also worth verifying -that the semantics of the C toolchain used to compile the binary being linked -against are compatible with those of the `#[ffi_const]`. - -[#58328]: https://github.com/rust-lang/rust/issues/58328 -[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacgigch.html -[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute -[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_const.htm diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/ffi-pure.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/ffi-pure.md deleted file mode 100644 index 236ccb9f9053..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/ffi-pure.md +++ /dev/null @@ -1,56 +0,0 @@ -# `ffi_pure` - -The tracking issue for this feature is: [#58329] - ------- - -The `#[ffi_pure]` attribute applies clang's `pure` attribute to foreign -functions declarations. - -That is, `#[ffi_pure]` functions shall have no effects except for its return -value, which shall not change across two consecutive function calls with -the same parameters. - -Applying the `#[ffi_pure]` attribute to a function that violates these -requirements is undefined behavior. - -This attribute enables Rust to perform common optimizations, like sub-expression -elimination and loop optimizations. Some common examples of pure functions are -`strlen` or `memcmp`. - -These optimizations are only applicable when the compiler can prove that no -program state observable by the `#[ffi_pure]` function has changed between calls -of the function, which could alter the result. See also the `#[ffi_const]` -attribute, which provides stronger guarantees regarding the allowable behavior -of a function, enabling further optimization. - -## Pitfalls - -A `#[ffi_pure]` function can read global memory through the function -parameters (e.g. pointers), globals, etc. `#[ffi_pure]` functions are not -referentially-transparent, and are therefore more relaxed than `#[ffi_const]` -functions. - -However, accessing global memory through volatile or atomic reads can violate the -requirement that two consecutive function calls shall return the same value. - -A `pure` function that returns unit has no effect on the abstract machine's -state. - -A `#[ffi_pure]` function must not diverge, neither via a side effect (e.g. a -call to `abort`) nor by infinite loops. - -When translating C headers to Rust FFI, it is worth verifying for which targets -the `pure` attribute is enabled in those headers, and using the appropriate -`cfg` macros in the Rust side to match those definitions. While the semantics of -`pure` are implemented identically by many C and C++ compilers, e.g., clang, -[GCC], [ARM C/C++ compiler], [IBM ILE C/C++], etc. they are not necessarily -implemented in this way on all of them. It is therefore also worth verifying -that the semantics of the C toolchain used to compile the binary being linked -against are compatible with those of the `#[ffi_pure]`. - - -[#58329]: https://github.com/rust-lang/rust/issues/58329 -[ARM C/C++ compiler]: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/Cacigdac.html -[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-pure-function-attribute -[IBM ILE C/C++]: https://www.ibm.com/support/knowledgecenter/fr/ssw_ibm_i_71/rzarg/fn_attrib_pure.htm diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/generators.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/generators.md deleted file mode 100644 index 7b865c9c679b..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/generators.md +++ /dev/null @@ -1,246 +0,0 @@ -# `generators` - -The tracking issue for this feature is: [#43122] - -[#43122]: https://github.com/rust-lang/rust/issues/43122 - ------------------------- - -The `generators` feature gate in Rust allows you to define generator or -coroutine literals. A generator is a "resumable function" that syntactically -resembles a closure but compiles to much different semantics in the compiler -itself. The primary feature of a generator is that it can be suspended during -execution to be resumed at a later date. Generators use the `yield` keyword to -"return", and then the caller can `resume` a generator to resume execution just -after the `yield` keyword. - -Generators are an extra-unstable feature in the compiler right now. Added in -[RFC 2033] they're mostly intended right now as a information/constraint -gathering phase. The intent is that experimentation can happen on the nightly -compiler before actual stabilization. A further RFC will be required to -stabilize generators/coroutines and will likely contain at least a few small -tweaks to the overall design. - -[RFC 2033]: https://github.com/rust-lang/rfcs/pull/2033 - -A syntactical example of a generator is: - -```rust -#![feature(generators, generator_trait)] - -use std::ops::{Generator, GeneratorState}; -use std::pin::Pin; - -fn main() { - let mut generator = || { - yield 1; - return "foo" - }; - - match Pin::new(&mut generator).resume(()) { - GeneratorState::Yielded(1) => {} - _ => panic!("unexpected value from resume"), - } - match Pin::new(&mut generator).resume(()) { - GeneratorState::Complete("foo") => {} - _ => panic!("unexpected value from resume"), - } -} -``` - -Generators are closure-like literals which can contain a `yield` statement. The -`yield` statement takes an optional expression of a value to yield out of the -generator. All generator literals implement the `Generator` trait in the -`std::ops` module. The `Generator` trait has one main method, `resume`, which -resumes execution of the generator at the previous suspension point. - -An example of the control flow of generators is that the following example -prints all numbers in order: - -```rust -#![feature(generators, generator_trait)] - -use std::ops::Generator; -use std::pin::Pin; - -fn main() { - let mut generator = || { - println!("2"); - yield; - println!("4"); - }; - - println!("1"); - Pin::new(&mut generator).resume(()); - println!("3"); - Pin::new(&mut generator).resume(()); - println!("5"); -} -``` - -At this time the main intended use case of generators is an implementation -primitive for async/await syntax, but generators will likely be extended to -ergonomic implementations of iterators and other primitives in the future. -Feedback on the design and usage is always appreciated! - -### The `Generator` trait - -The `Generator` trait in `std::ops` currently looks like: - -```rust -# #![feature(arbitrary_self_types, generator_trait)] -# use std::ops::GeneratorState; -# use std::pin::Pin; - -pub trait Generator { - type Yield; - type Return; - fn resume(self: Pin<&mut Self>, resume: R) -> GeneratorState; -} -``` - -The `Generator::Yield` type is the type of values that can be yielded with the -`yield` statement. The `Generator::Return` type is the returned type of the -generator. This is typically the last expression in a generator's definition or -any value passed to `return` in a generator. The `resume` function is the entry -point for executing the `Generator` itself. - -The return value of `resume`, `GeneratorState`, looks like: - -```rust -pub enum GeneratorState { - Yielded(Y), - Complete(R), -} -``` - -The `Yielded` variant indicates that the generator can later be resumed. This -corresponds to a `yield` point in a generator. The `Complete` variant indicates -that the generator is complete and cannot be resumed again. Calling `resume` -after a generator has returned `Complete` will likely result in a panic of the -program. - -### Closure-like semantics - -The closure-like syntax for generators alludes to the fact that they also have -closure-like semantics. Namely: - -* When created, a generator executes no code. A closure literal does not - actually execute any of the closure's code on construction, and similarly a - generator literal does not execute any code inside the generator when - constructed. - -* Generators can capture outer variables by reference or by move, and this can - be tweaked with the `move` keyword at the beginning of the closure. Like - closures all generators will have an implicit environment which is inferred by - the compiler. Outer variables can be moved into a generator for use as the - generator progresses. - -* Generator literals produce a value with a unique type which implements the - `std::ops::Generator` trait. This allows actual execution of the generator - through the `Generator::resume` method as well as also naming it in return - types and such. - -* Traits like `Send` and `Sync` are automatically implemented for a `Generator` - depending on the captured variables of the environment. Unlike closures, - generators also depend on variables live across suspension points. This means - that although the ambient environment may be `Send` or `Sync`, the generator - itself may not be due to internal variables live across `yield` points being - not-`Send` or not-`Sync`. Note that generators do - not implement traits like `Copy` or `Clone` automatically. - -* Whenever a generator is dropped it will drop all captured environment - variables. - -### Generators as state machines - -In the compiler, generators are currently compiled as state machines. Each -`yield` expression will correspond to a different state that stores all live -variables over that suspension point. Resumption of a generator will dispatch on -the current state and then execute internally until a `yield` is reached, at -which point all state is saved off in the generator and a value is returned. - -Let's take a look at an example to see what's going on here: - -```rust -#![feature(generators, generator_trait)] - -use std::ops::Generator; -use std::pin::Pin; - -fn main() { - let ret = "foo"; - let mut generator = move || { - yield 1; - return ret - }; - - Pin::new(&mut generator).resume(()); - Pin::new(&mut generator).resume(()); -} -``` - -This generator literal will compile down to something similar to: - -```rust -#![feature(arbitrary_self_types, generators, generator_trait)] - -use std::ops::{Generator, GeneratorState}; -use std::pin::Pin; - -fn main() { - let ret = "foo"; - let mut generator = { - enum __Generator { - Start(&'static str), - Yield1(&'static str), - Done, - } - - impl Generator for __Generator { - type Yield = i32; - type Return = &'static str; - - fn resume(mut self: Pin<&mut Self>, resume: ()) -> GeneratorState { - use std::mem; - match mem::replace(&mut *self, __Generator::Done) { - __Generator::Start(s) => { - *self = __Generator::Yield1(s); - GeneratorState::Yielded(1) - } - - __Generator::Yield1(s) => { - *self = __Generator::Done; - GeneratorState::Complete(s) - } - - __Generator::Done => { - panic!("generator resumed after completion") - } - } - } - } - - __Generator::Start(ret) - }; - - Pin::new(&mut generator).resume(()); - Pin::new(&mut generator).resume(()); -} -``` - -Notably here we can see that the compiler is generating a fresh type, -`__Generator` in this case. This type has a number of states (represented here -as an `enum`) corresponding to each of the conceptual states of the generator. -At the beginning we're closing over our outer variable `foo` and then that -variable is also live over the `yield` point, so it's stored in both states. - -When the generator starts it'll immediately yield 1, but it saves off its state -just before it does so indicating that it has reached the yield point. Upon -resuming again we'll execute the `return ret` which returns the `Complete` -state. - -Here we can also note that the `Done` state, if resumed, panics immediately as -it's invalid to resume a completed generator. It's also worth noting that this -is just a rough desugaring, not a normative specification for what the compiler -does. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/half-open-range-patterns.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/half-open-range-patterns.md deleted file mode 100644 index 3b16dd049ce3..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/half-open-range-patterns.md +++ /dev/null @@ -1,27 +0,0 @@ -# `half_open_range_patterns` - -The tracking issue for this feature is: [#67264] -It is part of the `#![exclusive_range_pattern]` feature, -tracked at [#37854]. - -[#67264]: https://github.com/rust-lang/rust/issues/67264 -[#37854]: https://github.com/rust-lang/rust/issues/37854 ------ - -The `half_open_range_patterns` feature allows RangeTo patterns -(`..10`) to be used in appropriate pattern matching contexts. -This requires also enabling the `exclusive_range_pattern` feature. - -It also enabled RangeFrom patterns but that has since been -stabilized. - -```rust -#![feature(half_open_range_patterns)] -#![feature(exclusive_range_pattern)] - let x = 5; - match x { - ..0 => println!("negative!"), // "RangeTo" pattern. Unstable. - 0 => println!("zero!"), - 1.. => println!("positive!"), // "RangeFrom" pattern. Stable. - } -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/infer-static-outlives-requirements.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/infer-static-outlives-requirements.md deleted file mode 100644 index 5f3f1b4dd8a3..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/infer-static-outlives-requirements.md +++ /dev/null @@ -1,44 +0,0 @@ -# `infer_static_outlives_requirements` - -The tracking issue for this feature is: [#54185] - -[#54185]: https://github.com/rust-lang/rust/issues/54185 - ------------------------- -The `infer_static_outlives_requirements` feature indicates that certain -`'static` outlives requirements can be inferred by the compiler rather than -stating them explicitly. - -Note: It is an accompanying feature to `infer_outlives_requirements`, -which must be enabled to infer outlives requirements. - -For example, currently generic struct definitions that contain -references, require where-clauses of the form T: 'static. By using -this feature the outlives predicates will be inferred, although -they may still be written explicitly. - -```rust,ignore (pseudo-Rust) -struct Foo where U: 'static { // <-- currently required - bar: Bar -} -struct Bar { - x: T, -} -``` - - -## Examples: - -```rust,ignore (pseudo-Rust) -#![feature(infer_outlives_requirements)] -#![feature(infer_static_outlives_requirements)] - -#[rustc_outlives] -// Implicitly infer U: 'static -struct Foo { - bar: Bar -} -struct Bar { - x: T, -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/inline-const-pat.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/inline-const-pat.md deleted file mode 100644 index 5f0f7547a0a8..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/inline-const-pat.md +++ /dev/null @@ -1,24 +0,0 @@ -# `inline_const_pat` - -The tracking issue for this feature is: [#76001] - -See also [`inline_const`](inline-const.md) - ------- - -This feature allows you to use inline constant expressions in pattern position: - -```rust -#![feature(inline_const_pat)] - -const fn one() -> i32 { 1 } - -let some_int = 3; -match some_int { - const { 1 + 2 } => println!("Matched 1 + 2"), - const { one() } => println!("Matched const fn returning 1"), - _ => println!("Didn't match anything :("), -} -``` - -[#76001]: https://github.com/rust-lang/rust/issues/76001 diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/inline-const.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/inline-const.md deleted file mode 100644 index 7be70eed6ced..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/inline-const.md +++ /dev/null @@ -1,32 +0,0 @@ -# `inline_const` - -The tracking issue for this feature is: [#76001] - -See also [`inline_const_pat`](inline-const-pat.md) - ------- - -This feature allows you to use inline constant expressions. For example, you can -turn this code: - -```rust -# fn add_one(x: i32) -> i32 { x + 1 } -const MY_COMPUTATION: i32 = 1 + 2 * 3 / 4; - -fn main() { - let x = add_one(MY_COMPUTATION); -} -``` - -into this code: - -```rust -#![feature(inline_const)] - -# fn add_one(x: i32) -> i32 { x + 1 } -fn main() { - let x = add_one(const { 1 + 2 * 3 / 4 }); -} -``` - -[#76001]: https://github.com/rust-lang/rust/issues/76001 diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/intra-doc-pointers.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/intra-doc-pointers.md deleted file mode 100644 index fbc83f4b4f48..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/intra-doc-pointers.md +++ /dev/null @@ -1,15 +0,0 @@ -# `intra-doc-pointers` - -The tracking issue for this feature is: [#80896] - -[#80896]: https://github.com/rust-lang/rust/issues/80896 - ------------------------- - -Rustdoc does not currently allow disambiguating between `*const` and `*mut`, and -raw pointers in intra-doc links are unstable until it does. - -```rust -#![feature(intra_doc_pointers)] -//! [pointer::add] -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/intrinsics.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/intrinsics.md deleted file mode 100644 index a0fb4e743d3f..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/intrinsics.md +++ /dev/null @@ -1,29 +0,0 @@ -# `intrinsics` - -The tracking issue for this feature is: None. - -Intrinsics are never intended to be stable directly, but intrinsics are often -exported in some sort of stable manner. Prefer using the stable interfaces to -the intrinsic directly when you can. - ------------------------- - - -These are imported as if they were FFI functions, with the special -`rust-intrinsic` ABI. For example, if one was in a freestanding -context, but wished to be able to `transmute` between types, and -perform efficient pointer arithmetic, one would import those functions -via a declaration like - -```rust -#![feature(intrinsics)] -# fn main() {} - -extern "rust-intrinsic" { - fn transmute(x: T) -> U; - - fn offset(dst: *const T, offset: isize) -> *const T; -} -``` - -As with any other FFI functions, these are always `unsafe` to call. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/lang-items.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/lang-items.md deleted file mode 100644 index 86bedb51538b..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/lang-items.md +++ /dev/null @@ -1,295 +0,0 @@ -# `lang_items` - -The tracking issue for this feature is: None. - ------------------------- - -The `rustc` compiler has certain pluggable operations, that is, -functionality that isn't hard-coded into the language, but is -implemented in libraries, with a special marker to tell the compiler -it exists. The marker is the attribute `#[lang = "..."]` and there are -various different values of `...`, i.e. various different 'lang -items'. - -For example, `Box` pointers require two lang items, one for allocation -and one for deallocation. A freestanding program that uses the `Box` -sugar for dynamic allocations via `malloc` and `free`: - -```rust,ignore (libc-is-finicky) -#![feature(lang_items, box_syntax, start, libc, core_intrinsics, rustc_private)] -#![no_std] -use core::intrinsics; -use core::panic::PanicInfo; - -extern crate libc; - -#[lang = "owned_box"] -pub struct Box(*mut T); - -#[lang = "exchange_malloc"] -unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { - let p = libc::malloc(size as libc::size_t) as *mut u8; - - // Check if `malloc` failed: - if p as usize == 0 { - intrinsics::abort(); - } - - p -} - -#[lang = "box_free"] -unsafe fn box_free(ptr: *mut T) { - libc::free(ptr as *mut libc::c_void) -} - -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { - let _x = box 1; - - 0 -} - -#[lang = "eh_personality"] extern fn rust_eh_personality() {} -#[lang = "panic_impl"] extern fn rust_begin_panic(info: &PanicInfo) -> ! { unsafe { intrinsics::abort() } } -#[no_mangle] pub extern fn rust_eh_register_frames () {} -#[no_mangle] pub extern fn rust_eh_unregister_frames () {} -``` - -Note the use of `abort`: the `exchange_malloc` lang item is assumed to -return a valid pointer, and so needs to do the check internally. - -Other features provided by lang items include: - -- overloadable operators via traits: the traits corresponding to the - `==`, `<`, dereferencing (`*`) and `+` (etc.) operators are all - marked with lang items; those specific four are `eq`, `ord`, - `deref`, and `add` respectively. -- stack unwinding and general failure; the `eh_personality`, - `panic` and `panic_bounds_check` lang items. -- the traits in `std::marker` used to indicate types of - various kinds; lang items `send`, `sync` and `copy`. -- the marker types and variance indicators found in - `std::marker`; lang items `covariant_type`, - `contravariant_lifetime`, etc. - -Lang items are loaded lazily by the compiler; e.g. if one never uses -`Box` then there is no need to define functions for `exchange_malloc` -and `box_free`. `rustc` will emit an error when an item is needed -but not found in the current crate or any that it depends on. - -Most lang items are defined by `libcore`, but if you're trying to build -an executable without the standard library, you'll run into the need -for lang items. The rest of this page focuses on this use-case, even though -lang items are a bit broader than that. - -### Using libc - -In order to build a `#[no_std]` executable we will need libc as a dependency. -We can specify this using our `Cargo.toml` file: - -```toml -[dependencies] -libc = { version = "0.2.14", default-features = false } -``` - -Note that the default features have been disabled. This is a critical step - -**the default features of libc include the standard library and so must be -disabled.** - -### Writing an executable without stdlib - -Controlling the entry point is possible in two ways: the `#[start]` attribute, -or overriding the default shim for the C `main` function with your own. - -The function marked `#[start]` is passed the command line parameters -in the same format as C: - -```rust,ignore (libc-is-finicky) -#![feature(lang_items, core_intrinsics, rustc_private)] -#![feature(start)] -#![no_std] -use core::intrinsics; -use core::panic::PanicInfo; - -// Pull in the system libc library for what crt0.o likely requires. -extern crate libc; - -// Entry point for this program. -#[start] -fn start(_argc: isize, _argv: *const *const u8) -> isize { - 0 -} - -// These functions are used by the compiler, but not -// for a bare-bones hello world. These are normally -// provided by libstd. -#[lang = "eh_personality"] -#[no_mangle] -pub extern fn rust_eh_personality() { -} - -#[lang = "panic_impl"] -#[no_mangle] -pub extern fn rust_begin_panic(info: &PanicInfo) -> ! { - unsafe { intrinsics::abort() } -} -``` - -To override the compiler-inserted `main` shim, one has to disable it -with `#![no_main]` and then create the appropriate symbol with the -correct ABI and the correct name, which requires overriding the -compiler's name mangling too: - -```rust,ignore (libc-is-finicky) -#![feature(lang_items, core_intrinsics, rustc_private)] -#![feature(start)] -#![no_std] -#![no_main] -use core::intrinsics; -use core::panic::PanicInfo; - -// Pull in the system libc library for what crt0.o likely requires. -extern crate libc; - -// Entry point for this program. -#[no_mangle] // ensure that this symbol is called `main` in the output -pub extern fn main(_argc: i32, _argv: *const *const u8) -> i32 { - 0 -} - -// These functions are used by the compiler, but not -// for a bare-bones hello world. These are normally -// provided by libstd. -#[lang = "eh_personality"] -#[no_mangle] -pub extern fn rust_eh_personality() { -} - -#[lang = "panic_impl"] -#[no_mangle] -pub extern fn rust_begin_panic(info: &PanicInfo) -> ! { - unsafe { intrinsics::abort() } -} -``` - -In many cases, you may need to manually link to the `compiler_builtins` crate -when building a `no_std` binary. You may observe this via linker error messages -such as "```undefined reference to `__rust_probestack'```". - -## More about the language items - -The compiler currently makes a few assumptions about symbols which are -available in the executable to call. Normally these functions are provided by -the standard library, but without it you must define your own. These symbols -are called "language items", and they each have an internal name, and then a -signature that an implementation must conform to. - -The first of these functions, `rust_eh_personality`, is used by the failure -mechanisms of the compiler. This is often mapped to GCC's personality function -(see the [libstd implementation][unwind] for more information), but crates -which do not trigger a panic can be assured that this function is never -called. The language item's name is `eh_personality`. - -[unwind]: https://github.com/rust-lang/rust/blob/master/library/panic_unwind/src/gcc.rs - -The second function, `rust_begin_panic`, is also used by the failure mechanisms of the -compiler. When a panic happens, this controls the message that's displayed on -the screen. While the language item's name is `panic_impl`, the symbol name is -`rust_begin_panic`. - -Finally, a `eh_catch_typeinfo` static is needed for certain targets which -implement Rust panics on top of C++ exceptions. - -## List of all language items - -This is a list of all language items in Rust along with where they are located in -the source code. - -- Primitives - - `i8`: `libcore/num/mod.rs` - - `i16`: `libcore/num/mod.rs` - - `i32`: `libcore/num/mod.rs` - - `i64`: `libcore/num/mod.rs` - - `i128`: `libcore/num/mod.rs` - - `isize`: `libcore/num/mod.rs` - - `u8`: `libcore/num/mod.rs` - - `u16`: `libcore/num/mod.rs` - - `u32`: `libcore/num/mod.rs` - - `u64`: `libcore/num/mod.rs` - - `u128`: `libcore/num/mod.rs` - - `usize`: `libcore/num/mod.rs` - - `f32`: `libstd/f32.rs` - - `f64`: `libstd/f64.rs` - - `char`: `libcore/char.rs` - - `slice`: `liballoc/slice.rs` - - `str`: `liballoc/str.rs` - - `const_ptr`: `libcore/ptr.rs` - - `mut_ptr`: `libcore/ptr.rs` - - `unsafe_cell`: `libcore/cell.rs` -- Runtime - - `start`: `libstd/rt.rs` - - `eh_personality`: `libpanic_unwind/emcc.rs` (EMCC) - - `eh_personality`: `libpanic_unwind/gcc.rs` (GNU) - - `eh_personality`: `libpanic_unwind/seh.rs` (SEH) - - `eh_catch_typeinfo`: `libpanic_unwind/emcc.rs` (EMCC) - - `panic`: `libcore/panicking.rs` - - `panic_bounds_check`: `libcore/panicking.rs` - - `panic_impl`: `libcore/panicking.rs` - - `panic_impl`: `libstd/panicking.rs` -- Allocations - - `owned_box`: `liballoc/boxed.rs` - - `exchange_malloc`: `liballoc/heap.rs` - - `box_free`: `liballoc/heap.rs` -- Operands - - `not`: `libcore/ops/bit.rs` - - `bitand`: `libcore/ops/bit.rs` - - `bitor`: `libcore/ops/bit.rs` - - `bitxor`: `libcore/ops/bit.rs` - - `shl`: `libcore/ops/bit.rs` - - `shr`: `libcore/ops/bit.rs` - - `bitand_assign`: `libcore/ops/bit.rs` - - `bitor_assign`: `libcore/ops/bit.rs` - - `bitxor_assign`: `libcore/ops/bit.rs` - - `shl_assign`: `libcore/ops/bit.rs` - - `shr_assign`: `libcore/ops/bit.rs` - - `deref`: `libcore/ops/deref.rs` - - `deref_mut`: `libcore/ops/deref.rs` - - `index`: `libcore/ops/index.rs` - - `index_mut`: `libcore/ops/index.rs` - - `add`: `libcore/ops/arith.rs` - - `sub`: `libcore/ops/arith.rs` - - `mul`: `libcore/ops/arith.rs` - - `div`: `libcore/ops/arith.rs` - - `rem`: `libcore/ops/arith.rs` - - `neg`: `libcore/ops/arith.rs` - - `add_assign`: `libcore/ops/arith.rs` - - `sub_assign`: `libcore/ops/arith.rs` - - `mul_assign`: `libcore/ops/arith.rs` - - `div_assign`: `libcore/ops/arith.rs` - - `rem_assign`: `libcore/ops/arith.rs` - - `eq`: `libcore/cmp.rs` - - `ord`: `libcore/cmp.rs` -- Functions - - `fn`: `libcore/ops/function.rs` - - `fn_mut`: `libcore/ops/function.rs` - - `fn_once`: `libcore/ops/function.rs` - - `generator_state`: `libcore/ops/generator.rs` - - `generator`: `libcore/ops/generator.rs` -- Other - - `coerce_unsized`: `libcore/ops/unsize.rs` - - `drop`: `libcore/ops/drop.rs` - - `drop_in_place`: `libcore/ptr.rs` - - `clone`: `libcore/clone.rs` - - `copy`: `libcore/marker.rs` - - `send`: `libcore/marker.rs` - - `sized`: `libcore/marker.rs` - - `unsize`: `libcore/marker.rs` - - `sync`: `libcore/marker.rs` - - `phantom_data`: `libcore/marker.rs` - - `discriminant_kind`: `libcore/marker.rs` - - `freeze`: `libcore/marker.rs` - - `debug_trait`: `libcore/fmt/mod.rs` - - `non_zero`: `libcore/nonzero.rs` - - `arc`: `liballoc/sync.rs` - - `rc`: `liballoc/rc.rs` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/link-cfg.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/link-cfg.md deleted file mode 100644 index ee0fd5bf8698..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/link-cfg.md +++ /dev/null @@ -1,5 +0,0 @@ -# `link_cfg` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/marker-trait-attr.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/marker-trait-attr.md deleted file mode 100644 index be350cd61696..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/marker-trait-attr.md +++ /dev/null @@ -1,35 +0,0 @@ -# `marker_trait_attr` - -The tracking issue for this feature is: [#29864] - -[#29864]: https://github.com/rust-lang/rust/issues/29864 - ------------------------- - -Normally, Rust keeps you from adding trait implementations that could -overlap with each other, as it would be ambiguous which to use. This -feature, however, carves out an exception to that rule: a trait can -opt-in to having overlapping implementations, at the cost that those -implementations are not allowed to override anything (and thus the -trait itself cannot have any associated items, as they're pointless -when they'd need to do the same thing for every type anyway). - -```rust -#![feature(marker_trait_attr)] - -#[marker] trait CheapToClone: Clone {} - -impl CheapToClone for T {} - -// These could potentially overlap with the blanket implementation above, -// so are only allowed because CheapToClone is a marker trait. -impl CheapToClone for (T, U) {} -impl CheapToClone for std::ops::Range {} - -fn cheap_clone(t: T) -> T { - t.clone() -} -``` - -This is expected to replace the unstable `overlapping_marker_traits` -feature, which applied to all empty traits (without needing an opt-in). diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/more-qualified-paths.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/more-qualified-paths.md deleted file mode 100644 index 857af577a6cf..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/more-qualified-paths.md +++ /dev/null @@ -1,29 +0,0 @@ -# `more_qualified_paths` - -The `more_qualified_paths` feature can be used in order to enable the -use of qualified paths in patterns. - -## Example - -```rust -#![feature(more_qualified_paths)] - -fn main() { - // destructure through a qualified path - let ::Assoc { br } = StructStruct { br: 2 }; -} - -struct StructStruct { - br: i8, -} - -struct Foo; - -trait A { - type Assoc; -} - -impl A for Foo { - type Assoc = StructStruct; -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-as-needed.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-as-needed.md deleted file mode 100644 index 1757673612c4..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-as-needed.md +++ /dev/null @@ -1,18 +0,0 @@ -# `native_link_modifiers_as_needed` - -The tracking issue for this feature is: [#81490] - -[#81490]: https://github.com/rust-lang/rust/issues/81490 - ------------------------- - -The `native_link_modifiers_as_needed` feature allows you to use the `as-needed` modifier. - -`as-needed` is only compatible with the `dynamic` and `framework` linking kinds. Using any other kind will result in a compiler error. - -`+as-needed` means that the library will be actually linked only if it satisfies some undefined symbols at the point at which it is specified on the command line, making it similar to static libraries in this regard. - -This modifier translates to `--as-needed` for ld-like linkers, and to `-dead_strip_dylibs` / `-needed_library` / `-needed_framework` for ld64. -The modifier does nothing for linkers that don't support it (e.g. `link.exe`). - -The default for this modifier is unclear, some targets currently specify it as `+as-needed`, some do not. We may want to try making `+as-needed` a default for all targets. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-bundle.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-bundle.md deleted file mode 100644 index ac192cff13a3..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-bundle.md +++ /dev/null @@ -1,19 +0,0 @@ -# `native_link_modifiers_bundle` - -The tracking issue for this feature is: [#81490] - -[#81490]: https://github.com/rust-lang/rust/issues/81490 - ------------------------- - -The `native_link_modifiers_bundle` feature allows you to use the `bundle` modifier. - -Only compatible with the `static` linking kind. Using any other kind will result in a compiler error. - -`+bundle` means objects from the static library are bundled into the produced crate (a rlib, for example) and are used from this crate later during linking of the final binary. - -`-bundle` means the static library is included into the produced rlib "by name" and object files from it are included only during linking of the final binary, the file search by that name is also performed during final linking. - -This modifier is supposed to supersede the `static-nobundle` linking kind defined by [RFC 1717](https://github.com/rust-lang/rfcs/pull/1717). - -The default for this modifier is currently `+bundle`, but it could be changed later on some future edition boundary. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-verbatim.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-verbatim.md deleted file mode 100644 index 02bd87e50956..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-verbatim.md +++ /dev/null @@ -1,20 +0,0 @@ -# `native_link_modifiers_verbatim` - -The tracking issue for this feature is: [#81490] - -[#81490]: https://github.com/rust-lang/rust/issues/81490 - ------------------------- - -The `native_link_modifiers_verbatim` feature allows you to use the `verbatim` modifier. - -`+verbatim` means that rustc itself won't add any target-specified library prefixes or suffixes (like `lib` or `.a`) to the library name, and will try its best to ask for the same thing from the linker. - -For `ld`-like linkers rustc will use the `-l:filename` syntax (note the colon) when passing the library, so the linker won't add any prefixes or suffixes as well. -See [`-l namespec`](https://sourceware.org/binutils/docs/ld/Options.html) in ld documentation for more details. -For linkers not supporting any verbatim modifiers (e.g. `link.exe` or `ld64`) the library name will be passed as is. - -The default for this modifier is `-verbatim`. - -This RFC changes the behavior of `raw-dylib` linking kind specified by [RFC 2627](https://github.com/rust-lang/rfcs/pull/2627). The `.dll` suffix (or other target-specified suffixes for other targets) is now added automatically. -If your DLL doesn't have the `.dll` suffix, it can be specified with `+verbatim`. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-whole-archive.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-whole-archive.md deleted file mode 100644 index 4961e88cad1e..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers-whole-archive.md +++ /dev/null @@ -1,18 +0,0 @@ -# `native_link_modifiers_whole_archive` - -The tracking issue for this feature is: [#81490] - -[#81490]: https://github.com/rust-lang/rust/issues/81490 - ------------------------- - -The `native_link_modifiers_whole_archive` feature allows you to use the `whole-archive` modifier. - -Only compatible with the `static` linking kind. Using any other kind will result in a compiler error. - -`+whole-archive` means that the static library is linked as a whole archive without throwing any object files away. - -This modifier translates to `--whole-archive` for `ld`-like linkers, to `/WHOLEARCHIVE` for `link.exe`, and to `-force_load` for `ld64`. -The modifier does nothing for linkers that don't support it. - -The default for this modifier is `-whole-archive`. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers.md deleted file mode 100644 index fc8b57546217..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/native-link-modifiers.md +++ /dev/null @@ -1,11 +0,0 @@ -# `native_link_modifiers` - -The tracking issue for this feature is: [#81490] - -[#81490]: https://github.com/rust-lang/rust/issues/81490 - ------------------------- - -The `native_link_modifiers` feature allows you to use the `modifiers` syntax with the `#[link(..)]` attribute. - -Modifiers are specified as a comma-delimited string with each modifier prefixed with either a `+` or `-` to indicate that the modifier is enabled or disabled, respectively. The last boolean value specified for a given modifier wins. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/negative-impls.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/negative-impls.md deleted file mode 100644 index 151520f0e4ab..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/negative-impls.md +++ /dev/null @@ -1,57 +0,0 @@ -# `negative_impls` - -The tracking issue for this feature is [#68318]. - -[#68318]: https://github.com/rust-lang/rust/issues/68318 - ----- - -With the feature gate `negative_impls`, you can write negative impls as well as positive ones: - -```rust -#![feature(negative_impls)] -trait DerefMut { } -impl !DerefMut for &T { } -``` - -Negative impls indicate a semver guarantee that the given trait will not be implemented for the given types. Negative impls play an additional purpose for auto traits, described below. - -Negative impls have the following characteristics: - -* They do not have any items. -* They must obey the orphan rules as if they were a positive impl. -* They cannot "overlap" with any positive impls. - -## Semver interaction - -It is a breaking change to remove a negative impl. Negative impls are a commitment not to implement the given trait for the named types. - -## Orphan and overlap rules - -Negative impls must obey the same orphan rules as a positive impl. This implies you cannot add a negative impl for types defined in upstream crates and so forth. - -Similarly, negative impls cannot overlap with positive impls, again using the same "overlap" check that we ordinarily use to determine if two impls overlap. (Note that positive impls typically cannot overlap with one another either, except as permitted by specialization.) - -## Interaction with auto traits - -Declaring a negative impl `impl !SomeAutoTrait for SomeType` for an -auto-trait serves two purposes: - -* as with any trait, it declares that `SomeType` will never implement `SomeAutoTrait`; -* it disables the automatic `SomeType: SomeAutoTrait` impl that would otherwise have been generated. - -Note that, at present, there is no way to indicate that a given type -does not implement an auto trait *but that it may do so in the -future*. For ordinary types, this is done by simply not declaring any -impl at all, but that is not an option for auto traits. A workaround -is that one could embed a marker type as one of the fields, where the -marker type is `!AutoTrait`. - -## Immediate uses - -Negative impls are used to declare that `&T: !DerefMut` and `&mut T: !Clone`, as required to fix the soundness of `Pin` described in [#66544](https://github.com/rust-lang/rust/issues/66544). - -This serves two purposes: - -* For proving the correctness of unsafe code, we can use that impl as evidence that no `DerefMut` or `Clone` impl exists. -* It prevents downstream crates from creating such impls. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/no-coverage.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/no-coverage.md deleted file mode 100644 index 327cdb39791a..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/no-coverage.md +++ /dev/null @@ -1,30 +0,0 @@ -# `no_coverage` - -The tracking issue for this feature is: [#84605] - -[#84605]: https://github.com/rust-lang/rust/issues/84605 - ---- - -The `no_coverage` attribute can be used to selectively disable coverage -instrumentation in an annotated function. This might be useful to: - -- Avoid instrumentation overhead in a performance critical function -- Avoid generating coverage for a function that is not meant to be executed, - but still target 100% coverage for the rest of the program. - -## Example - -```rust -#![feature(no_coverage)] - -// `foo()` will get coverage instrumentation (by default) -fn foo() { - // ... -} - -#[no_coverage] -fn bar() { - // ... -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/no-sanitize.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/no-sanitize.md deleted file mode 100644 index 28c683934d4e..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/no-sanitize.md +++ /dev/null @@ -1,29 +0,0 @@ -# `no_sanitize` - -The tracking issue for this feature is: [#39699] - -[#39699]: https://github.com/rust-lang/rust/issues/39699 - ------------------------- - -The `no_sanitize` attribute can be used to selectively disable sanitizer -instrumentation in an annotated function. This might be useful to: avoid -instrumentation overhead in a performance critical function, or avoid -instrumenting code that contains constructs unsupported by given sanitizer. - -The precise effect of this annotation depends on particular sanitizer in use. -For example, with `no_sanitize(thread)`, the thread sanitizer will no longer -instrument non-atomic store / load operations, but it will instrument atomic -operations to avoid reporting false positives and provide meaning full stack -traces. - -## Examples - -``` rust -#![feature(no_sanitize)] - -#[no_sanitize(address)] -fn foo() { - // ... -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/plugin.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/plugin.md deleted file mode 100644 index 040f46f8b7c7..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/plugin.md +++ /dev/null @@ -1,116 +0,0 @@ -# `plugin` - -The tracking issue for this feature is: [#29597] - -[#29597]: https://github.com/rust-lang/rust/issues/29597 - - -This feature is part of "compiler plugins." It will often be used with the -`rustc_private` feature. - ------------------------- - -`rustc` can load compiler plugins, which are user-provided libraries that -extend the compiler's behavior with new lint checks, etc. - -A plugin is a dynamic library crate with a designated *registrar* function that -registers extensions with `rustc`. Other crates can load these extensions using -the crate attribute `#![plugin(...)]`. See the -`rustc_driver::plugin` documentation for more about the -mechanics of defining and loading a plugin. - -In the vast majority of cases, a plugin should *only* be used through -`#![plugin]` and not through an `extern crate` item. Linking a plugin would -pull in all of librustc_ast and librustc as dependencies of your crate. This is -generally unwanted unless you are building another plugin. - -The usual practice is to put compiler plugins in their own crate, separate from -any `macro_rules!` macros or ordinary Rust code meant to be used by consumers -of a library. - -# Lint plugins - -Plugins can extend [Rust's lint -infrastructure](../../reference/attributes/diagnostics.md#lint-check-attributes) with -additional checks for code style, safety, etc. Now let's write a plugin -[`lint-plugin-test.rs`](https://github.com/rust-lang/rust/blob/master/src/test/ui-fulldeps/auxiliary/lint-plugin-test.rs) -that warns about any item named `lintme`. - -```rust,ignore (requires-stage-2) -#![feature(box_syntax, rustc_private)] - -extern crate rustc_ast; - -// Load rustc as a plugin to get macros -extern crate rustc_driver; -#[macro_use] -extern crate rustc_lint; -#[macro_use] -extern crate rustc_session; - -use rustc_driver::plugin::Registry; -use rustc_lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass}; -use rustc_ast::ast; -declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'"); - -declare_lint_pass!(Pass => [TEST_LINT]); - -impl EarlyLintPass for Pass { - fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) { - if it.ident.name.as_str() == "lintme" { - cx.lint(TEST_LINT, |lint| { - lint.build("item is named 'lintme'").set_span(it.span).emit() - }); - } - } -} - -#[no_mangle] -fn __rustc_plugin_registrar(reg: &mut Registry) { - reg.lint_store.register_lints(&[&TEST_LINT]); - reg.lint_store.register_early_pass(|| box Pass); -} -``` - -Then code like - -```rust,ignore (requires-plugin) -#![feature(plugin)] -#![plugin(lint_plugin_test)] - -fn lintme() { } -``` - -will produce a compiler warning: - -```txt -foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default -foo.rs:4 fn lintme() { } - ^~~~~~~~~~~~~~~ -``` - -The components of a lint plugin are: - -* one or more `declare_lint!` invocations, which define static `Lint` structs; - -* a struct holding any state needed by the lint pass (here, none); - -* a `LintPass` - implementation defining how to check each syntax element. A single - `LintPass` may call `span_lint` for several different `Lint`s, but should - register them all through the `get_lints` method. - -Lint passes are syntax traversals, but they run at a late stage of compilation -where type information is available. `rustc`'s [built-in -lints](https://github.com/rust-lang/rust/blob/master/src/librustc_session/lint/builtin.rs) -mostly use the same infrastructure as lint plugins, and provide examples of how -to access type information. - -Lints defined by plugins are controlled by the usual [attributes and compiler -flags](../../reference/attributes/diagnostics.md#lint-check-attributes), e.g. -`#[allow(test_lint)]` or `-A test-lint`. These identifiers are derived from the -first argument to `declare_lint!`, with appropriate case and punctuation -conversion. - -You can run `rustc -W help foo.rs` to see a list of lints known to `rustc`, -including those provided by plugins loaded by `foo.rs`. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/profiler-runtime.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/profiler-runtime.md deleted file mode 100644 index aee86f63952a..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/profiler-runtime.md +++ /dev/null @@ -1,5 +0,0 @@ -# `profiler_runtime` - -The tracking issue for this feature is: [#42524](https://github.com/rust-lang/rust/issues/42524). - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/raw-dylib.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/raw-dylib.md deleted file mode 100644 index 23fc5b3052d8..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/raw-dylib.md +++ /dev/null @@ -1,34 +0,0 @@ -# `raw_dylib` - -The tracking issue for this feature is: [#58713] - -[#58713]: https://github.com/rust-lang/rust/issues/58713 - ------------------------- - -The `raw_dylib` feature allows you to link against the implementations of functions in an `extern` -block without, on Windows, linking against an import library. - -```rust,ignore (partial-example) -#![feature(raw_dylib)] - -#[link(name="library", kind="raw-dylib")] -extern { - fn extern_function(x: i32); -} - -fn main() { - unsafe { - extern_function(14); - } -} -``` - -## Limitations - -Currently, this feature is only supported on `-windows-msvc` targets. Non-Windows platforms don't have import -libraries, and an incompatibility between LLVM and the BFD linker means that it is not currently supported on -`-windows-gnu` targets. - -On the `i686-pc-windows-msvc` target, this feature supports only the `cdecl`, `stdcall`, `system`, and `fastcall` -calling conventions. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/repr128.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/repr128.md deleted file mode 100644 index 146f50ee67b5..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/repr128.md +++ /dev/null @@ -1,18 +0,0 @@ -# `repr128` - -The tracking issue for this feature is: [#56071] - -[#56071]: https://github.com/rust-lang/rust/issues/56071 - ------------------------- - -The `repr128` feature adds support for `#[repr(u128)]` on `enum`s. - -```rust -#![feature(repr128)] - -#[repr(u128)] -enum Foo { - Bar(u64), -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/rustc-attrs.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/rustc-attrs.md deleted file mode 100644 index c67b806f06af..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/rustc-attrs.md +++ /dev/null @@ -1,53 +0,0 @@ -# `rustc_attrs` - -This feature has no tracking issue, and is therefore internal to -the compiler, not being intended for general use. - -Note: `rustc_attrs` enables many rustc-internal attributes and this page -only discuss a few of them. - ------------------------- - -The `rustc_attrs` feature allows debugging rustc type layouts by using -`#[rustc_layout(...)]` to debug layout at compile time (it even works -with `cargo check`) as an alternative to `rustc -Z print-type-sizes` -that is way more verbose. - -Options provided by `#[rustc_layout(...)]` are `debug`, `size`, `align`, -`abi`. Note that it only works on sized types without generics. - -## Examples - -```rust,compile_fail -#![feature(rustc_attrs)] - -#[rustc_layout(abi, size)] -pub enum X { - Y(u8, u8, u8), - Z(isize), -} -``` - -When that is compiled, the compiler will error with something like - -```text -error: abi: Aggregate { sized: true } - --> src/lib.rs:4:1 - | -4 | / pub enum T { -5 | | Y(u8, u8, u8), -6 | | Z(isize), -7 | | } - | |_^ - -error: size: Size { raw: 16 } - --> src/lib.rs:4:1 - | -4 | / pub enum T { -5 | | Y(u8, u8, u8), -6 | | Z(isize), -7 | | } - | |_^ - -error: aborting due to 2 previous errors -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/trait-alias.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/trait-alias.md deleted file mode 100644 index f1be053ddc42..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/trait-alias.md +++ /dev/null @@ -1,34 +0,0 @@ -# `trait_alias` - -The tracking issue for this feature is: [#41517] - -[#41517]: https://github.com/rust-lang/rust/issues/41517 - ------------------------- - -The `trait_alias` feature adds support for trait aliases. These allow aliases -to be created for one or more traits (currently just a single regular trait plus -any number of auto-traits), and used wherever traits would normally be used as -either bounds or trait objects. - -```rust -#![feature(trait_alias)] - -trait Foo = std::fmt::Debug + Send; -trait Bar = Foo + Sync; - -// Use trait alias as bound on type parameter. -fn foo(v: &T) { - println!("{:?}", v); -} - -pub fn main() { - foo(&1); - - // Use trait alias for trait objects. - let a: &Bar = &123; - println!("{:?}", a); - let b = Box::new(456) as Box; - println!("{:?}", b); -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/trait-upcasting.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/trait-upcasting.md deleted file mode 100644 index 3697ae38f9d8..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/trait-upcasting.md +++ /dev/null @@ -1,27 +0,0 @@ -# `trait_upcasting` - -The tracking issue for this feature is: [#65991] - -[#65991]: https://github.com/rust-lang/rust/issues/65991 - ------------------------- - -The `trait_upcasting` feature adds support for trait upcasting coercion. This allows a -trait object of type `dyn Bar` to be cast to a trait object of type `dyn Foo` -so long as `Bar: Foo`. - -```rust,edition2018 -#![feature(trait_upcasting)] -#![allow(incomplete_features)] - -trait Foo {} - -trait Bar: Foo {} - -impl Foo for i32 {} - -impl Bar for T {} - -let bar: &dyn Bar = &123; -let foo: &dyn Foo = bar; -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/transparent-unions.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/transparent-unions.md deleted file mode 100644 index 9b39b8971644..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/transparent-unions.md +++ /dev/null @@ -1,83 +0,0 @@ -# `transparent_unions` - -The tracking issue for this feature is [#60405] - -[#60405]: https://github.com/rust-lang/rust/issues/60405 - ----- - -The `transparent_unions` feature allows you mark `union`s as -`#[repr(transparent)]`. A `union` may be `#[repr(transparent)]` in exactly the -same conditions in which a `struct` may be `#[repr(transparent)]` (generally, -this means the `union` must have exactly one non-zero-sized field). Some -concrete illustrations follow. - -```rust -#![feature(transparent_unions)] - -// This union has the same representation as `f32`. -#[repr(transparent)] -union SingleFieldUnion { - field: f32, -} - -// This union has the same representation as `usize`. -#[repr(transparent)] -union MultiFieldUnion { - field: usize, - nothing: (), -} -``` - -For consistency with transparent `struct`s, `union`s must have exactly one -non-zero-sized field. If all fields are zero-sized, the `union` must not be -`#[repr(transparent)]`: - -```rust -#![feature(transparent_unions)] - -// This (non-transparent) union is already valid in stable Rust: -pub union GoodUnion { - pub nothing: (), -} - -// Error: transparent union needs exactly one non-zero-sized field, but has 0 -// #[repr(transparent)] -// pub union BadUnion { -// pub nothing: (), -// } -``` - -The one exception is if the `union` is generic over `T` and has a field of type -`T`, it may be `#[repr(transparent)]` even if `T` is a zero-sized type: - -```rust -#![feature(transparent_unions)] - -// This union has the same representation as `T`. -#[repr(transparent)] -pub union GenericUnion { // Unions with non-`Copy` fields are unstable. - pub field: T, - pub nothing: (), -} - -// This is okay even though `()` is a zero-sized type. -pub const THIS_IS_OKAY: GenericUnion<()> = GenericUnion { field: () }; -``` - -Like transarent `struct`s, a transparent `union` of type `U` has the same -layout, size, and ABI as its single non-ZST field. If it is generic over a type -`T`, and all its fields are ZSTs except for exactly one field of type `T`, then -it has the same layout and ABI as `T` (even if `T` is a ZST when monomorphized). - -Like transparent `struct`s, transparent `union`s are FFI-safe if and only if -their underlying representation type is also FFI-safe. - -A `union` may not be eligible for the same nonnull-style optimizations that a -`struct` or `enum` (with the same fields) are eligible for. Adding -`#[repr(transparent)]` to `union` does not change this. To give a more concrete -example, it is unspecified whether `size_of::()` is equal to -`size_of::>()`, where `T` is a `union` (regardless of whether or not -it is transparent). The Rust compiler is free to perform this optimization if -possible, but is not required to, and different compiler versions may differ in -their application of these optimizations. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/try-blocks.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/try-blocks.md deleted file mode 100644 index e342c260a739..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/try-blocks.md +++ /dev/null @@ -1,30 +0,0 @@ -# `try_blocks` - -The tracking issue for this feature is: [#31436] - -[#31436]: https://github.com/rust-lang/rust/issues/31436 - ------------------------- - -The `try_blocks` feature adds support for `try` blocks. A `try` -block creates a new scope one can use the `?` operator in. - -```rust,edition2018 -#![feature(try_blocks)] - -use std::num::ParseIntError; - -let result: Result = try { - "1".parse::()? - + "2".parse::()? - + "3".parse::()? -}; -assert_eq!(result, Ok(6)); - -let result: Result = try { - "1".parse::()? - + "foo".parse::()? - + "3".parse::()? -}; -assert!(result.is_err()); -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/type-changing-struct-update.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/type-changing-struct-update.md deleted file mode 100644 index 9909cf35b5b5..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/type-changing-struct-update.md +++ /dev/null @@ -1,33 +0,0 @@ -# `type_changing_struct_update` - -The tracking issue for this feature is: [#86555] - -[#86555]: https://github.com/rust-lang/rust/issues/86555 - ------------------------- - -This implements [RFC2528]. When turned on, you can create instances of the same struct -that have different generic type or lifetime parameters. - -[RFC2528]: https://github.com/rust-lang/rfcs/blob/master/text/2528-type-changing-struct-update-syntax.md - -```rust -#![allow(unused_variables, dead_code)] -#![feature(type_changing_struct_update)] - -fn main () { - struct Foo { - field1: T, - field2: U, - } - - let base: Foo = Foo { - field1: String::from("hello"), - field2: 1234, - }; - let updated: Foo = Foo { - field1: 3.14, - ..base - }; -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/unboxed-closures.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/unboxed-closures.md deleted file mode 100644 index e4113d72d091..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/unboxed-closures.md +++ /dev/null @@ -1,25 +0,0 @@ -# `unboxed_closures` - -The tracking issue for this feature is [#29625] - -See Also: [`fn_traits`](../library-features/fn-traits.md) - -[#29625]: https://github.com/rust-lang/rust/issues/29625 - ----- - -The `unboxed_closures` feature allows you to write functions using the `"rust-call"` ABI, -required for implementing the [`Fn*`] family of traits. `"rust-call"` functions must have -exactly one (non self) argument, a tuple representing the argument list. - -[`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html - -```rust -#![feature(unboxed_closures)] - -extern "rust-call" fn add_args(args: (u32, u32)) -> u32 { - args.0 + args.1 -} - -fn main() {} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/unsized-locals.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/unsized-locals.md deleted file mode 100644 index d5b01a3d6168..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/unsized-locals.md +++ /dev/null @@ -1,175 +0,0 @@ -# `unsized_locals` - -The tracking issue for this feature is: [#48055] - -[#48055]: https://github.com/rust-lang/rust/issues/48055 - ------------------------- - -This implements [RFC1909]. When turned on, you can have unsized arguments and locals: - -[RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-unsized-rvalues.md - -```rust -#![allow(incomplete_features)] -#![feature(unsized_locals, unsized_fn_params)] - -use std::any::Any; - -fn main() { - let x: Box = Box::new(42); - let x: dyn Any = *x; - // ^ unsized local variable - // ^^ unsized temporary - foo(x); -} - -fn foo(_: dyn Any) {} -// ^^^^^^ unsized argument -``` - -The RFC still forbids the following unsized expressions: - -```rust,compile_fail -#![feature(unsized_locals)] - -use std::any::Any; - -struct MyStruct { - content: T, -} - -struct MyTupleStruct(T); - -fn answer() -> Box { - Box::new(42) -} - -fn main() { - // You CANNOT have unsized statics. - static X: dyn Any = *answer(); // ERROR - const Y: dyn Any = *answer(); // ERROR - - // You CANNOT have struct initialized unsized. - MyStruct { content: *answer() }; // ERROR - MyTupleStruct(*answer()); // ERROR - (42, *answer()); // ERROR - - // You CANNOT have unsized return types. - fn my_function() -> dyn Any { *answer() } // ERROR - - // You CAN have unsized local variables... - let mut x: dyn Any = *answer(); // OK - // ...but you CANNOT reassign to them. - x = *answer(); // ERROR - - // You CANNOT even initialize them separately. - let y: dyn Any; // OK - y = *answer(); // ERROR - - // Not mentioned in the RFC, but by-move captured variables are also Sized. - let x: dyn Any = *answer(); - (move || { // ERROR - let y = x; - })(); - - // You CAN create a closure with unsized arguments, - // but you CANNOT call it. - // This is an implementation detail and may be changed in the future. - let f = |x: dyn Any| {}; - f(*answer()); // ERROR -} -``` - -## By-value trait objects - -With this feature, you can have by-value `self` arguments without `Self: Sized` bounds. - -```rust -#![feature(unsized_fn_params)] - -trait Foo { - fn foo(self) {} -} - -impl Foo for T {} - -fn main() { - let slice: Box<[i32]> = Box::new([1, 2, 3]); - <[i32] as Foo>::foo(*slice); -} -``` - -And `Foo` will also be object-safe. - -```rust -#![feature(unsized_fn_params)] - -trait Foo { - fn foo(self) {} -} - -impl Foo for T {} - -fn main () { - let slice: Box = Box::new([1, 2, 3]); - // doesn't compile yet - ::foo(*slice); -} -``` - -One of the objectives of this feature is to allow `Box`. - -## Variable length arrays - -The RFC also describes an extension to the array literal syntax: `[e; dyn n]`. In the syntax, `n` isn't necessarily a constant expression. The array is dynamically allocated on the stack and has the type of `[T]`, instead of `[T; n]`. - -```rust,ignore (not-yet-implemented) -#![feature(unsized_locals)] - -fn mergesort(a: &mut [T]) { - let mut tmp = [T; dyn a.len()]; - // ... -} - -fn main() { - let mut a = [3, 1, 5, 6]; - mergesort(&mut a); - assert_eq!(a, [1, 3, 5, 6]); -} -``` - -VLAs are not implemented yet. The syntax isn't final, either. We may need an alternative syntax for Rust 2015 because, in Rust 2015, expressions like `[e; dyn(1)]` would be ambiguous. One possible alternative proposed in the RFC is `[e; n]`: if `n` captures one or more local variables, then it is considered as `[e; dyn n]`. - -## Advisory on stack usage - -It's advised not to casually use the `#![feature(unsized_locals)]` feature. Typical use-cases are: - -- When you need a by-value trait objects. -- When you really need a fast allocation of small temporary arrays. - -Another pitfall is repetitive allocation and temporaries. Currently the compiler simply extends the stack frame every time it encounters an unsized assignment. So for example, the code - -```rust -#![feature(unsized_locals)] - -fn main() { - let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]); - let _x = {{{{{{{{{{*x}}}}}}}}}}; -} -``` - -and the code - -```rust -#![feature(unsized_locals)] - -fn main() { - for _ in 0..10 { - let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]); - let _x = *x; - } -} -``` - -will unnecessarily extend the stack frame. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/language-features/unsized-tuple-coercion.md b/tools/bookrunner/rust-doc/unstable-book/src/language-features/unsized-tuple-coercion.md deleted file mode 100644 index 310c8d962948..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/language-features/unsized-tuple-coercion.md +++ /dev/null @@ -1,27 +0,0 @@ -# `unsized_tuple_coercion` - -The tracking issue for this feature is: [#42877] - -[#42877]: https://github.com/rust-lang/rust/issues/42877 - ------------------------- - -This is a part of [RFC0401]. According to the RFC, there should be an implementation like this: - -```rust,ignore (partial-example) -impl<..., T, U: ?Sized> Unsized<(..., U)> for (..., T) where T: Unsized {} -``` - -This implementation is currently gated behind `#[feature(unsized_tuple_coercion)]` to avoid insta-stability. Therefore you can use it like this: - -```rust -#![feature(unsized_tuple_coercion)] - -fn main() { - let x : ([i32; 3], [i32; 3]) = ([1, 2, 3], [4, 5, 6]); - let y : &([i32; 3], [i32]) = &x; - assert_eq!(y.1[0], 4); -} -``` - -[RFC0401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features.md deleted file mode 100644 index 9f537e26132b..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features.md +++ /dev/null @@ -1 +0,0 @@ -# Library Features diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/allocator-api.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/allocator-api.md deleted file mode 100644 index 9f045ce08a43..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/allocator-api.md +++ /dev/null @@ -1,15 +0,0 @@ -# `allocator_api` - -The tracking issue for this feature is [#32838] - -[#32838]: https://github.com/rust-lang/rust/issues/32838 - ------------------------- - -Sometimes you want the memory for one collection to use a different -allocator than the memory for another collection. In this case, -replacing the global allocator is not a workable option. Instead, -you need to pass in an instance of an `AllocRef` to each collection -for which you want a custom allocator. - -TBD diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/c-variadic.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/c-variadic.md deleted file mode 100644 index 77762116e6b1..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/c-variadic.md +++ /dev/null @@ -1,26 +0,0 @@ -# `c_variadic` - -The tracking issue for this feature is: [#44930] - -[#44930]: https://github.com/rust-lang/rust/issues/44930 - ------------------------- - -The `c_variadic` library feature exposes the `VaList` structure, -Rust's analogue of C's `va_list` type. - -## Examples - -```rust -#![feature(c_variadic)] - -use std::ffi::VaList; - -pub unsafe extern "C" fn vadd(n: usize, mut args: VaList) -> usize { - let mut sum = 0; - for _ in 0..n { - sum += args.arg::(); - } - sum -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/c-void-variant.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/c-void-variant.md deleted file mode 100644 index a2fdc9936300..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/c-void-variant.md +++ /dev/null @@ -1,5 +0,0 @@ -# `c_void_variant` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/char-error-internals.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/char-error-internals.md deleted file mode 100644 index 8013b4988e14..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/char-error-internals.md +++ /dev/null @@ -1,5 +0,0 @@ -# `char_error_internals` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/concat-idents.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/concat-idents.md deleted file mode 100644 index 73f6cfa21787..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/concat-idents.md +++ /dev/null @@ -1,22 +0,0 @@ -# `concat_idents` - -The tracking issue for this feature is: [#29599] - -[#29599]: https://github.com/rust-lang/rust/issues/29599 - ------------------------- - -The `concat_idents` feature adds a macro for concatenating multiple identifiers -into one identifier. - -## Examples - -```rust -#![feature(concat_idents)] - -fn main() { - fn foobar() -> u32 { 23 } - let f = concat_idents!(foo, bar); - assert_eq!(f(), 23); -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/core-intrinsics.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/core-intrinsics.md deleted file mode 100644 index 28ad3525ef7a..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/core-intrinsics.md +++ /dev/null @@ -1,5 +0,0 @@ -# `core_intrinsics` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/core-panic.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/core-panic.md deleted file mode 100644 index c197588404c9..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/core-panic.md +++ /dev/null @@ -1,5 +0,0 @@ -# `core_panic` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/core-private-bignum.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/core-private-bignum.md deleted file mode 100644 index f85811c545e4..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/core-private-bignum.md +++ /dev/null @@ -1,5 +0,0 @@ -# `core_private_bignum` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/core-private-diy-float.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/core-private-diy-float.md deleted file mode 100644 index 8465921d673b..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/core-private-diy-float.md +++ /dev/null @@ -1,5 +0,0 @@ -# `core_private_diy_float` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/dec2flt.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/dec2flt.md deleted file mode 100644 index 311ab4adcfd7..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/dec2flt.md +++ /dev/null @@ -1,5 +0,0 @@ -# `dec2flt` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/default-free-fn.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/default-free-fn.md deleted file mode 100644 index d40a27dddf36..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/default-free-fn.md +++ /dev/null @@ -1,47 +0,0 @@ -# `default_free_fn` - -The tracking issue for this feature is: [#73014] - -[#73014]: https://github.com/rust-lang/rust/issues/73014 - ------------------------- - -Adds a free `default()` function to the `std::default` module. This function -just forwards to [`Default::default()`], but may remove repetition of the word -"default" from the call site. - -[`Default::default()`]: https://doc.rust-lang.org/nightly/std/default/trait.Default.html#tymethod.default - -Here is an example: - -```rust -#![feature(default_free_fn)] -use std::default::default; - -#[derive(Default)] -struct AppConfig { - foo: FooConfig, - bar: BarConfig, -} - -#[derive(Default)] -struct FooConfig { - foo: i32, -} - -#[derive(Default)] -struct BarConfig { - bar: f32, - baz: u8, -} - -fn main() { - let options = AppConfig { - foo: default(), - bar: BarConfig { - bar: 10.1, - ..default() - }, - }; -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/derive-clone-copy.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/derive-clone-copy.md deleted file mode 100644 index cc603911cbd2..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/derive-clone-copy.md +++ /dev/null @@ -1,5 +0,0 @@ -# `derive_clone_copy` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/derive-eq.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/derive-eq.md deleted file mode 100644 index 68a275f5419d..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/derive-eq.md +++ /dev/null @@ -1,5 +0,0 @@ -# `derive_eq` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/fd-read.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/fd-read.md deleted file mode 100644 index e78d4330abfc..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/fd-read.md +++ /dev/null @@ -1,5 +0,0 @@ -# `fd_read` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/fd.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/fd.md deleted file mode 100644 index 0414244285ba..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/fd.md +++ /dev/null @@ -1,5 +0,0 @@ -# `fd` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/flt2dec.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/flt2dec.md deleted file mode 100644 index 15e62a3a7dad..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/flt2dec.md +++ /dev/null @@ -1,5 +0,0 @@ -# `flt2dec` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/fmt-internals.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/fmt-internals.md deleted file mode 100644 index 7cbe3c89a644..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/fmt-internals.md +++ /dev/null @@ -1,5 +0,0 @@ -# `fmt_internals` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/fn-traits.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/fn-traits.md deleted file mode 100644 index 29a8aecee6c2..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/fn-traits.md +++ /dev/null @@ -1,35 +0,0 @@ -# `fn_traits` - -The tracking issue for this feature is [#29625] - -See Also: [`unboxed_closures`](../language-features/unboxed-closures.md) - -[#29625]: https://github.com/rust-lang/rust/issues/29625 - ----- - -The `fn_traits` feature allows for implementation of the [`Fn*`] traits -for creating custom closure-like types. - -[`Fn*`]: https://doc.rust-lang.org/std/ops/trait.Fn.html - -```rust -#![feature(unboxed_closures)] -#![feature(fn_traits)] - -struct Adder { - a: u32 -} - -impl FnOnce<(u32, )> for Adder { - type Output = u32; - extern "rust-call" fn call_once(self, b: (u32, )) -> Self::Output { - self.a + b.0 - } -} - -fn main() { - let adder = Adder { a: 3 }; - assert_eq!(adder(2), 5); -} -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/int-error-internals.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/int-error-internals.md deleted file mode 100644 index 402e4fa5ef6d..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/int-error-internals.md +++ /dev/null @@ -1,5 +0,0 @@ -# `int_error_internals` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/internal-output-capture.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/internal-output-capture.md deleted file mode 100644 index 7e1241fce985..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/internal-output-capture.md +++ /dev/null @@ -1,5 +0,0 @@ -# `internal_output_capture` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/is-sorted.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/is-sorted.md deleted file mode 100644 index e3b7dc3b28eb..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/is-sorted.md +++ /dev/null @@ -1,11 +0,0 @@ -# `is_sorted` - -The tracking issue for this feature is: [#53485] - -[#53485]: https://github.com/rust-lang/rust/issues/53485 - ------------------------- - -Add the methods `is_sorted`, `is_sorted_by` and `is_sorted_by_key` to `[T]`; -add the methods `is_sorted`, `is_sorted_by` and `is_sorted_by_key` to -`Iterator`. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/libstd-sys-internals.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/libstd-sys-internals.md deleted file mode 100644 index 1b53faa8a007..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/libstd-sys-internals.md +++ /dev/null @@ -1,5 +0,0 @@ -# `libstd_sys_internals` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/libstd-thread-internals.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/libstd-thread-internals.md deleted file mode 100644 index b682d12e7cdd..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/libstd-thread-internals.md +++ /dev/null @@ -1,5 +0,0 @@ -# `libstd_thread_internals` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/print-internals.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/print-internals.md deleted file mode 100644 index a68557872af5..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/print-internals.md +++ /dev/null @@ -1,5 +0,0 @@ -# `print_internals` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/profiler-runtime-lib.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/profiler-runtime-lib.md deleted file mode 100644 index a01f1e73ab40..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/profiler-runtime-lib.md +++ /dev/null @@ -1,5 +0,0 @@ -# `profiler_runtime_lib` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/rt.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/rt.md deleted file mode 100644 index 007acc207a65..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/rt.md +++ /dev/null @@ -1,5 +0,0 @@ -# `rt` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/sort-internals.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/sort-internals.md deleted file mode 100644 index 6f2385e53008..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/sort-internals.md +++ /dev/null @@ -1,5 +0,0 @@ -# `sort_internals` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/str-internals.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/str-internals.md deleted file mode 100644 index af8ef056dbe2..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/str-internals.md +++ /dev/null @@ -1,5 +0,0 @@ -# `str_internals` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/test.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/test.md deleted file mode 100644 index c99584e5fb39..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/test.md +++ /dev/null @@ -1,158 +0,0 @@ -# `test` - -The tracking issue for this feature is: None. - ------------------------- - -The internals of the `test` crate are unstable, behind the `test` flag. The -most widely used part of the `test` crate are benchmark tests, which can test -the performance of your code. Let's make our `src/lib.rs` look like this -(comments elided): - -```rust,no_run -#![feature(test)] - -extern crate test; - -pub fn add_two(a: i32) -> i32 { - a + 2 -} - -#[cfg(test)] -mod tests { - use super::*; - use test::Bencher; - - #[test] - fn it_works() { - assert_eq!(4, add_two(2)); - } - - #[bench] - fn bench_add_two(b: &mut Bencher) { - b.iter(|| add_two(2)); - } -} -``` - -Note the `test` feature gate, which enables this unstable feature. - -We've imported the `test` crate, which contains our benchmarking support. -We have a new function as well, with the `bench` attribute. Unlike regular -tests, which take no arguments, benchmark tests take a `&mut Bencher`. This -`Bencher` provides an `iter` method, which takes a closure. This closure -contains the code we'd like to benchmark. - -We can run benchmark tests with `cargo bench`: - -```bash -$ cargo bench - Compiling adder v0.0.1 (file:///home/steve/tmp/adder) - Running target/release/adder-91b3e234d4ed382a - -running 2 tests -test tests::it_works ... ignored -test tests::bench_add_two ... bench: 1 ns/iter (+/- 0) - -test result: ok. 0 passed; 0 failed; 1 ignored; 1 measured -``` - -Our non-benchmark test was ignored. You may have noticed that `cargo bench` -takes a bit longer than `cargo test`. This is because Rust runs our benchmark -a number of times, and then takes the average. Because we're doing so little -work in this example, we have a `1 ns/iter (+/- 0)`, but this would show -the variance if there was one. - -Advice on writing benchmarks: - - -* Move setup code outside the `iter` loop; only put the part you want to measure inside -* Make the code do "the same thing" on each iteration; do not accumulate or change state -* Make the outer function idempotent too; the benchmark runner is likely to run - it many times -* Make the inner `iter` loop short and fast so benchmark runs are fast and the - calibrator can adjust the run-length at fine resolution -* Make the code in the `iter` loop do something simple, to assist in pinpointing - performance improvements (or regressions) - -## Gotcha: optimizations - -There's another tricky part to writing benchmarks: benchmarks compiled with -optimizations activated can be dramatically changed by the optimizer so that -the benchmark is no longer benchmarking what one expects. For example, the -compiler might recognize that some calculation has no external effects and -remove it entirely. - -```rust,no_run -#![feature(test)] - -extern crate test; -use test::Bencher; - -#[bench] -fn bench_xor_1000_ints(b: &mut Bencher) { - b.iter(|| { - (0..1000).fold(0, |old, new| old ^ new); - }); -} -``` - -gives the following results - -```text -running 1 test -test bench_xor_1000_ints ... bench: 0 ns/iter (+/- 0) - -test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured -``` - -The benchmarking runner offers two ways to avoid this. Either, the closure that -the `iter` method receives can return an arbitrary value which forces the -optimizer to consider the result used and ensures it cannot remove the -computation entirely. This could be done for the example above by adjusting the -`b.iter` call to - -```rust -# struct X; -# impl X { fn iter(&self, _: F) where F: FnMut() -> T {} } let b = X; -b.iter(|| { - // Note lack of `;` (could also use an explicit `return`). - (0..1000).fold(0, |old, new| old ^ new) -}); -``` - -Or, the other option is to call the generic `test::black_box` function, which -is an opaque "black box" to the optimizer and so forces it to consider any -argument as used. - -```rust -#![feature(test)] - -extern crate test; - -# fn main() { -# struct X; -# impl X { fn iter(&self, _: F) where F: FnMut() -> T {} } let b = X; -b.iter(|| { - let n = test::black_box(1000); - - (0..n).fold(0, |a, b| a ^ b) -}) -# } -``` - -Neither of these read or modify the value, and are very cheap for small values. -Larger values can be passed indirectly to reduce overhead (e.g. -`black_box(&huge_struct)`). - -Performing either of the above changes gives the following benchmarking results - -```text -running 1 test -test bench_xor_1000_ints ... bench: 131 ns/iter (+/- 3) - -test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured -``` - -However, the optimizer can still modify a testcase in an undesirable manner -even when using either of the above. diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/thread-local-internals.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/thread-local-internals.md deleted file mode 100644 index e1cdcc339d22..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/thread-local-internals.md +++ /dev/null @@ -1,5 +0,0 @@ -# `thread_local_internals` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/trace-macros.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/trace-macros.md deleted file mode 100644 index 41aa286e69bf..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/trace-macros.md +++ /dev/null @@ -1,39 +0,0 @@ -# `trace_macros` - -The tracking issue for this feature is [#29598]. - -[#29598]: https://github.com/rust-lang/rust/issues/29598 - ------------------------- - -With `trace_macros` you can trace the expansion of macros in your code. - -## Examples - -```rust -#![feature(trace_macros)] - -fn main() { - trace_macros!(true); - println!("Hello, Rust!"); - trace_macros!(false); -} -``` - -The `cargo build` output: - -```txt -note: trace_macro - --> src/main.rs:5:5 - | -5 | println!("Hello, Rust!"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: expanding `println! { "Hello, Rust!" }` - = note: to `print ! ( concat ! ( "Hello, Rust!" , "\n" ) )` - = note: expanding `print! { concat ! ( "Hello, Rust!" , "\n" ) }` - = note: to `$crate :: io :: _print ( format_args ! ( concat ! ( "Hello, Rust!" , "\n" ) ) - )` - - Finished dev [unoptimized + debuginfo] target(s) in 0.60 secs -``` diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/update-panic-count.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/update-panic-count.md deleted file mode 100644 index d315647ba104..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/update-panic-count.md +++ /dev/null @@ -1,5 +0,0 @@ -# `update_panic_count` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-c.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-c.md deleted file mode 100644 index 3f833eb3d093..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-c.md +++ /dev/null @@ -1,5 +0,0 @@ -# `windows_c` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-handle.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-handle.md deleted file mode 100644 index f47a8425045b..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-handle.md +++ /dev/null @@ -1,5 +0,0 @@ -# `windows_handle` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-net.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-net.md deleted file mode 100644 index 174960d4f004..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-net.md +++ /dev/null @@ -1,5 +0,0 @@ -# `windows_net` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-stdio.md b/tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-stdio.md deleted file mode 100644 index 4d361442386a..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/library-features/windows-stdio.md +++ /dev/null @@ -1,5 +0,0 @@ -# `windows_stdio` - -This feature is internal to the Rust compiler and is not intended for general use. - ------------------------- diff --git a/tools/bookrunner/rust-doc/unstable-book/src/the-unstable-book.md b/tools/bookrunner/rust-doc/unstable-book/src/the-unstable-book.md deleted file mode 100644 index 554c52c3c9c2..000000000000 --- a/tools/bookrunner/rust-doc/unstable-book/src/the-unstable-book.md +++ /dev/null @@ -1,22 +0,0 @@ -# The Unstable Book - -Welcome to the Unstable Book! This book consists of a number of chapters, -each one organized by a "feature flag." That is, when using an unstable -feature of Rust, you must use a flag, like this: - -```rust -#![feature(box_syntax)] - -fn main() { - let five = box 5; -} -``` - -The `box_syntax` feature [has a chapter][box] describing how to use it. - -[box]: language-features/box-syntax.md - -Because this documentation relates to unstable features, we make no guarantees -that what is contained here is accurate or up to date. It's developed on a -best-effort basis. Each page will have a link to its tracking issue with the -latest developments; you might want to check those as well. diff --git a/tools/bookrunner/src/bookrunner.rs b/tools/bookrunner/src/bookrunner.rs deleted file mode 100644 index ecce5bd3e779..000000000000 --- a/tools/bookrunner/src/bookrunner.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT -//! Data structures representing the book report and their utilities. - -use std::fmt::{Display, Formatter, Result, Write}; - -/// This data structure holds the results of running a test or a suite. -#[derive(Clone, Debug)] -pub struct Node { - pub name: String, - pub num_pass: u32, - pub num_fail: u32, -} - -impl Node { - /// Creates a new test [`Node`]. - pub fn new(name: String, num_pass: u32, num_fail: u32) -> Node { - Node { name, num_pass, num_fail } - } -} - -/// Tree data structure representing a book report. `children` -/// represent sub-tests and sub-suites of the current test suite. This tree -/// structure allows us to collect and display a summary for test results in an -/// organized manner. -#[derive(Clone, Debug)] -pub struct Tree { - pub data: Node, - pub children: Vec, -} - -impl Tree { - /// Creates a new [`Tree`] representing a book report or a part of it. - pub fn new(data: Node, children: Vec) -> Tree { - Tree { data, children } - } - - /// Merges two trees, if their root have equal node names, and returns the - /// merged tree. - pub fn merge(mut l: Tree, r: Tree) -> Option { - if l.data.name != r.data.name { - return None; - } - // For each subtree of `r`... - for cnr in r.children { - // Look for a subtree of `l` with an equal root node name. - let index = l.children.iter().position(|cnl| cnl.data.name == cnr.data.name); - if let Some(index) = index { - // If you find one, merge it with `r`'s subtree. - let cnl = l.children.remove(index); - l.children.insert(index, Tree::merge(cnl, cnr)?); - } else { - // Otherwise, `r`'s subtree is new. So, add it to `l`'s - // list of subtrees. - l.children.push(cnr); - } - } - Some(Tree::new( - Node::new( - l.data.name, - l.data.num_pass + r.data.num_pass, - l.data.num_fail + r.data.num_fail, - ), - l.children, - )) - } - - /// A helper format function that indents each level of the tree. - fn fmt_aux(&self, p: usize, f: &mut Formatter<'_>) -> Result { - // Do not print line numbers. - if self.children.is_empty() { - return Ok(()); - } - // Write `p` spaces into the formatter. - f.write_fmt(format_args!("{:p$}", ""))?; - f.write_str(&self.data.name)?; - if self.data.num_pass > 0 { - f.write_fmt(format_args!(" ✔️ {}", self.data.num_pass))?; - } - if self.data.num_fail > 0 { - f.write_fmt(format_args!(" ❌ {}", self.data.num_fail))?; - } - f.write_char('\n')?; - for cn in &self.children { - cn.fmt_aux(p + 2, f)?; - } - Ok(()) - } -} - -impl Display for Tree { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - self.fmt_aux(0, f) - } -} diff --git a/tools/bookrunner/src/books.rs b/tools/bookrunner/src/books.rs deleted file mode 100644 index be0c39497795..000000000000 --- a/tools/bookrunner/src/books.rs +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT -//! Utilities to extract examples from Rust books, run them through Kani, and -//! display their results. - -extern crate rustc_span; - -use crate::{ - bookrunner, - litani::{Litani, LitaniPipeline, LitaniRun}, - util::{self, FailStep, TestProps}, -}; -use inflector::cases::{snakecase::to_snake_case, titlecase::to_title_case}; -use pulldown_cmark::{Event, Parser, Tag, TagEnd}; -use rustc_span::edition::Edition; -use rustdoc::{ - doctest::{make_test, Tester}, - html::markdown::{find_testable_code, ErrorCodes, Ignore, LangString}, -}; -use std::{ - collections::{HashMap, HashSet}, - ffi::OsStr, - fmt::Write, - fs, - io::BufReader, - iter::FromIterator, - path::{Path, PathBuf}, - str::FromStr, -}; -use walkdir::WalkDir; - -// Books may include a `SUMMARY.md` file or not. If they do, the info in -// `SummaryData` is helpful to parse the hierarchy, otherwise we use a -// `DirectoryData` structure - -// Data needed for parsing a book with a summary file -struct SummaryData { - // Path to the summary file - summary_path: PathBuf, - // Line that indicates the start of the summary section - summary_start: String, -} - -// Data needed for parsing book without a summary file -struct DirectoryData { - // Directory to be processed, starting from root of the book - src: PathBuf, - // Directory where the examples extracted from the book should reside - dest: PathBuf, -} - -// Data structure representing a Rust book -struct Book { - // Name of the book - name: String, - // Default Rust edition - default_edition: Edition, - // Data about the summary file - summary_data: Option, - // Data about the source/destination directories - directory_data: Option, - // Path to the `book.toml` file - toml_path: PathBuf, - // The hierarchy map used for example extraction - hierarchy: HashMap, -} - -impl Book { - /// Parse the chapter/section hierarchy and set the default edition - fn parse_hierarchy(&mut self) { - if self.summary_data.is_some() { - assert!(self.directory_data.is_none()); - self.parse_hierarchy_with_summary(); - } else { - assert!(self.directory_data.is_some()); - self.parse_hierarchy_without_summary(); - } - self.default_edition = self.get_rust_edition().unwrap_or(Edition::Edition2015); - } - - /// Parses the chapter/section hierarchy in the markdown file specified by - /// `summary_path` and returns a mapping from markdown files containing rust - /// code to corresponding directories where the extracted rust code should - /// reside. - fn parse_hierarchy_with_summary(&mut self) { - let summary_path = &self.summary_data.as_ref().unwrap().summary_path; - let summary_start = &self.summary_data.as_ref().unwrap().summary_start; - let summary_dir = summary_path.parent().unwrap().to_path_buf(); - let summary = fs::read_to_string(summary_path.clone()).unwrap(); - assert!( - summary.starts_with(summary_start.as_str()), - "Error: The start of {} summary file changed.", - self.name - ); - // Skip the `start` of the summary. - let n = Parser::new(summary_start.as_str()).count(); - let parser = Parser::new(&summary).skip(n); - // Set `self.name` as the root of the hierarchical path. - let mut hierarchy_path: PathBuf = - ["tests", "bookrunner", "books", self.name.as_str()].iter().collect(); - let mut prev_event_is_text_or_code = false; - let mut current_link_url = String::from(""); - for event in parser { - match event { - Event::End(TagEnd::Item) => { - // Pop the current chapter/section from the hierarchy once - // we are done processing it and its subsections. - hierarchy_path.pop(); - prev_event_is_text_or_code = false; - } - Event::Start(Tag::Link { dest_url, .. }) => { - current_link_url = dest_url.into_string(); - } - Event::End(TagEnd::Link) => { - // At the start of the link tag, the hierarchy does not yet - // contain the title of the current chapter/section. So, we wait - // for the end of the link tag before adding the path and - // hierarchy of the current chapter/section to the map. - let mut full_path = summary_dir.clone(); - full_path.extend(current_link_url.split('/')); - self.hierarchy.insert(full_path, hierarchy_path.clone()); - prev_event_is_text_or_code = false; - } - Event::Text(text) | Event::Code(text) => { - // Remove characters that are problematic to the file system or - // terminal. - let text = text.replace(&['/', '(', ')', '\''][..], "_"); - // Does the chapter/section title contain normal text and inline - // code? - if prev_event_is_text_or_code { - // If so, we combine them into one hierarchy level. - let prev_text = - hierarchy_path.file_name().unwrap().to_str().unwrap().to_string(); - hierarchy_path.pop(); - hierarchy_path.push(format!("{prev_text}{text}")); - } else { - // If not, add the current title to the hierarchy. - hierarchy_path.push(&text); - } - prev_event_is_text_or_code = true; - } - _ => (), - } - } - } - - /// Parses books that do not have a `SUMMARY.md` file (i.e., a table of - /// contents). We parse them manually and make a "best effort" to make it - /// look like the online version. - fn parse_hierarchy_without_summary(&mut self) { - let directory_data = self.directory_data.as_ref().unwrap(); - let src = &directory_data.src; - let dest = &directory_data.dest; - let mut src_prefix: PathBuf = src.clone(); - let mut dest_prefix: PathBuf = dest.clone(); - for entry in WalkDir::new(&src_prefix) { - let entry = entry.unwrap().into_path(); - // `WalkDir` returns entries in a depth-first fashion. Once we are done - // processing a directory, it will jump to a different child entry of a - // predecessor. To copy examples to the correct location, we need to - // know how far back we jumped and update `dest_prefix` accordingly. - while !entry.starts_with(&src_prefix) { - src_prefix.pop(); - dest_prefix.pop(); - } - if entry.is_dir() { - src_prefix.push(entry.file_name().unwrap()); - // Follow the book's title case format for directories. - dest_prefix.push(to_title_case(entry.file_name().unwrap().to_str().unwrap())); - } else { - // Only process markdown files. - if entry.extension() == Some(OsStr::new("md")) { - let entry_stem = entry.file_stem().unwrap().to_str().unwrap(); - // If a file has the stem name as a sibling directory... - if src_prefix.join(entry.file_stem().unwrap()).exists() { - // Its extracted examples should reside under that - // directory. - self.hierarchy - .insert(entry.clone(), dest_prefix.join(to_title_case(entry_stem))); - } else { - // Otherwise, follow the book's snake case format for files. - self.hierarchy - .insert(entry.clone(), dest_prefix.join(to_snake_case(entry_stem))); - } - } - } - } - } - - // Get the Rust edition from the `book.toml` file - fn get_rust_edition(&self) -> Option { - let file = fs::read_to_string(&self.toml_path).unwrap(); - let toml_data: toml::Value = toml::from_str(&file).unwrap(); - // The Rust edition is specified in the `rust.edition` attribute - let rust_block = toml_data.get("rust")?; - let edition_attr = rust_block.get("edition")?; - let edition_str = edition_attr.as_str()?; - Some(Edition::from_str(edition_str).unwrap()) - } - - /// Extracts examples from the markdown files specified by each key in the given - /// `map`, pre-processes those examples, and saves them in the directory - /// specified by the corresponding value. - fn extract_examples(&self) { - let mut config_paths = get_config_paths(self.name.as_str()); - for (par_from, par_to) in &self.hierarchy { - extract(par_from, par_to, &mut config_paths, self.default_edition); - } - if !config_paths.is_empty() { - panic!( - "Error: The examples corresponding to the following config files \ - were not encountered in the pre-processing step:\n{}This is most \ - likely because the line numbers of the config files are not in \ - sync with the line numbers of the corresponding code blocks in \ - the latest versions of the Rust books. Please update the line \ - numbers of the config files and rerun the program.", - paths_to_string(config_paths) - ); - } - } -} -/// Set up [The Rust Reference](https://doc.rust-lang.org/nightly/reference) -/// book. -fn setup_reference_book() -> Book { - let summary_data = SummaryData { - summary_start: "# The Rust Reference\n\n[Introduction](introduction.md)".to_string(), - summary_path: ["tools", "bookrunner", "rust-doc", "reference", "src", "SUMMARY.md"] - .iter() - .collect(), - }; - Book { - name: "The Rust Reference".to_string(), - summary_data: Some(summary_data), - directory_data: None, - toml_path: ["tools", "bookrunner", "rust-doc", "reference", "book.toml"].iter().collect(), - hierarchy: HashMap::from_iter([( - ["tools", "bookrunner", "rust-doc", "reference", "src", "introduction.md"] - .iter() - .collect(), - ["tests", "bookrunner", "books", "The Rust Reference", "Introduction"].iter().collect(), - )]), - default_edition: Edition::Edition2015, - } -} - -/// Set up [The Rustonomicon](https://doc.rust-lang.org/nightly/nomicon) book. -fn setup_nomicon_book() -> Book { - let summary_data = SummaryData { - summary_path: ["tools", "bookrunner", "rust-doc", "nomicon", "src", "SUMMARY.md"] - .iter() - .collect(), - summary_start: "# Summary\n\n[Introduction](intro.md)".to_string(), - }; - Book { - name: "The Rustonomicon".to_string(), - summary_data: Some(summary_data), - directory_data: None, - toml_path: ["tools", "bookrunner", "rust-doc", "nomicon", "book.toml"].iter().collect(), - hierarchy: HashMap::from_iter([( - ["tools", "bookrunner", "rust-doc", "nomicon", "src", "intro.md"].iter().collect(), - ["tests", "bookrunner", "books", "The Rustonomicon", "Introduction"].iter().collect(), - )]), - default_edition: Edition::Edition2015, - } -} - -/// Set up the -/// [Rust Unstable Book](https://doc.rust-lang.org/beta/unstable-book/). -fn setup_unstable_book() -> Book { - let directory_data = DirectoryData { - src: ["tools", "bookrunner", "rust-doc", "unstable-book", "src"].iter().collect(), - dest: ["tests", "bookrunner", "books", "The Unstable Book"].iter().collect(), - }; - Book { - name: "The Rust Unstable Book".to_string(), - summary_data: None, - directory_data: Some(directory_data), - toml_path: ["tools", "bookrunner", "rust-doc", "unstable-book", "book.toml"] - .iter() - .collect(), - hierarchy: HashMap::new(), - default_edition: Edition::Edition2015, - } -} - -/// Set up the -/// [Rust by Example](https://doc.rust-lang.org/nightly/rust-by-example) book. -fn setup_rust_by_example_book() -> Book { - let summary_data = SummaryData { - summary_path: ["tools", "bookrunner", "rust-doc", "rust-by-example", "src", "SUMMARY.md"] - .iter() - .collect(), - summary_start: "# Summary\n\n[Introduction](index.md)".to_string(), - }; - Book { - name: "Rust by Example".to_string(), - summary_data: Some(summary_data), - directory_data: None, - toml_path: ["tools", "bookrunner", "rust-doc", "rust-by-example", "book.toml"] - .iter() - .collect(), - hierarchy: HashMap::from_iter([( - ["tools", "bookrunner", "rust-doc", "rust-by-example", "src", "index.md"] - .iter() - .collect(), - ["tests", "bookrunner", "books", "Rust by Example", "Introduction"].iter().collect(), - )]), - default_edition: Edition::Edition2015, - } -} - -/// This data structure contains the code and configs of an example in the Rust books. -struct Example { - /// The example code extracted from a codeblock. - code: String, - // Line number of the code block. - line: usize, - // Configurations in the header of the codeblock. - config: LangString, -} - -/// Data structure representing a list of examples. Mainly for implementing the -/// [`Tester`] trait. -struct Examples(Vec); - -impl Tester for Examples { - fn add_test(&mut self, test: String, config: LangString, line: usize) { - if config.ignore != Ignore::All { - self.0.push(Example { code: test, line, config }) - } - } -} - -/// Applies the diff corresponding to `example` with parent `path` (if it exists). -fn apply_diff(path: &Path, example: &mut Example, config_paths: &mut HashSet) { - let config_dir: PathBuf = ["tools", "bookrunner", "configs"].iter().collect(); - let test_dir: PathBuf = ["tests", "bookrunner"].iter().collect(); - // `path` has the following form: - // `tests/bookrunner/books/ - // If `example` has a custom diff file, the path to the diff file will have - // the following form: - // `tools/bookrunner/configs/books//.diff` - // where is the same for both paths. - let mut diff_path = config_dir.join(path.strip_prefix(&test_dir).unwrap()); - diff_path.extend_one(format!("{}.diff", example.line)); - if diff_path.exists() { - config_paths.remove(&diff_path); - let mut code_lines: Vec<_> = example.code.lines().collect(); - let diff = fs::read_to_string(diff_path).unwrap(); - for line in diff.lines() { - // `*.diff` files have a simple format: - // `- ` for removing lines. - // `+ ` for inserting lines. - // Notice that for a series of `+` and `-`, the developer must keep - // track of the changing line numbers. - let mut split = line.splitn(3, ' '); - let symbol = split.next().unwrap(); - let line = split.next().unwrap().parse::().unwrap() - 1; - if symbol == "+" { - let diff = split.next().unwrap(); - code_lines.insert(line, diff); - } else { - code_lines.remove(line); - } - } - example.code = code_lines.join("\n"); - } -} - -/// Prepends example properties in `example.config` to the code in `example.code`. -fn prepend_props(path: &Path, example: &mut Example, config_paths: &mut HashSet) { - let config_dir: PathBuf = ["tools", "bookrunner", "configs"].iter().collect(); - let test_dir: PathBuf = ["tests", "bookrunner"].iter().collect(); - // `path` has the following form: - // `tests/bookrunner/books/ - // If `example` has a custom props file, the path to the props file will - // have the following form: - // `tools/bookrunner/configs/books//.props` - // where is the same for both paths. - let mut props_path = config_dir.join(path.strip_prefix(&test_dir).unwrap()); - props_path.extend_one(format!("{}.props", example.line)); - let mut props = if props_path.exists() { - config_paths.remove(&props_path); - util::parse_test_header(&props_path) - } else { - TestProps::new(path.to_path_buf(), None, Vec::new(), Vec::new()) - }; - // Add edition flag to the example - let edition_year = format!("{}", example.config.edition.unwrap()); - props.rustc_args.push(String::from("--edition")); - props.rustc_args.push(edition_year); - - if props.fail_step.is_none() { - if example.config.compile_fail { - // Most examples with `compile_fail` annotation fail because of - // check errors. This heuristic can be overridden by manually - //specifying the fail step in the corresponding config file. - props.fail_step = Some(FailStep::Check); - } else if example.config.should_panic { - // Kani should catch run-time errors. - props.fail_step = Some(FailStep::Verification); - } - } - example.code = format!("{props}{}", example.code); -} - -/// Extracts examples from the markdown file specified by `par_from`, -/// pre-processes those examples, and saves them in the directory specified by -/// `par_to`. -fn extract( - par_from: &Path, - par_to: &Path, - config_paths: &mut HashSet, - default_edition: Edition, -) { - let code = fs::read_to_string(par_from).unwrap(); - let mut examples = Examples(Vec::new()); - find_testable_code(&code, &mut examples, ErrorCodes::No, false, None); - for mut example in examples.0 { - apply_diff(par_to, &mut example, config_paths); - example.config.edition = Some(example.config.edition.unwrap_or(default_edition)); - example.code = make_test( - &example.code, - None, - false, - &Default::default(), - example.config.edition.unwrap(), - None, - ) - .0; - prepend_props(par_to, &mut example, config_paths); - let rs_path = par_to.join(format!("{}.rs", example.line)); - fs::create_dir_all(rs_path.parent().unwrap()).unwrap(); - fs::write(rs_path, example.code).unwrap(); - } -} - -/// Returns a set of paths to the config files for examples in the Rust books. -fn get_config_paths(book_name: &str) -> HashSet { - let config_dir: PathBuf = - ["tools", "bookrunner", "configs", "books", book_name].iter().collect(); - let mut config_paths = HashSet::new(); - if config_dir.exists() { - for entry in WalkDir::new(config_dir) { - let entry = entry.unwrap().into_path(); - if entry.is_file() { - config_paths.insert(entry); - } - } - } - config_paths -} - -/// Pretty prints the `paths` set. -fn paths_to_string(paths: HashSet) -> String { - let mut f = String::new(); - for path in paths { - f.write_fmt(format_args!(" {:?}\n", path.to_str().unwrap())).unwrap(); - } - f -} - -/// Creates a new [`bookrunner::Tree`] from `path`, and a test `result`. -fn tree_from_path(mut path: Vec, result: bool) -> bookrunner::Tree { - assert!(!path.is_empty(), "Error: `path` must contain at least 1 element."); - let mut tree = bookrunner::Tree::new( - bookrunner::Node::new( - path.pop().unwrap(), - if result { 1 } else { 0 }, - if result { 0 } else { 1 }, - ), - vec![], - ); - for _ in 0..path.len() { - tree = bookrunner::Tree::new( - bookrunner::Node::new(path.pop().unwrap(), tree.data.num_pass, tree.data.num_fail), - vec![tree], - ); - } - tree -} - -/// Parses a `litani` run and generates a bookrunner tree from it -fn parse_litani_output(path: &Path) -> bookrunner::Tree { - let file = fs::File::open(path).unwrap(); - let reader = BufReader::new(file); - let run: LitaniRun = serde_json::from_reader(reader).unwrap(); - let mut tests = - bookrunner::Tree::new(bookrunner::Node::new(String::from("bookrunner"), 0, 0), vec![]); - let pipelines = run.get_pipelines(); - for pipeline in pipelines { - let (ns, l) = parse_log_line(&pipeline); - tests = bookrunner::Tree::merge(tests, tree_from_path(ns, l)).unwrap(); - } - tests -} - -/// Parses a `litani` pipeline and returns a pair containing -/// the path to a test and its result. -fn parse_log_line(pipeline: &LitaniPipeline) -> (Vec, bool) { - let l = pipeline.get_status(); - let name = pipeline.get_name(); - let mut ns: Vec = name.split(&['/', '.'][..]).map(String::from).collect(); - // Remove unnecessary items from the path until "bookrunner" - let dash_index = ns.iter().position(|item| item == "bookrunner").unwrap(); - ns.drain(..dash_index); - // Remove unnecessary "rs" suffix. - ns.pop(); - (ns, l) -} - -/// Format and write a text version of the bookrunner report -fn generate_text_bookrunner(bookrunner: bookrunner::Tree, path: &Path) { - let bookrunner_str = format!( - "# of tests: {}\t✔️ {}\t❌ {}\n{}", - bookrunner.data.num_pass + bookrunner.data.num_fail, - bookrunner.data.num_pass, - bookrunner.data.num_fail, - bookrunner - ); - fs::write(path, bookrunner_str).expect("Error: Unable to write bookrunner results"); -} - -/// Runs examples using Litani build. -fn litani_run_tests() { - let output_prefix: PathBuf = ["build", "output"].iter().collect(); - let output_symlink: PathBuf = output_prefix.join("latest"); - let bookrunner_dir: PathBuf = ["tests", "bookrunner"].iter().collect(); - let stage_names = ["check", "codegen", "verification"]; - - util::add_kani_to_path(); - let mut litani = Litani::init("Book Runner", &stage_names, &output_prefix, &output_symlink); - - // Run all tests under the `tests/bookrunner` directory. - for entry in WalkDir::new(bookrunner_dir) { - let entry = entry.unwrap().into_path(); - if entry.is_file() { - // Ensure that we parse only Rust files by checking their extension - let entry_ext = &entry.extension().and_then(OsStr::to_str); - if let Some("rs") = entry_ext { - let test_props = util::parse_test_header(&entry); - util::add_test_pipeline(&mut litani, &test_props); - } - } - } - litani.run_build(); -} - -/// Extracts examples from the Rust books, run them through Kani, and displays -/// their results in a HTML webpage. -pub fn generate_run() { - let litani_log: PathBuf = ["build", "output", "latest", "run.json"].iter().collect(); - let text_dash: PathBuf = - ["build", "output", "latest", "html", "bookrunner.txt"].iter().collect(); - // Set up books - let books: Vec = vec![ - setup_reference_book(), - setup_nomicon_book(), - setup_unstable_book(), - setup_rust_by_example_book(), - ]; - for mut book in books { - // Parse the chapter/section hierarchy - book.parse_hierarchy(); - // Extract examples, pre-process them, and save them according to the - // parsed hierarchy - book.extract_examples(); - } - // Generate Litani's HTML bookrunner - litani_run_tests(); - // Parse Litani's output - let bookrunner = parse_litani_output(&litani_log); - // Generate text version - generate_text_bookrunner(bookrunner, &text_dash); -} diff --git a/tools/bookrunner/src/litani.rs b/tools/bookrunner/src/litani.rs deleted file mode 100644 index 620aa9c9f0d0..000000000000 --- a/tools/bookrunner/src/litani.rs +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT -//! Utilities to interact with the `Litani` build accumulator. - -use pulldown_cmark_escape::StrWrite; -use serde::Deserialize; -use std::collections::HashMap; -use std::path::Path; -use std::process::{Child, Command}; - -/// Data structure representing a full `litani` run. -/// The same representation is used to represent a run -/// in the `run.json` (cache) file generated by `litani` -/// -/// Deserialization is performed automatically for most -/// attributes in such files, but it may require you to -/// extend it if advanced features are used (e.g., pools) -#[derive(Debug, Deserialize)] -pub struct LitaniRun { - pub aux: Option>, - pub project: String, - pub version: String, - pub version_major: u32, - pub version_minor: u32, - pub version_patch: u32, - pub release_candidate: bool, - pub run_id: String, - pub start_time: String, - pub parallelism: LitaniParalellism, - pub latest_symlink: Option, - pub end_time: String, - pub pipelines: Vec, -} - -impl LitaniRun { - pub fn get_pipelines(self) -> Vec { - self.pipelines - } -} - -#[derive(Debug, Deserialize)] -pub struct LitaniParalellism { - pub trace: Vec, - pub max_paralellism: Option, - pub n_proc: u32, -} - -#[derive(Debug, Deserialize)] -pub struct LitaniTrace { - pub running: u32, - pub finished: u32, - pub total: u32, - pub time: String, -} - -#[derive(Debug, Deserialize)] -pub struct LitaniPipeline { - pub name: String, - pub ci_stages: Vec, - pub url: String, - pub status: String, -} - -impl LitaniPipeline { - pub fn get_name(&self) -> &String { - &self.name - } - - pub fn get_status(&self) -> bool { - match self.status.as_str() { - "fail" => false, - "success" => true, - _ => panic!("pipeline status is not \"fail\" nor \"success\""), - } - } -} - -#[derive(Debug, Deserialize)] -pub struct LitaniStage { - pub jobs: Vec, - pub progress: u32, - pub complete: bool, - pub status: String, - pub url: String, - pub name: String, -} - -// Some attributes in litani's `jobs` are not always included -// or they are null, so we use `Option<...>` to deserialize them -#[derive(Debug, Deserialize)] -pub struct LitaniJob { - pub wrapper_arguments: LitaniWrapperArguments, - pub complete: bool, - pub start_time: Option, - pub timeout_reached: Option, - pub command_return_code: Option, - pub memory_trace: Option>, - pub loaded_outcome_dict: Option>, - pub outcome: Option, - pub wrapper_return_code: Option, - pub stdout: Option>, - pub stderr: Option>, - pub end_time: Option, - pub duration_str: Option, - pub duration: Option, -} - -// Some attributes in litani's `wrapper_arguments` are not always included -// or they are null, so we use `Option<...>` to deserialize them -#[derive(Debug, Deserialize)] -pub struct LitaniWrapperArguments { - pub subcommand: String, - pub verbose: bool, - pub very_verbose: bool, - pub inputs: Vec, - pub command: String, - pub outputs: Option>, - pub pipeline_name: String, - pub ci_stage: String, - pub cwd: Option, - pub timeout: Option, - pub timeout_ok: Option, - pub timeout_ignore: Option, - pub ignore_returns: Option, - pub ok_returns: Vec, - pub outcome_table: Option>, - pub interleave_stdout_stderr: bool, - pub stdout_file: Option, - pub stderr_file: Option, - pub pool: Option, - pub description: String, - pub profile_memory: bool, - pub profile_memory_interval: u32, - pub phony_outputs: Option>, - pub tags: Option, - pub status_file: String, - pub job_id: String, -} - -/// Data structure representing a `Litani` build. -pub struct Litani { - /// A buffer of the `spawn`ed Litani jobs so far. `Litani` takes some time - /// to execute each `add-job` command and executing thousands of them - /// sequentially takes a considerable amount of time. To speed up the - /// execution of those commands, we spawn those commands sequentially (as - /// normal). However, instead of `wait`ing for each process to terminate, - /// we add its handle to a buffer of the `spawn`ed processes and continue - /// with our program. Once we are done adding jobs, we wait for all of them - /// to terminate before we run the `run-build` command. - spawned_commands: Vec, -} - -impl Litani { - /// Sets up a new [`Litani`] run. - pub fn init( - project_name: &str, - stage_names: &[&str], - output_prefix: &Path, - output_symlink: &Path, - ) -> Self { - Command::new("litani") - .args([ - "init", - "--project-name", - project_name, - "--output-prefix", - output_prefix.to_str().unwrap(), - "--output-symlink", - output_symlink.to_str().unwrap(), - "--stages", - ]) - .args(stage_names) - .spawn() - .unwrap() - .wait() - .unwrap(); - Self { spawned_commands: Vec::new() } - } - - /// Adds a single command with its dependencies. - #[allow(clippy::too_many_arguments)] - pub fn add_job( - &mut self, - command: &Command, - inputs: &[&Path], - outputs: &[&Path], - description: &str, - pipeline: &str, - stage: &str, - exit_status: i32, - timeout: u32, - ) { - let mut job = Command::new("litani"); - // The given command may contain additional env vars. Prepend those vars - // to the command before passing it to Litani. - let job_envs: HashMap<_, _> = job.get_envs().collect(); - let mut new_envs = String::new(); - command.get_envs().fold(&mut new_envs, |fmt, (k, v)| { - if !job_envs.contains_key(k) { - fmt.write_fmt(format_args!( - "{}=\"{}\" ", - k.to_str().unwrap(), - v.unwrap().to_str().unwrap() - )) - .unwrap(); - } - fmt - }); - job.args([ - "add-job", - "--command", - &format!("{new_envs}{command:?}"), - "--description", - description, - "--pipeline-name", - pipeline, - "--ci-stage", - stage, - "--ok-returns", - &exit_status.to_string(), - "--timeout", - &timeout.to_string(), - ]); - if !inputs.is_empty() { - job.arg("--inputs").args(inputs); - } - if !outputs.is_empty() { - job.arg("--outputs").args(outputs).arg("--phony-outputs").args(outputs); - } - // Start executing the command, but do not wait for it to terminate. - self.spawned_commands.push(job.spawn().unwrap()); - } - - /// Starts a [`Litani`] run. - pub fn run_build(&mut self) { - // Wait for all spawned processes to terminate. - for command in self.spawned_commands.iter_mut() { - command.wait().unwrap(); - } - self.spawned_commands.clear(); - // Run `run-build` command and wait for it to finish. - Command::new("litani") - .args(["run-build", "--no-pipeline-dep-graph"]) - .spawn() - .unwrap() - .wait() - .unwrap(); - } -} diff --git a/tools/bookrunner/src/main.rs b/tools/bookrunner/src/main.rs deleted file mode 100644 index 65fd9196b365..000000000000 --- a/tools/bookrunner/src/main.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT -#![feature(extend_one)] -#![feature(rustc_private)] - -mod bookrunner; -mod books; -mod litani; -mod util; - -fn main() { - books::generate_run(); -} diff --git a/tools/bookrunner/src/util.rs b/tools/bookrunner/src/util.rs deleted file mode 100644 index 542ad70dbc96..000000000000 --- a/tools/bookrunner/src/util.rs +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT -//! Utilities types and procedures to parse and run tests. -//! -//! TODO: The types and procedures in this modules are similar to the ones in -//! `compiletest`. Consider using `Litani` to run the test suites (see -//! [issue #390](https://github.com/model-checking/kani/issues/390)). - -use crate::litani::Litani; -use std::{ - env, - fmt::{self, Display, Formatter, Write}, - fs::File, - io::{BufRead, BufReader}, - path::{Path, PathBuf}, - process::Command, -}; - -/// Step at which Kani should panic. -#[derive(PartialEq, Eq)] -pub enum FailStep { - /// Kani panics before the codegen step (up to MIR generation). This step - /// runs the same checks on the test code as `cargo check` including syntax, - /// type, name resolution, and borrow checks. - Check, - /// Kani panics at the codegen step because the test code uses unimplemented - /// and/or unsupported features. - Codegen, - /// Kani panics after the codegen step because of verification failures or - /// other CBMC errors. - Verification, -} - -impl Display for FailStep { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let str = match self { - FailStep::Check => "check", - FailStep::Codegen => "codegen", - FailStep::Verification => "verify", - }; - f.write_str(str) - } -} - -/// Data structure representing properties specific to each test. -pub struct TestProps { - pub path: PathBuf, - /// How far this test should proceed to start failing. - pub fail_step: Option, - /// Extra arguments to pass to `rustc`. - pub rustc_args: Vec, - /// Extra arguments to pass to Kani. - pub kani_args: Vec, -} - -impl TestProps { - /// Creates a new instance of [`TestProps`] for a test. - pub fn new( - path: PathBuf, - fail_step: Option, - rustc_args: Vec, - kani_args: Vec, - ) -> Self { - Self { path, fail_step, rustc_args, kani_args } - } -} - -impl Display for TestProps { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - if let Some(fail_step) = &self.fail_step { - f.write_fmt(format_args!("// kani-{fail_step}-fail\n"))?; - } - if !self.rustc_args.is_empty() { - f.write_str("// compile-flags:")?; - for arg in &self.rustc_args { - f.write_fmt(format_args!(" {arg}"))?; - } - f.write_char('\n')?; - } - if !self.kani_args.is_empty() { - f.write_str("// kani-flags:")?; - for arg in &self.kani_args { - f.write_fmt(format_args!(" {arg}"))?; - } - f.write_char('\n')?; - } - Ok(()) - } -} - -/// Parses strings of the form `kani-*-fail` and returns the step at which Kani is -/// expected to panic. -fn try_parse_fail_step(cur_fail_step: Option, line: &str) -> Option { - let fail_step = if line.contains("kani-check-fail") { - Some(FailStep::Check) - } else if line.contains("kani-codegen-fail") { - Some(FailStep::Codegen) - } else if line.contains("kani-verify-fail") { - Some(FailStep::Verification) - } else { - None - }; - match (cur_fail_step.is_some(), fail_step.is_some()) { - (true, true) => panic!("Error: multiple `kani-*-fail` headers in a single test."), - (false, true) => fail_step, - _ => cur_fail_step, - } -} - -/// Parses strings of the form `-flags: ...` and returns the list of -/// arguments. -fn try_parse_args(cur_args: Vec, name: &str, line: &str) -> Vec { - let name = format!("{name}-flags:"); - let mut split = line.split(&name).skip(1); - let args: Vec = if let Some(rest) = split.next() { - rest.split_whitespace().map(String::from).collect() - } else { - Vec::new() - }; - match (cur_args.is_empty(), args.is_empty()) { - (false, false) => panic!("Error: multiple `{}-flags: ...` headers in a single test.", name), - (true, false) => args, - _ => cur_args, - } -} - -/// Parses and returns the properties in a test file. -pub fn parse_test_header(path: &Path) -> TestProps { - let mut fail_step = None; - let mut rustc_args = Vec::new(); - let mut kani_args = Vec::new(); - let it = BufReader::new(File::open(path).unwrap()); - for line in it.lines() { - let line = line.unwrap(); - let line = line.trim_start(); - if line.is_empty() { - continue; - } - if !line.starts_with("//") { - break; - } - fail_step = try_parse_fail_step(fail_step, line); - rustc_args = try_parse_args(rustc_args, "compile", line); - kani_args = try_parse_args(kani_args, "kani", line); - } - TestProps::new(path.to_path_buf(), fail_step, rustc_args, kani_args) -} - -/// Adds Kani to the current `PATH` environment variable. -pub fn add_kani_to_path() { - let cwd = env::current_dir().unwrap(); - let kani_bin = cwd.join("target").join("kani").join("bin"); - let kani_scripts = cwd.join("scripts"); - env::set_var( - "PATH", - format!("{}:{}:{}", kani_scripts.display(), kani_bin.display(), env::var("PATH").unwrap()), - ); -} - -/// Does Kani catch syntax, type, and borrow errors (if any)? -pub fn add_check_job(litani: &mut Litani, test_props: &TestProps) { - let exit_status = if test_props.fail_step == Some(FailStep::Check) { 1 } else { 0 }; - let mut kani_rustc = Command::new("kani-compiler"); - kani_rustc.args(&test_props.rustc_args).args(["-Z", "no-codegen"]).arg(&test_props.path); - - let mut phony_out = test_props.path.clone(); - phony_out.set_extension("check"); - litani.add_job( - &kani_rustc, - &[&test_props.path], - &[&phony_out], - "Is this valid Rust code?", - test_props.path.to_str().unwrap(), - "check", - exit_status, - 5, - ); -} - -/// Is Kani expected to codegen all the Rust features in the test? -pub fn add_codegen_job(litani: &mut Litani, test_props: &TestProps) { - let exit_status = if test_props.fail_step == Some(FailStep::Codegen) { 1 } else { 0 }; - let mut kani_rustc = Command::new("kani-compiler"); - kani_rustc.args(&test_props.rustc_args).args(["--out-dir", "build/tmp"]).arg(&test_props.path); - - let mut phony_in = test_props.path.clone(); - phony_in.set_extension("check"); - let mut phony_out = test_props.path.clone(); - phony_out.set_extension("codegen"); - litani.add_job( - &kani_rustc, - &[&phony_in], - &[&phony_out], - "Does Kani support all the Rust features used in it?", - test_props.path.to_str().unwrap(), - "codegen", - exit_status, - 10, - ); -} - -// Does verification pass/fail as it is expected to? -pub fn add_verification_job(litani: &mut Litani, test_props: &TestProps) { - let exit_status = if test_props.fail_step == Some(FailStep::Verification) { 10 } else { 0 }; - let mut kani = Command::new("kani"); - // Add `--function main` so we can run these without having to amend them to add `#[kani::proof]`. - // Some of test_props.kani_args will contains `--cbmc-args` so we should always put that last. - kani.arg(&test_props.path) - .args(["--enable-unstable", "--function", "main"]) - .args(&test_props.kani_args); - if !test_props.rustc_args.is_empty() { - kani.env("RUSTFLAGS", test_props.rustc_args.join(" ")); - } - - let mut phony_in = test_props.path.clone(); - phony_in.set_extension("codegen"); - litani.add_job( - &kani, - &[&phony_in], - &[], - "Can Kani reason about it?", - test_props.path.to_str().unwrap(), - "verification", - exit_status, - 60, - ); -} - -/// Creates a new pipeline for the test specified by `path` consisting of 3 -/// jobs/steps: `check`, `codegen`, and `verification`. -pub fn add_test_pipeline(litani: &mut Litani, test_props: &TestProps) { - // The first step ensures that the Rust code in the test compiles (if it is - // expected to). - add_check_job(litani, test_props); - if test_props.fail_step == Some(FailStep::Check) { - return; - } - // The second step ensures that we can codegen the code in the test. - add_codegen_job(litani, test_props); - if test_props.fail_step == Some(FailStep::Codegen) { - return; - } - // The final step ensures that CBMC can verify the code in the test. - // Notice that 10 is the expected error code for verification failure. - add_verification_job(litani, test_props); -} From a589e57d4b9e9fafa52608b996537dc729867056 Mon Sep 17 00:00:00 2001 From: "Felipe R. Monteiro" Date: Fri, 5 Apr 2024 06:47:09 -0400 Subject: [PATCH 057/225] Upgrade Rust toolchain to nightly-2024-03-29 (#3116) Resolves https://github.com/model-checking/kani/issues/3115. Co-authored-by: Michael Tautschnig --- docs/src/rust-feature-support/intrinsics.md | 1 + .../codegen/intrinsic.rs | 42 +++++++++++++++++++ .../codegen_cprover_gotoc/codegen/rvalue.rs | 2 +- .../src/codegen_cprover_gotoc/codegen/typ.rs | 11 ++--- .../codegen_cprover_gotoc/context/goto_ctx.rs | 5 --- kani-compiler/src/kani_middle/mod.rs | 8 ++-- kani-compiler/src/kani_middle/reachability.rs | 8 +--- .../src/kani_middle/transform/body.rs | 8 ++-- kani-driver/src/call_cbmc.rs | 11 +---- kani-driver/src/cbmc_output_parser.rs | 3 -- .../src/concrete_playback/test_generator.rs | 3 -- rust-toolchain.toml | 2 +- tests/kani/Intrinsics/typed_swap.rs | 21 ++++++++++ 13 files changed, 83 insertions(+), 42 deletions(-) create mode 100644 tests/kani/Intrinsics/typed_swap.rs diff --git a/docs/src/rust-feature-support/intrinsics.md b/docs/src/rust-feature-support/intrinsics.md index 7df1a093943f..49a1f0845ecc 100644 --- a/docs/src/rust-feature-support/intrinsics.md +++ b/docs/src/rust-feature-support/intrinsics.md @@ -220,6 +220,7 @@ truncf64 | Yes | | try | No | [#267](https://github.com/model-checking/kani/issues/267) | type_id | Yes | | type_name | Yes | | +typed_swap | Yes | | unaligned_volatile_load | No | See [Notes - Concurrency](#concurrency) | unaligned_volatile_store | No | See [Notes - Concurrency](#concurrency) | unchecked_add | Yes | | diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 8fce4b309820..8a61d46ed518 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -580,6 +580,7 @@ impl<'tcx> GotocCtx<'tcx> { "truncf64" => codegen_simple_intrinsic!(Trunc), "type_id" => codegen_intrinsic_const!(), "type_name" => codegen_intrinsic_const!(), + "typed_swap" => self.codegen_swap(fargs, farg_types, loc), "unaligned_volatile_load" => { unstable_codegen!( self.codegen_expr_to_place_stable(place, fargs.remove(0).dereference()) @@ -1943,6 +1944,47 @@ impl<'tcx> GotocCtx<'tcx> { let zero = Type::size_t().zero(); cast_ptr.rem(align).eq(zero) } + + /// Swaps the memory contents pointed to by arguments `x` and `y`, respectively, which is + /// required for the `typed_swap` intrinsic. + /// + /// The standard library API requires that `x` and `y` are readable and writable as their + /// (common) type (which auto-generated checks for dereferencing will take care of), and the + /// memory regions pointed to must be non-overlapping. + pub fn codegen_swap(&mut self, mut fargs: Vec, farg_types: &[Ty], loc: Location) -> Stmt { + // two parameters, and both must be raw pointers with the same base type + assert!(fargs.len() == 2); + assert!(farg_types[0].kind().is_raw_ptr()); + assert!(farg_types[0] == farg_types[1]); + + let x = fargs.remove(0); + let y = fargs.remove(0); + + // if(same_object(x, y)) { + // assert(x + 1 <= y || y + 1 <= x); + // assume(x + 1 <= y || y + 1 <= x); + // } + let one = Expr::int_constant(1, Type::c_int()); + let non_overlapping = + x.clone().plus(one.clone()).le(y.clone()).or(y.clone().plus(one.clone()).le(x.clone())); + let non_overlapping_check = self.codegen_assert_assume( + non_overlapping, + PropertyClass::SafetyCheck, + "memory regions pointed to by `x` and `y` must not overlap", + loc, + ); + let non_overlapping_stmt = + Stmt::if_then_else(x.clone().same_object(y.clone()), non_overlapping_check, None, loc); + + // T t = *y; *y = *x; *x = t; + let deref_y = y.clone().dereference(); + let (temp_var, assign_to_t) = + self.decl_temp_variable(deref_y.typ().clone(), Some(deref_y), loc); + let assign_to_y = y.dereference().assign(x.clone().dereference(), loc); + let assign_to_x = x.dereference().assign(temp_var, loc); + + Stmt::block(vec![non_overlapping_stmt, assign_to_t, assign_to_y, assign_to_x], loc) + } } fn instance_args(instance: &Instance) -> GenericArgs { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index 5867448d9973..74622d41ed50 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -725,7 +725,7 @@ impl<'tcx> GotocCtx<'tcx> { .bytes(), Type::size_t(), ), - NullOp::UbCheck(_) => Expr::c_false(), + NullOp::UbChecks => Expr::c_false(), } } Rvalue::ShallowInitBox(ref operand, content_ty) => { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index 089d871d22b4..b5ccf1a83716 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -570,7 +570,7 @@ impl<'tcx> GotocCtx<'tcx> { // Note: This is not valid C but CBMC seems to be ok with it. ty::Slice(e) => self.codegen_ty(*e).flexible_array_of(), ty::Str => Type::unsigned_int(8).flexible_array_of(), - ty::Ref(_, t, _) | ty::RawPtr(ty::TypeAndMut { ty: t, .. }) => self.codegen_ty_ref(*t), + ty::Ref(_, t, _) | ty::RawPtr(t, _) => self.codegen_ty_ref(*t), ty::FnDef(def_id, args) => { let instance = Instance::resolve(self.tcx, ty::ParamEnv::reveal_all(), *def_id, args) @@ -993,7 +993,7 @@ impl<'tcx> GotocCtx<'tcx> { | ty::Foreign(_) | ty::Coroutine(..) | ty::Int(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::Tuple(_) | ty::Uint(_) => self.codegen_ty(pointee_type).to_pointer(), @@ -1352,10 +1352,7 @@ impl<'tcx> GotocCtx<'tcx> { // `F16` and `F128` are not yet handled. // Tracked here: Primitive::F16 | Primitive::F128 => unimplemented!(), - Primitive::Pointer(_) => Ty::new_ptr( - self.tcx, - ty::TypeAndMut { ty: self.tcx.types.u8, mutbl: Mutability::Not }, - ), + Primitive::Pointer(_) => Ty::new_ptr(self.tcx, self.tcx.types.u8, Mutability::Not), } } @@ -1659,7 +1656,7 @@ fn common_vtable_fields(drop_in_place: Type) -> Vec { pub fn pointee_type(mir_type: Ty) -> Option { match mir_type.kind() { ty::Ref(_, pointee_type, _) => Some(*pointee_type), - ty::RawPtr(ty::TypeAndMut { ty: pointee_type, .. }) => Some(*pointee_type), + ty::RawPtr(pointee_type, _) => Some(*pointee_type), _ => None, } } 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 a25e502aa574..3a6501d544d8 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs @@ -23,7 +23,6 @@ use crate::kani_queries::QueryDb; use cbmc::goto_program::{DatatypeComponent, Expr, Location, Stmt, Symbol, SymbolTable, Type}; use cbmc::utils::aggr_tag; use cbmc::{InternedString, MachineModel}; -use kani_metadata::HarnessMetadata; use rustc_data_structures::fx::FxHashMap; use rustc_middle::span_bug; use rustc_middle::ty::layout::{ @@ -61,8 +60,6 @@ pub struct GotocCtx<'tcx> { /// map from symbol identifier to string literal /// TODO: consider making the map from Expr to String instead pub str_literals: FxHashMap, - pub proof_harnesses: Vec, - pub test_harnesses: Vec, /// a global counter for generating unique IDs for checks pub global_checks_count: u64, /// A map of unsupported constructs that were found while codegen @@ -98,8 +95,6 @@ impl<'tcx> GotocCtx<'tcx> { current_fn: None, type_map: FxHashMap::default(), str_literals: FxHashMap::default(), - proof_harnesses: vec![], - test_harnesses: vec![], global_checks_count: 0, unsupported_constructs: FxHashMap::default(), concurrent_constructs: FxHashMap::default(), diff --git a/kani-compiler/src/kani_middle/mod.rs b/kani-compiler/src/kani_middle/mod.rs index ab694ceec614..17992953d5c7 100644 --- a/kani-compiler/src/kani_middle/mod.rs +++ b/kani-compiler/src/kani_middle/mod.rs @@ -46,7 +46,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_crate_items(()).items() { + for item in tcx.hir().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 { @@ -127,9 +127,11 @@ pub fn dump_mir_items(tcx: TyCtxt, items: &[MonoItem], output: &Path) { pub struct SourceLocation { pub filename: String, pub start_line: usize, - pub start_col: usize, + #[allow(dead_code)] + pub start_col: usize, // set, but not currently used in Goto output pub end_line: usize, - pub end_col: usize, + #[allow(dead_code)] + pub end_col: usize, // set, but not currently used in Goto output } impl SourceLocation { diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index eb40f7ec673d..456d5425b245 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -25,7 +25,6 @@ use rustc_middle::ty::{TyCtxt, VtblEntry}; use rustc_smir::rustc_internal; use stable_mir::mir::alloc::{AllocId, GlobalAlloc}; use stable_mir::mir::mono::{Instance, InstanceKind, MonoItem, StaticDef}; -use stable_mir::mir::pretty::pretty_ty; use stable_mir::mir::{ visit::Location, Body, CastKind, Constant, MirVisitor, PointerCoercion, Rvalue, Terminator, TerminatorKind, @@ -434,7 +433,7 @@ impl<'a, 'tcx> MirVisitor for MonoItemsFnCollector<'a, 'tcx> { `{}`. The function `{}` \ cannot be stubbed by `{}` due to \ generic bounds not being met. Callee: {}", - pretty_ty(receiver_ty.kind()), + receiver_ty, trait_, caller, self.tcx.def_path_str(stub), @@ -453,10 +452,7 @@ impl<'a, 'tcx> MirVisitor for MonoItemsFnCollector<'a, 'tcx> { objects in the modifies clause (including return types) \ implement `{}`.\nThis is a strict condition to use \ function contracts as verified stubs.", - pretty_ty(receiver_ty.kind()), - trait_, - callee, - trait_, + receiver_ty, trait_, callee, trait_, ), ); } else { diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index 32ffb373d767..d14aca3a7c5b 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -173,7 +173,7 @@ impl MutableBody { let terminator = Terminator { kind, span }; self.split_bb(source, terminator); } - CheckType::Panic(..) | CheckType::NoCore => { + CheckType::Panic | CheckType::NoCore => { tcx.sess .dcx() .struct_err("Failed to instrument the code. Cannot find `kani::assert`") @@ -231,7 +231,7 @@ pub enum CheckType { /// This is used by default when the `kani` crate is available. Assert(Instance), /// When the `kani` crate is not available, we have to model the check as an `if { panic!() }`. - Panic(Instance), + Panic, /// When building non-core crate, such as `rustc-std-workspace-core`, we cannot /// instrument code, but we can still compile them. NoCore, @@ -246,8 +246,8 @@ impl CheckType { pub fn new(tcx: TyCtxt) -> CheckType { if let Some(instance) = find_instance(tcx, "KaniAssert") { CheckType::Assert(instance) - } else if let Some(instance) = find_instance(tcx, "panic_str") { - CheckType::Panic(instance) + } else if find_instance(tcx, "panic_str").is_some() { + CheckType::Panic } else { CheckType::NoCore } diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index a806331401cd..7d0edf6f50e5 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -11,7 +11,7 @@ use std::time::{Duration, Instant}; use crate::args::{OutputFormat, VerificationArgs}; use crate::cbmc_output_parser::{ - extract_results, process_cbmc_output, CheckStatus, ParserItem, Property, VerificationOutput, + extract_results, process_cbmc_output, CheckStatus, Property, VerificationOutput, }; use crate::cbmc_property_renderer::{format_coverage, format_result, kani_cbmc_output_filter}; use crate::session::KaniSession; @@ -45,9 +45,6 @@ pub struct VerificationResult { pub status: VerificationStatus, /// The compact representation for failed properties pub failed_properties: FailedProperties, - /// The parsed output, message by message, of CBMC. However, the `Result` message has been - /// removed and is available in `results` instead. - pub messages: Option>, /// The `Result` properties in detail or the exit_status of CBMC. /// Note: CBMC process exit status is only potentially useful if `status` is `Failure`. /// Kani will see CBMC report "failure" that's actually success (interpreting "failed" @@ -254,7 +251,7 @@ impl VerificationResult { start_time: Instant, ) -> VerificationResult { let runtime = start_time.elapsed(); - let (items, results) = extract_results(output.processed_items); + let (_, results) = extract_results(output.processed_items); if let Some(results) = results { let (status, failed_properties) = @@ -262,7 +259,6 @@ impl VerificationResult { VerificationResult { status, failed_properties, - messages: Some(items), results: Ok(results), runtime, generated_concrete_test: false, @@ -272,7 +268,6 @@ impl VerificationResult { VerificationResult { status: VerificationStatus::Failure, failed_properties: FailedProperties::Other, - messages: Some(items), results: Err(output.process_status), runtime, generated_concrete_test: false, @@ -284,7 +279,6 @@ impl VerificationResult { VerificationResult { status: VerificationStatus::Success, failed_properties: FailedProperties::None, - messages: None, results: Ok(vec![]), runtime: Duration::from_secs(0), generated_concrete_test: false, @@ -295,7 +289,6 @@ impl VerificationResult { VerificationResult { status: VerificationStatus::Failure, failed_properties: FailedProperties::Other, - messages: None, // on failure, exit codes in theory might be used, // but `mock_failure` should never be used in a context where they will, // so again use something weird: diff --git a/kani-driver/src/cbmc_output_parser.rs b/kani-driver/src/cbmc_output_parser.rs index 1bb353d7d4c0..127f98beab56 100644 --- a/kani-driver/src/cbmc_output_parser.rs +++ b/kani-driver/src/cbmc_output_parser.rs @@ -287,9 +287,7 @@ fn filepath(file: String) -> String { #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TraceItem { - pub thread: u32, pub step_type: String, - pub hidden: bool, pub lhs: Option, pub source_location: Option, pub value: Option, @@ -301,7 +299,6 @@ pub struct TraceItem { /// The fields included right now are relevant to primitive types. #[derive(Clone, Debug, Deserialize)] pub struct TraceValue { - pub name: String, pub binary: Option, pub data: Option, pub width: Option, diff --git a/kani-driver/src/concrete_playback/test_generator.rs b/kani-driver/src/concrete_playback/test_generator.rs index 53c3a92dd94c..dbd2fb9e03d2 100644 --- a/kani-driver/src/concrete_playback/test_generator.rs +++ b/kani-driver/src/concrete_playback/test_generator.rs @@ -588,9 +588,7 @@ mod tests { line: None, }, trace: Some(vec![TraceItem { - thread: 0, step_type: "assignment".to_string(), - hidden: false, lhs: Some("goto_symex$$return_value".to_string()), source_location: Some(SourceLocation { column: None, @@ -599,7 +597,6 @@ mod tests { line: None, }), value: Some(TraceValue { - name: "".to_string(), binary: Some("0000001100000001".to_string()), data: Some(TraceData::NonBool("385".to_string())), width: Some(16), diff --git a/rust-toolchain.toml b/rust-toolchain.toml index d08e3a84f0ad..eee073e29a89 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-03-21" +channel = "nightly-2024-03-29" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/kani/Intrinsics/typed_swap.rs b/tests/kani/Intrinsics/typed_swap.rs new file mode 100644 index 000000000000..4279bb713ece --- /dev/null +++ b/tests/kani/Intrinsics/typed_swap.rs @@ -0,0 +1,21 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// Check that `typed_swap` yields the expected results. +// https://doc.rust-lang.org/nightly/std/intrinsics/fn.typed_swap.html + +#![feature(core_intrinsics)] +#![allow(internal_features)] + +#[kani::proof] +fn test_typed_swap_u32() { + let mut a: u32 = kani::any(); + let a_before = a; + let mut b: u32 = kani::any(); + let b_before = b; + unsafe { + std::intrinsics::typed_swap(&mut a, &mut b); + } + assert!(b == a_before); + assert!(a == b_before); +} From 5131258d3ac3b5cf78d14848ffb1adcb8c619528 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Fri, 5 Apr 2024 04:16:55 -0700 Subject: [PATCH 058/225] Remove unnecessary build step for some workflows (#3124) Workflows that run the perf regressions (`scripts/kani-perf.sh`) do not need to build Kani, as the perf script itself builds Kani in release mode: https://github.com/model-checking/kani/blob/dc0978043c52492112e4ad37a617fd3c8100ef1f/scripts/kani-perf.sh#L12 The extra build step was causing Kani to be built two extra times for the `benchcomp` flow because the workflow was building it in debug mode, so the script will end up rebuilding it in release mode. This should save about 3 minutes for the `benchcomp` flow. --- .github/workflows/bench.yml | 6 ------ .github/workflows/cbmc-latest.yml | 4 ---- .github/workflows/kani-m1.yml | 3 --- .github/workflows/kani.yml | 6 ------ 4 files changed, 19 deletions(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index ad0bbf81f2c2..12b08eba7c9c 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -55,12 +55,6 @@ jobs: os: ubuntu-20.04 kani_dir: new - - name: Build Kani (new variant) - run: pushd new && cargo build-dev - - - name: Build Kani (old variant) - run: pushd old && cargo build-dev - - name: Copy benchmarks from new to old run: rm -rf ./old/tests/perf ; cp -r ./new/tests/perf ./old/tests/ diff --git a/.github/workflows/cbmc-latest.yml b/.github/workflows/cbmc-latest.yml index d8da02d21840..f707c5d558a5 100644 --- a/.github/workflows/cbmc-latest.yml +++ b/.github/workflows/cbmc-latest.yml @@ -32,10 +32,6 @@ jobs: os: ${{ matrix.os }} kani_dir: 'kani' - - name: Build Kani - working-directory: ./kani - run: cargo build-dev - - name: Checkout CBMC under "cbmc" uses: actions/checkout@v4 with: diff --git a/.github/workflows/kani-m1.yml b/.github/workflows/kani-m1.yml index 36f2b615c4aa..0f884f2fe013 100644 --- a/.github/workflows/kani-m1.yml +++ b/.github/workflows/kani-m1.yml @@ -23,8 +23,5 @@ jobs: with: os: macos-13-xlarge - - name: Build Kani - run: cargo build-dev - - name: Execute Kani regression run: ./scripts/kani-regression.sh diff --git a/.github/workflows/kani.yml b/.github/workflows/kani.yml index 12b4ac5fb6d2..d98e0a2b0a5b 100644 --- a/.github/workflows/kani.yml +++ b/.github/workflows/kani.yml @@ -27,9 +27,6 @@ jobs: with: os: ${{ matrix.os }} - - name: Build Kani - run: cargo build-dev - - name: Execute Kani regression run: ./scripts/kani-regression.sh @@ -88,9 +85,6 @@ jobs: with: os: ubuntu-20.04 - - name: Build Kani using release mode - run: cargo build-dev -- --release - - name: Execute Kani performance tests run: ./scripts/kani-perf.sh env: From c4f16e9a7a45d2feaef1f82d57195a839821423c Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Fri, 5 Apr 2024 04:57:12 -0700 Subject: [PATCH 059/225] Ensure storage markers are kept in std code (#3080) This is a follow-up to #3063 that turns off that MIR pass while compiling `std` as well to ensure any bugs of the same nature in `std` are captured by Kani. Resolves #3079 --- kani-compiler/src/args.rs | 3 +++ .../src/codegen_cprover_gotoc/codegen/statement.rs | 14 ++++++++++++-- kani-driver/src/args/mod.rs | 8 ++++++++ kani-driver/src/call_single_file.rs | 4 ++++ tests/perf/btreeset/insert_any/Cargo.toml | 5 +++++ tests/perf/btreeset/insert_multi/Cargo.toml | 5 +++++ tests/perf/btreeset/insert_same/Cargo.toml | 5 +++++ tests/perf/hashset/Cargo.toml | 5 +++++ tools/build-kani/src/sysroot.rs | 9 ++++++++- 9 files changed, 55 insertions(+), 3 deletions(-) diff --git a/kani-compiler/src/args.rs b/kani-compiler/src/args.rs index fcc346bd745f..225cb94f4264 100644 --- a/kani-compiler/src/args.rs +++ b/kani-compiler/src/args.rs @@ -74,6 +74,9 @@ pub struct Arguments { /// Enable specific checks. #[clap(long)] pub ub_check: Vec, + /// Ignore storage markers. + #[clap(long)] + pub ignore_storage_markers: bool, } #[derive(Debug, Clone, Copy, AsRefStr, EnumString, VariantNames, PartialEq, Eq)] diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 6379a023423f..9a78515fde90 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -76,9 +76,19 @@ impl<'tcx> GotocCtx<'tcx> { self.codegen_set_discriminant(dest_ty, dest_expr, *variant_index, location) } StatementKind::StorageLive(var_id) => { - Stmt::decl(self.codegen_local(*var_id), None, location) + if self.queries.args().ignore_storage_markers { + Stmt::skip(location) + } else { + Stmt::decl(self.codegen_local(*var_id), None, location) + } + } + StatementKind::StorageDead(var_id) => { + if self.queries.args().ignore_storage_markers { + Stmt::skip(location) + } else { + Stmt::dead(self.codegen_local(*var_id), location) + } } - StatementKind::StorageDead(var_id) => Stmt::dead(self.codegen_local(*var_id), location), StatementKind::Intrinsic(NonDivergingIntrinsic::CopyNonOverlapping( CopyNonOverlapping { src, dst, count }, )) => { diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index aeda94168840..36d9e0af2d0e 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -252,6 +252,14 @@ pub struct VerificationArgs { #[arg(long, hide_short_help = true, requires("enable_unstable"))] pub ignore_global_asm: bool, + /// Ignore lifetimes of local variables. This effectively extends their + /// lifetimes to the function scope, and hence may cause Kani to miss + /// undefined behavior resulting from using the variable after it dies. + /// This option may impact the soundness of the analysis and may cause false + /// proofs and/or counterexamples + #[arg(long, hide_short_help = true, requires("enable_unstable"))] + pub ignore_locals_lifetime: bool, + /// Write the GotoC symbol table to a file in JSON format instead of goto binary format. #[arg(long, hide_short_help = true)] pub write_json_symtab: bool, diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index 24691943bc0f..07899c8c4899 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -105,6 +105,10 @@ impl KaniSession { flags.push("--ub-check=validity".into()) } + if self.args.ignore_locals_lifetime { + flags.push("--ignore-storage-markers".into()) + } + flags.extend(self.args.common_args.unstable_features.as_arguments().map(str::to_string)); // This argument will select the Kani flavour of the compiler. It will be removed before diff --git a/tests/perf/btreeset/insert_any/Cargo.toml b/tests/perf/btreeset/insert_any/Cargo.toml index 41fa0a2db3ba..66d8ecdddeb1 100644 --- a/tests/perf/btreeset/insert_any/Cargo.toml +++ b/tests/perf/btreeset/insert_any/Cargo.toml @@ -9,3 +9,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] + +# Temporarily ignore the handling of storage markers till +# https://github.com/model-checking/kani/issues/3099 is fixed +[package.metadata.kani] +flags = { ignore-locals-lifetime = true, enable-unstable = true } diff --git a/tests/perf/btreeset/insert_multi/Cargo.toml b/tests/perf/btreeset/insert_multi/Cargo.toml index bdd2f4e3528a..44028f8c842d 100644 --- a/tests/perf/btreeset/insert_multi/Cargo.toml +++ b/tests/perf/btreeset/insert_multi/Cargo.toml @@ -9,3 +9,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] + +# Temporarily ignore the handling of storage markers till +# https://github.com/model-checking/kani/issues/3099 is fixed +[package.metadata.kani] +flags = { ignore-locals-lifetime = true, enable-unstable = true } diff --git a/tests/perf/btreeset/insert_same/Cargo.toml b/tests/perf/btreeset/insert_same/Cargo.toml index 0a4e0f7ee037..465119c74fbe 100644 --- a/tests/perf/btreeset/insert_same/Cargo.toml +++ b/tests/perf/btreeset/insert_same/Cargo.toml @@ -9,3 +9,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] + +# Temporarily ignore the handling of storage markers till +# https://github.com/model-checking/kani/issues/3099 is fixed +[package.metadata.kani] +flags = { ignore-locals-lifetime = true, enable-unstable = true } diff --git a/tests/perf/hashset/Cargo.toml b/tests/perf/hashset/Cargo.toml index d0757e11154b..464fba412e6d 100644 --- a/tests/perf/hashset/Cargo.toml +++ b/tests/perf/hashset/Cargo.toml @@ -12,3 +12,8 @@ description = "Verify HashSet basic behavior" [package.metadata.kani.unstable] stubbing = true + +# Temporarily ignore the handling of storage markers till +# https://github.com/model-checking/kani/issues/3099 is fixed +[package.metadata.kani] +flags = { ignore-locals-lifetime = true, enable-unstable = true } diff --git a/tools/build-kani/src/sysroot.rs b/tools/build-kani/src/sysroot.rs index 3a6239106826..b831ed0d63a8 100644 --- a/tools/build-kani/src/sysroot.rs +++ b/tools/build-kani/src/sysroot.rs @@ -124,7 +124,14 @@ fn build_kani_lib( "--message-format", "json-diagnostic-rendered-ansi", ]; - let mut rustc_args = vec!["--cfg=kani", "--cfg=kani_sysroot", "-Z", "always-encode-mir"]; + let mut rustc_args = vec![ + "--cfg=kani", + "--cfg=kani_sysroot", + "-Z", + "always-encode-mir", + "-Z", + "mir-enable-passes=-RemoveStorageMarkers", + ]; rustc_args.extend_from_slice(extra_rustc_args); let mut cmd = Command::new("cargo") .env("CARGO_ENCODED_RUSTFLAGS", rustc_args.join("\x1f")) From ac46d986b74f1f0a431b5a087fab9b6fd376fb2a Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Fri, 5 Apr 2024 16:11:57 +0200 Subject: [PATCH 060/225] Bump Kani version to 0.49.0 (#3126) Updated version in all `Cargo.toml` files (via `find . -name Cargo.toml -exec sed -i 's/version = "0.48.0"/version = "0.49.0"/' {} \;`) and ran `cargo build-dev` to have `Cargo.lock` files updated. GitHub generated release notes: ## What's Changed * Upgrade Rust toolchain to 2024-03-14 by @zhassan-aws in https://github.com/model-checking/kani/pull/3081 * Disable removal of storage markers by @zhassan-aws in https://github.com/model-checking/kani/pull/3083 * Automatic cargo update to 2024-03-18 by @github-actions in https://github.com/model-checking/kani/pull/3086 * Bump tests/perf/s2n-quic from `1a7faa8` to `9e39ca0` by @dependabot in https://github.com/model-checking/kani/pull/3087 * Upgrade toolchain to nightly-2024-03-15 by @celinval in https://github.com/model-checking/kani/pull/3084 * Add optional scatterplot to benchcomp output by @tautschnig in https://github.com/model-checking/kani/pull/3077 * Benchcomp scatterplots: quote axis labels by @tautschnig in https://github.com/model-checking/kani/pull/3097 * Expand ${var} in benchcomp variant `env` by @karkhaz in https://github.com/model-checking/kani/pull/3090 * Add test for #3099 by @zhassan-aws in https://github.com/model-checking/kani/pull/3100 * Automatic cargo update to 2024-03-25 by @github-actions in https://github.com/model-checking/kani/pull/3103 * Bump tests/perf/s2n-quic from `1a7faa8` to `0a60ec1` by @dependabot in https://github.com/model-checking/kani/pull/3104 * Implement validity checks by @celinval in https://github.com/model-checking/kani/pull/3085 * Add `benchcomp filter` command by @karkhaz in https://github.com/model-checking/kani/pull/3105 * Add CI test for --use-local-toolchain by @jaisnan in https://github.com/model-checking/kani/pull/3074 * Upgrade Rust toolchain to `nightly-2024-03-21` by @adpaco-aws in https://github.com/model-checking/kani/pull/3102 * Use `intrinsic_name` to get the intrinsic name by @adpaco-aws in https://github.com/model-checking/kani/pull/3114 * Bump tests/perf/s2n-quic from `0a60ec1` to `2d5e891` by @dependabot in https://github.com/model-checking/kani/pull/3118 * Allow modifies clause for verification only by @feliperodri in https://github.com/model-checking/kani/pull/3098 * Automatic cargo update to 2024-04-01 by @github-actions in https://github.com/model-checking/kani/pull/3117 * Automatic cargo update to 2024-04-04 by @github-actions in https://github.com/model-checking/kani/pull/3122 * Remove bookrunner by @tautschnig in https://github.com/model-checking/kani/pull/3123 * Upgrade Rust toolchain to nightly-2024-03-29 by @feliperodri in https://github.com/model-checking/kani/pull/3116 * Remove unnecessary build step for some workflows by @zhassan-aws in https://github.com/model-checking/kani/pull/3124 * Ensure storage markers are kept in std code by @zhassan-aws in https://github.com/model-checking/kani/pull/3080 **Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.48.0...kani-0.49.0 --- CHANGELOG.md | 14 ++++++++++++++ Cargo.lock | 18 +++++++++--------- 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_macros/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- tools/build-kani/Cargo.toml | 2 +- 11 files changed, 32 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08f687d8e31c..bd592b2f27a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ 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.49.0] + +### What's Changed +* Disable removal of storage markers by @zhassan-aws in https://github.com/model-checking/kani/pull/3083 +* Ensure storage markers are kept in std code by @zhassan-aws in https://github.com/model-checking/kani/pull/3080 +* Implement validity checks by @celinval in https://github.com/model-checking/kani/pull/3085 +* Allow modifies clause for verification only by @feliperodri in https://github.com/model-checking/kani/pull/3098 +* Add optional scatterplot to benchcomp output by @tautschnig in https://github.com/model-checking/kani/pull/3077 +* Expand ${var} in benchcomp variant `env` by @karkhaz in https://github.com/model-checking/kani/pull/3090 +* Add `benchcomp filter` command by @karkhaz in https://github.com/model-checking/kani/pull/3105 +* Upgrade Rust toolchain to 2024-03-29 by @zhassan-aws @celinval @adpaco-aws @feliperodri + +**Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.48.0...kani-0.49.0 + ## [0.48.0] ### Major Changes diff --git a/Cargo.lock b/Cargo.lock index 2a7cbf2d7028..ae2e712e1049 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,7 +97,7 @@ checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "build-kani" -version = "0.48.0" +version = "0.49.0" dependencies = [ "anyhow", "cargo_metadata", @@ -233,7 +233,7 @@ dependencies = [ [[package]] name = "cprover_bindings" -version = "0.48.0" +version = "0.49.0" dependencies = [ "lazy_static", "linear-map", @@ -410,14 +410,14 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "kani" -version = "0.48.0" +version = "0.49.0" dependencies = [ "kani_macros", ] [[package]] name = "kani-compiler" -version = "0.48.0" +version = "0.49.0" dependencies = [ "clap", "cprover_bindings", @@ -438,7 +438,7 @@ dependencies = [ [[package]] name = "kani-driver" -version = "0.48.0" +version = "0.49.0" dependencies = [ "anyhow", "cargo_metadata", @@ -466,7 +466,7 @@ dependencies = [ [[package]] name = "kani-verifier" -version = "0.48.0" +version = "0.49.0" dependencies = [ "anyhow", "home", @@ -475,7 +475,7 @@ dependencies = [ [[package]] name = "kani_macros" -version = "0.48.0" +version = "0.49.0" dependencies = [ "proc-macro-error", "proc-macro2", @@ -485,7 +485,7 @@ dependencies = [ [[package]] name = "kani_metadata" -version = "0.48.0" +version = "0.49.0" dependencies = [ "clap", "cprover_bindings", @@ -992,7 +992,7 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "std" -version = "0.48.0" +version = "0.49.0" dependencies = [ "kani", ] diff --git a/Cargo.toml b/Cargo.toml index 810b4e2bb5eb..e271a3650c03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-verifier" -version = "0.48.0" +version = "0.49.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 b9d0259b3577..ed0e57847e71 100644 --- a/cprover_bindings/Cargo.toml +++ b/cprover_bindings/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "cprover_bindings" -version = "0.48.0" +version = "0.49.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index a5b7fd006180..ffc508e90866 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-compiler" -version = "0.48.0" +version = "0.49.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index 36a979a1366d..3a476922a838 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-driver" -version = "0.48.0" +version = "0.49.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 6b6db72f4a6d..582c20c7bd9f 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_metadata" -version = "0.48.0" +version = "0.49.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index 4da7f91a9ed7..b87536740fcd 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.48.0" +version = "0.49.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 84ed56a9ef12..fe4279fc4366 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_macros" -version = "0.48.0" +version = "0.49.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 29467fddf70b..05f5b4de5635 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.48.0" +version = "0.49.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 b75c373655bd..525c060232a7 100644 --- a/tools/build-kani/Cargo.toml +++ b/tools/build-kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "build-kani" -version = "0.48.0" +version = "0.49.0" edition = "2021" description = "Builds Kani, Sysroot and release bundle." license = "MIT OR Apache-2.0" From 81e11ba9e31ba6d46b59fa0f85f256989126793a Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Fri, 5 Apr 2024 21:04:39 +0200 Subject: [PATCH 061/225] Update the rust toolchain to nightly-2024-04-02 (#3127) Changes required due to: - rust-lang/rust@a325bce3cd Normalize the result of Fields::ty_with_args Resolves: #3125, #3113 --- rust-toolchain.toml | 2 +- .../ValidValues/{write_invalid_fixme.rs => write_invalid.rs} | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) rename tests/kani/ValidValues/{write_invalid_fixme.rs => write_invalid.rs} (88%) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index eee073e29a89..1ee76e0f92f1 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-03-29" +channel = "nightly-2024-04-02" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/kani/ValidValues/write_invalid_fixme.rs b/tests/kani/ValidValues/write_invalid.rs similarity index 88% rename from tests/kani/ValidValues/write_invalid_fixme.rs rename to tests/kani/ValidValues/write_invalid.rs index f04a667a47d1..05d3705bd69a 100644 --- a/tests/kani/ValidValues/write_invalid_fixme.rs +++ b/tests/kani/ValidValues/write_invalid.rs @@ -6,9 +6,6 @@ //! Writing invalid bytes is not UB as long as the incorrect value is not read. //! However, we over-approximate for sake of simplicity and performance. -// Note: We're getting an unexpected compilation error because the type returned -// from StableMIR is `Alias`: https://github.com/model-checking/kani/issues/3113 - use std::num::NonZeroU8; #[kani::proof] From b329c85e6c399269102aa6eb389ae6b320b2f73a Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Fri, 5 Apr 2024 13:09:23 -0700 Subject: [PATCH 062/225] Implement check for `write_bytes` (#3108) In the previous PR #3085, we did not support checks for `write_bytes` which is added in this PR. I am waiting for #3092 to add expected tests. --- .../src/kani_middle/transform/check_values.rs | 21 ++++++++++++++++++- tests/kani/ValidValues/maybe_uninit.rs | 21 +++++++++++++++++++ tests/kani/ValidValues/write_bytes.rs | 21 +++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 tests/kani/ValidValues/maybe_uninit.rs create mode 100644 tests/kani/ValidValues/write_bytes.rs diff --git a/kani-compiler/src/kani_middle/transform/check_values.rs b/kani-compiler/src/kani_middle/transform/check_values.rs index 4888e032af4f..a620dfabb72e 100644 --- a/kani-compiler/src/kani_middle/transform/check_values.rs +++ b/kani-compiler/src/kani_middle/transform/check_values.rs @@ -440,7 +440,13 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> { // The write bytes intrinsic may trigger UB in safe code. // pub unsafe fn write_bytes(dst: *mut T, val: u8, count: usize) // - // We don't support this operation yet. + // This is an over-approximation since writing an invalid value is + // not UB, only reading it will be. + assert_eq!( + args.len(), + 3, + "Unexpected number of arguments for `write_bytes`" + ); let TyKind::RigidTy(RigidTy::RawPtr(target_ty, Mutability::Mut)) = args[0].ty(self.locals).unwrap().kind() else { @@ -449,6 +455,19 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> { let validity = ty_validity_per_offset(&self.machine, target_ty, 0); match validity { Ok(ranges) if ranges.is_empty() => {} + Ok(ranges) => { + let sz = Const::try_from_uint( + target_ty.layout().unwrap().shape().size.bytes() + as u128, + UintTy::Usize, + ) + .unwrap(); + self.push_target(SourceOp::BytesValidity { + target_ty, + rvalue: Rvalue::Repeat(args[1].clone(), sz), + ranges, + }) + } _ => self.push_target(SourceOp::UnsupportedCheck { check: "write_bytes".to_string(), ty: target_ty, diff --git a/tests/kani/ValidValues/maybe_uninit.rs b/tests/kani/ValidValues/maybe_uninit.rs new file mode 100644 index 000000000000..620ad8ef5ba3 --- /dev/null +++ b/tests/kani/ValidValues/maybe_uninit.rs @@ -0,0 +1,21 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z valid-value-checks +//! Check that Kani can identify UB when converting from a maybe uninit(). + +use std::mem::MaybeUninit; +use std::num::NonZeroI64; + +#[kani::proof] +pub fn check_valid_zeroed() { + let maybe = MaybeUninit::zeroed(); + let val: u128 = unsafe { maybe.assume_init() }; + assert_eq!(val, 0); +} + +#[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/kani/ValidValues/write_bytes.rs b/tests/kani/ValidValues/write_bytes.rs new file mode 100644 index 000000000000..e4f73b1f3479 --- /dev/null +++ b/tests/kani/ValidValues/write_bytes.rs @@ -0,0 +1,21 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z valid-value-checks +//! Check that Kani can identify UB when using write_bytes for initializing a variable. +#![feature(core_intrinsics)] + +#[kani::proof] +#[kani::should_panic] +pub fn check_invalid_write() { + let mut val = 'a'; + let ptr = &mut val as *mut char; + // Should fail given that we wrote invalid value to array of char. + unsafe { std::intrinsics::write_bytes(ptr, kani::any(), 1) }; +} + +#[kani::proof] +pub fn check_valid_write() { + let mut val = 10u128; + let ptr = &mut val as *mut _; + unsafe { std::intrinsics::write_bytes(ptr, kani::any(), 1) }; +} From 5490933e4110e6e35031db0e331d0871839e6f0a Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Sat, 6 Apr 2024 08:54:48 +0200 Subject: [PATCH 063/225] Fix TestLocalToolchain jobs for release push (#3131) Upon pushing a release tag, we use the numeric version instead of "latest", which made `kani-latest` a non-existent folder. See https://github.com/model-checking/kani/actions/runs/8574249138 for such an example. Use the `version` variable instead, which will be set to "latest" or the numeric version as appropriate. --- .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 04ac4d20280b..4ae93c291484 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -136,7 +136,7 @@ jobs: - name: Get toolchain version used to setup kani run: | tar zxvf ${{ matrix.prev_job.bundle }} - DATE=$(cat ./kani-latest/rust-toolchain-version | cut -d'-' -f2,3,4) + DATE=$(cat ./kani-${{ matrix.prev_job.version }}/rust-toolchain-version | cut -d'-' -f2,3,4) echo "Nightly date: $DATE" echo "DATE=$DATE" >> $GITHUB_ENV From f68db95b6027b91bf8864f0345777b3a28094bd0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 13:45:18 +0200 Subject: [PATCH 064/225] Automatic cargo update to 2024-04-08 (#3132) Dependency upgrade resulting from `cargo update`. --- Cargo.lock | 47 ++++++++++++++--------------------------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ae2e712e1049..ae711d863430 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -191,13 +191,13 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "comfy-table" -version = "7.1.0" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c64043d6c7b7a4c58e39e7efccfdea7b93d885a795d0c054a69dbbf4dd52686" +checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" dependencies = [ "crossterm", - "strum 0.25.0", - "strum_macros 0.25.3", + "strum", + "strum_macros", "unicode-width", ] @@ -338,9 +338,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "a06fddc2749e0528d2813f95e050e87e52c8cbbae56223b9babf73b3e53b0cc6" dependencies = [ "cfg-if", "libc", @@ -430,8 +430,8 @@ dependencies = [ "serde", "serde_json", "shell-words", - "strum 0.26.2", - "strum_macros 0.26.2", + "strum", + "strum_macros", "tracing", "tracing-subscriber", ] @@ -455,8 +455,8 @@ dependencies = [ "rustc-demangle", "serde", "serde_json", - "strum 0.26.2", - "strum_macros 0.26.2", + "strum", + "strum_macros", "tempfile", "toml", "tracing", @@ -490,8 +490,8 @@ dependencies = [ "clap", "cprover_bindings", "serde", - "strum 0.26.2", - "strum_macros 0.26.2", + "strum", + "strum_macros", ] [[package]] @@ -873,9 +873,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "ryu" @@ -1014,31 +1014,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" - [[package]] name = "strum" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" -[[package]] -name = "strum_macros" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.58", -] - [[package]] name = "strum_macros" version = "0.26.2" From 7cfc8f43286726d7fd79a161179ed4e1adad1a41 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 12:14:17 -0700 Subject: [PATCH 065/225] Bump tests/perf/s2n-quic from `2d5e891` to `1130df6` (#3135) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `2d5e891` to `1130df6`.
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 2d5e891f3fdc..1130df6d2c45 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 2d5e891f3fdc8a88b2d457baceedea5751efaa0d +Subproject commit 1130df6d2c45e0b5345dbcd2b1a6ea2663dac143 From 43475da1c41596c1eb64bcd9e47e7faea1fa7174 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 10 Apr 2024 10:45:55 +0200 Subject: [PATCH 066/225] Restore mdbook-graphviz installation (#3136) mdbook-graphviz has released 0.1.7, which has updated dependencies. Resolves: #3005 --- scripts/setup/ubuntu/install_doc_deps.sh | 2 +- tests/perf/s2n-quic | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/setup/ubuntu/install_doc_deps.sh b/scripts/setup/ubuntu/install_doc_deps.sh index 924687923041..e3f4dd2c0d74 100755 --- a/scripts/setup/ubuntu/install_doc_deps.sh +++ b/scripts/setup/ubuntu/install_doc_deps.sh @@ -4,5 +4,5 @@ set -eux -# cargo install mdbook-graphviz +cargo install mdbook-graphviz DEBIAN_FRONTEND=noninteractive sudo apt-get install --no-install-recommends --yes graphviz diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 1130df6d2c45..2d5e891f3fdc 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 1130df6d2c45e0b5345dbcd2b1a6ea2663dac143 +Subproject commit 2d5e891f3fdc8a88b2d457baceedea5751efaa0d From e906cde630dc4d9105fda37b636a39328c34641c Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 10 Apr 2024 15:22:15 +0200 Subject: [PATCH 067/225] Remove is-a-parameter from gen_stack_variable (#3137) A function-local variable cannot at the same time be a parameter. Alas, all uses of gen_stack_variable passed in `false` for `is_param`, so this wasn't making a difference anyway. --- .../src/codegen_cprover_gotoc/context/goto_ctx.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) 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 3a6501d544d8..095f907228a4 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs @@ -137,7 +137,7 @@ impl<'tcx> GotocCtx<'tcx> { // Generate a Symbol Expression representing a function variable from the MIR pub fn gen_function_local_variable(&mut self, c: u64, fname: &str, t: Type) -> Symbol { - self.gen_stack_variable(c, fname, "var", t, Location::none(), false) + self.gen_stack_variable(c, fname, "var", t, Location::none()) } /// Given a counter `c` a function name `fname, and a prefix `prefix`, generates a new function local variable @@ -149,11 +149,10 @@ impl<'tcx> GotocCtx<'tcx> { prefix: &str, t: Type, loc: Location, - is_param: bool, ) -> Symbol { let base_name = format!("{prefix}_{c}"); let name = format!("{fname}::1::{base_name}"); - let symbol = Symbol::variable(name, base_name, t, loc).with_is_parameter(is_param); + let symbol = Symbol::variable(name, base_name, t, loc); self.symbol_table.insert(symbol.clone()); symbol } @@ -167,8 +166,7 @@ impl<'tcx> GotocCtx<'tcx> { loc: Location, ) -> (Expr, Stmt) { let c = self.current_fn_mut().get_and_incr_counter(); - let var = - self.gen_stack_variable(c, &self.current_fn().name(), "temp", t, loc, false).to_expr(); + let var = self.gen_stack_variable(c, &self.current_fn().name(), "temp", t, loc).to_expr(); let value = value.or_else(|| self.codegen_default_initializer(&var)); let decl = Stmt::decl(var.clone(), value, loc); (var, decl) From b9bd7a8a949105d42ef94591389883545cde88ef Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Tue, 16 Apr 2024 15:02:36 -0700 Subject: [PATCH 068/225] Upgrade toolchain to nightly-2024-04-15 (#3144) Related changes: - https://github.com/rust-lang/rust/pull/118310: Add `Ord::cmp` for primitives as a `BinOp` in MIR - https://github.com/rust-lang/rust/pull/120131: Add support to `Pat` pattern type - https://github.com/rust-lang/rust/pull/122935: Rename CastKind::PointerWithExposedProvenance - https://github.com/rust-lang/rust/pull/123097: Adapt to changes to local_def_path_hash_to_def_id Resolves #3130, #3142 --- cprover_bindings/src/goto_program/expr.rs | 1 + .../codegen_cprover_gotoc/codegen/place.rs | 5 + .../codegen_cprover_gotoc/codegen/rvalue.rs | 122 ++++++++++++------ .../src/codegen_cprover_gotoc/codegen/typ.rs | 8 ++ .../src/kani_middle/stubbing/annotations.rs | 2 +- .../src/kani_middle/stubbing/transform.rs | 2 +- .../src/kani_middle/transform/check_values.rs | 8 +- rust-toolchain.toml | 2 +- 8 files changed, 110 insertions(+), 40 deletions(-) diff --git a/cprover_bindings/src/goto_program/expr.rs b/cprover_bindings/src/goto_program/expr.rs index 28f8afb4fae7..9f97e7a0a599 100644 --- a/cprover_bindings/src/goto_program/expr.rs +++ b/cprover_bindings/src/goto_program/expr.rs @@ -389,6 +389,7 @@ impl Expr { source.is_integer() || source.is_pointer() || source.is_bool() } else if target.is_integer() { source.is_c_bool() + || source.is_bool() || source.is_integer() || source.is_floating_point() || source.is_pointer() diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs index 6b764fb63365..d24e5448c595 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs @@ -292,6 +292,11 @@ impl<'tcx> GotocCtx<'tcx> { "element of {parent_ty:?} is not accessed via field projection" ) } + TyKind::RigidTy(RigidTy::Pat(..)) => { + // See https://github.com/rust-lang/types-team/issues/126 + // for what is currently supported. + unreachable!("projection inside a pattern is not supported, only transmute") + } } } // if we fall here, then we are handling an enum diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index 74622d41ed50..cb78163fe99b 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -20,6 +20,7 @@ use num::bigint::BigInt; use rustc_middle::ty::{TyCtxt, 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::{ AggregateKind, BinOp, CastKind, NullOp, Operand, Place, PointerCoercion, Rvalue, UnOp, @@ -32,9 +33,11 @@ impl<'tcx> GotocCtx<'tcx> { fn codegen_comparison(&mut self, op: &BinOp, e1: &Operand, e2: &Operand) -> Expr { let left_op = self.codegen_operand_stable(e1); let right_op = self.codegen_operand_stable(e2); - let is_float = - matches!(self.operand_ty_stable(e1).kind(), TyKind::RigidTy(RigidTy::Float(..))); - comparison_expr(op, left_op, right_op, is_float) + let left_ty = self.operand_ty_stable(e1); + let right_ty = self.operand_ty_stable(e2); + let res_ty = op.ty(left_ty, right_ty); + let is_float = matches!(left_ty.kind(), TyKind::RigidTy(RigidTy::Float(..))); + self.comparison_expr(op, left_op, right_op, res_ty, is_float) } /// This function codegen comparison for fat pointers. @@ -72,16 +75,18 @@ impl<'tcx> GotocCtx<'tcx> { Expr::statement_expression(body, ret_type).with_location(loc) } else { // Compare data pointer. + let res_ty = op.ty(left_typ, right_typ); let left_ptr = self.codegen_operand_stable(left_op); let left_data = left_ptr.clone().member("data", &self.symbol_table); let right_ptr = self.codegen_operand_stable(right_op); let right_data = right_ptr.clone().member("data", &self.symbol_table); - let data_cmp = comparison_expr(op, left_data.clone(), right_data.clone(), false); + let data_cmp = + self.comparison_expr(op, left_data.clone(), right_data.clone(), res_ty, false); // Compare the slice metadata (this logic could be adapted to compare vtable if needed). let left_len = left_ptr.member("len", &self.symbol_table); let right_len = right_ptr.member("len", &self.symbol_table); - let metadata_cmp = comparison_expr(op, left_len, right_len, false); + let metadata_cmp = self.comparison_expr(op, left_len, right_len, res_ty, false); // Join the results. // https://github.com/rust-lang/rust/pull/29781 @@ -93,10 +98,20 @@ impl<'tcx> GotocCtx<'tcx> { // If data is different, only compare data. // If data is equal, apply operator to metadata. BinOp::Lt | BinOp::Le | BinOp::Ge | BinOp::Gt => { - let data_eq = - comparison_expr(&BinOp::Eq, left_data.clone(), right_data.clone(), false); - let data_strict_comp = - comparison_expr(&get_strict_operator(op), left_data, right_data, false); + let data_eq = self.comparison_expr( + &BinOp::Eq, + left_data.clone(), + right_data.clone(), + res_ty, + false, + ); + let data_strict_comp = self.comparison_expr( + &get_strict_operator(op), + left_data, + right_data, + res_ty, + false, + ); data_strict_comp.or(data_eq.and(metadata_cmp)) } _ => unreachable!("Unexpected operator {:?}", op), @@ -376,7 +391,7 @@ impl<'tcx> GotocCtx<'tcx> { BinOp::BitXor | BinOp::BitAnd | BinOp::BitOr => { self.codegen_unchecked_scalar_binop(op, e1, e2) } - BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { + BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt | BinOp::Cmp => { let op_ty = self.operand_ty_stable(e1); if self.is_fat_pointer_stable(op_ty) { self.codegen_comparison_fat_ptr(op, e1, e2, loc) @@ -681,7 +696,7 @@ impl<'tcx> GotocCtx<'tcx> { | CastKind::FnPtrToPtr | CastKind::PtrToPtr | CastKind::PointerExposeAddress - | CastKind::PointerFromExposedAddress, + | CastKind::PointerWithExposedProvenance, e, t, ) => self.codegen_misc_cast(e, *t), @@ -1260,7 +1275,7 @@ impl<'tcx> GotocCtx<'tcx> { fn check_vtable_size(&mut self, operand_type: Ty, vt_size: Expr) -> Stmt { // Check against the size we get from the layout from the what we // get constructing a value of that type - let ty: Type = self.codegen_ty_stable(operand_type); + let ty = self.codegen_ty_stable(operand_type); let codegen_size = ty.sizeof(&self.symbol_table); assert_eq!(vt_size.int_constant_value().unwrap(), BigInt::from(codegen_size)); @@ -1423,6 +1438,65 @@ impl<'tcx> GotocCtx<'tcx> { } } } + + fn comparison_expr( + &mut self, + op: &BinOp, + left: Expr, + right: Expr, + res_ty: Ty, + is_float: bool, + ) -> Expr { + match op { + BinOp::Eq => { + if is_float { + left.feq(right) + } else { + left.eq(right) + } + } + BinOp::Lt => left.lt(right), + BinOp::Le => left.le(right), + BinOp::Ne => { + if is_float { + left.fneq(right) + } else { + left.neq(right) + } + } + BinOp::Ge => left.ge(right), + BinOp::Gt => left.gt(right), + BinOp::Cmp => { + // Implement https://doc.rust-lang.org/core/cmp/trait.Ord.html as done in cranelift, + // i.e., (left > right) - (left < right) + // Return value is the Ordering enumeration: + // ``` + // #[repr(i8)] + // pub enum Ordering { + // Less = -1, + // Equal = 0, + // Greater = 1, + // } + // ``` + let res_typ = self.codegen_ty_stable(res_ty); + let ValueAbi::Scalar(Scalar::Initialized { value, valid_range }) = + res_ty.layout().unwrap().shape().abi + else { + unreachable!("Unexpected layout") + }; + assert_eq!(valid_range.start, -1i8 as u8 as u128); + assert_eq!(valid_range.end, 1); + let Primitive::Int { length, signed: true } = value else { unreachable!() }; + let scalar_typ = Type::signed_int(length.bits()); + left.clone() + .gt(right.clone()) + .cast_to(scalar_typ.clone()) + .sub(left.lt(right).cast_to(scalar_typ)) + .transmute_to(res_typ, &self.symbol_table) + } + _ => unreachable!(), + } + } } /// Perform a wrapping subtraction of an Expr with a constant "expr - constant" @@ -1445,30 +1519,6 @@ fn wrapping_sub(expr: &Expr, constant: u64) -> Expr { } } -fn comparison_expr(op: &BinOp, left: Expr, right: Expr, is_float: bool) -> Expr { - match op { - BinOp::Eq => { - if is_float { - left.feq(right) - } else { - left.eq(right) - } - } - BinOp::Lt => left.lt(right), - BinOp::Le => left.le(right), - BinOp::Ne => { - if is_float { - left.fneq(right) - } else { - left.neq(right) - } - } - BinOp::Ge => left.ge(right), - BinOp::Gt => left.gt(right), - _ => unreachable!(), - } -} - /// Remove the equality from an operator. Translates `<=` to `<` and `>=` to `>` fn get_strict_operator(op: &BinOp) -> BinOp { match op { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index b5ccf1a83716..64c36dd76cfb 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -595,6 +595,13 @@ impl<'tcx> GotocCtx<'tcx> { ) } } + // This object has the same layout as base. For now, translate this into `(base)`. + // The only difference is the niche. + ty::Pat(base_ty, ..) => { + self.ensure_struct(self.ty_mangled_name(ty), self.ty_pretty_name(ty), |tcx, _| { + tcx.codegen_ty_tuple_like(ty, vec![*base_ty]) + }) + } ty::Alias(..) => { unreachable!("Type should've been normalized already") } @@ -995,6 +1002,7 @@ impl<'tcx> GotocCtx<'tcx> { | ty::Int(_) | ty::RawPtr(_, _) | ty::Ref(..) + | ty::Pat(..) | ty::Tuple(_) | ty::Uint(_) => self.codegen_ty(pointee_type).to_pointer(), diff --git a/kani-compiler/src/kani_middle/stubbing/annotations.rs b/kani-compiler/src/kani_middle/stubbing/annotations.rs index 52b994ab97d2..26e508707207 100644 --- a/kani-compiler/src/kani_middle/stubbing/annotations.rs +++ b/kani-compiler/src/kani_middle/stubbing/annotations.rs @@ -56,7 +56,7 @@ pub fn update_stub_mapping( "duplicate stub mapping: {} mapped to {} and {}", tcx.def_path_str(orig_id), tcx.def_path_str(stub_id), - tcx.def_path_str(tcx.def_path_hash_to_def_id(other, &mut || panic!())) + tcx.def_path_str(tcx.def_path_hash_to_def_id(other, &())) ), ); } diff --git a/kani-compiler/src/kani_middle/stubbing/transform.rs b/kani-compiler/src/kani_middle/stubbing/transform.rs index 16c46990bc6f..f101a6009907 100644 --- a/kani-compiler/src/kani_middle/stubbing/transform.rs +++ b/kani-compiler/src/kani_middle/stubbing/transform.rs @@ -234,7 +234,7 @@ fn deserialize_mapping(tcx: TyCtxt, val: &str) -> HashMap { type Item = (u64, u64); let item_to_def_id = |item: Item| -> DefId { let hash = DefPathHash(Fingerprint::new(item.0, item.1)); - tcx.def_path_hash_to_def_id(hash, &mut || panic!()) + tcx.def_path_hash_to_def_id(hash, &()) }; let pairs: Vec<(Item, Item)> = serde_json::from_str(val).unwrap(); let mut m = HashMap::default(); diff --git a/kani-compiler/src/kani_middle/transform/check_values.rs b/kani-compiler/src/kani_middle/transform/check_values.rs index a620dfabb72e..d62b5807319e 100644 --- a/kani-compiler/src/kani_middle/transform/check_values.rs +++ b/kani-compiler/src/kani_middle/transform/check_values.rs @@ -631,7 +631,7 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> { ty: (rvalue.ty(self.locals).unwrap()), }), CastKind::PointerExposeAddress - | CastKind::PointerFromExposedAddress + | CastKind::PointerWithExposedProvenance | CastKind::PointerCoercion(_) | CastKind::IntToInt | CastKind::FloatToInt @@ -898,6 +898,12 @@ fn ty_validity_per_offset( } } } + RigidTy::Pat(base_ty, ..) => { + // This is similar to a structure with one field and with niche defined. + let mut pat_validity = ty_req(); + pat_validity.append(&mut ty_validity_per_offset(machine_info, *base_ty, 0)?); + Ok(pat_validity) + } RigidTy::Tuple(tys) => { let mut tuple_validity = vec![]; for idx in layout.fields.fields_by_offset_order() { diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 1ee76e0f92f1..be42da187e10 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-04-02" +channel = "nightly-2024-04-15" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From 7b08d7f9dc262a096c7264bd3d00052c64dbff44 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 17 Apr 2024 00:40:32 +0200 Subject: [PATCH 069/225] Fix syntax error in cbmc-update CI job (#3139) It's "env" and not "evn". --- .github/workflows/cbmc-update.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cbmc-update.yml b/.github/workflows/cbmc-update.yml index 74d9caeb0fcf..6f9a6ae3a74b 100644 --- a/.github/workflows/cbmc-update.yml +++ b/.github/workflows/cbmc-update.yml @@ -79,7 +79,7 @@ jobs: token: ${{ github.token }} title: 'CBMC upgrade to ${{ env.CBMC_LATEST }} failed' body: > - Updating CBMC from ${{ evn.CBMC_VERSION }} to ${{ env.CBMC_LATEST }} failed. + Updating CBMC from ${{ env.CBMC_VERSION }} to ${{ env.CBMC_LATEST }} failed. The failed automated run [can be found here.](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) From ec34e01431d70cff72ab8b76f29c347bb086c4c6 Mon Sep 17 00:00:00 2001 From: Kareem Khazem Date: Wed, 17 Apr 2024 00:27:55 +0100 Subject: [PATCH 070/225] Handle errors thrown from benchcomp column exprs (#3145) Prior to this commit, errors thrown when evaluating the text of a benchcomp extra column would crash benchcomp. This could happen, for example, if a column tries to compare an old variant with a new one, but no data for the old variant exists, as seen in this run: https://github.com/model-checking/kani/actions/runs/8700040930/job/23859607740 Forcing the user to do error handling in the column text would make the text even more unwieldy than it already is, so this commit makes the column text evaluate to **** if an exception is raised during evaluation. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- tools/benchcomp/benchcomp/visualizers/__init__.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tools/benchcomp/benchcomp/visualizers/__init__.py b/tools/benchcomp/benchcomp/visualizers/__init__.py index 8de4a9f130b6..1452488c76b1 100644 --- a/tools/benchcomp/benchcomp/visualizers/__init__.py +++ b/tools/benchcomp/benchcomp/visualizers/__init__.py @@ -358,7 +358,20 @@ def _add_extra_columns(self, metrics): for bench, variants in benches.items(): tmp_variants = dict(variants) for column in columns: - variants[column["column_name"]] = column["text"](tmp_variants) + if "column_name" not in column: + logging.error( + "A column specification for metric %s did not " + "contain a column_name field. Each column should " + "have a column name and column text", metric) + sys.exit(1) + try: + variants[column["column_name"]] = column["text"](tmp_variants) + except BaseException: + # This may be reached when evaluating the column text + # throws an exception. The column text is written in a + # YAML file and is typically a simple lambda so can't + # contain sophisticated error handling. + variants[column["column_name"]] = "**ERROR**" @staticmethod From 299b0b3cf94a96dbf09e8efb42858079c0dc1117 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 17 Apr 2024 14:01:05 -0700 Subject: [PATCH 071/225] Bump dependencies and Kani's version to 0.50.0 (#3148) Release notes are the following: ### Major Changes * Fix compilation issue with proc_macro2 (v1.0.80+) and Kani v0.49.0 (https://github.com/model-checking/kani/issues/3138). ### What's Changed * Implement valid value check for `write_bytes` by @celinval in https://github.com/model-checking/kani/pull/3108 * Rust toolchain upgraded to 2024-04-15 by @tautschnig @celinval **Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.49.0...kani-0.50.0 --- CHANGELOG.md | 14 ++++ Cargo.lock | 131 +++++++++++++++++---------------- 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_macros/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- tools/build-kani/Cargo.toml | 2 +- 11 files changed, 92 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd592b2f27a7..61e06601dfc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ 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.50.0] + +### Major Changes +* Fix compilation issue with proc_macro2 (v1.0.80+) and Kani v0.49.0 +(https://github.com/model-checking/kani/issues/3138). + +### What's Changed +* Implement valid value check for `write_bytes` by @celinval in +https://github.com/model-checking/kani/pull/3108 +* Rust toolchain upgraded to 2024-04-15 by @tautschnig @celinval + +**Full Changelog**: +https://github.com/model-checking/kani/compare/kani-0.49.0...kani-0.50.0 + ## [0.49.0] ### What's Changed diff --git a/Cargo.lock b/Cargo.lock index ae711d863430..821dbd9c346c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,9 +73,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "autocfg" @@ -97,7 +97,7 @@ checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "build-kani" -version = "0.49.0" +version = "0.50.0" dependencies = [ "anyhow", "cargo_metadata", @@ -174,7 +174,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -233,7 +233,7 @@ dependencies = [ [[package]] name = "cprover_bindings" -version = "0.49.0" +version = "0.50.0" dependencies = [ "lazy_static", "linear-map", @@ -295,9 +295,9 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "encode_unicode" @@ -338,9 +338,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06fddc2749e0528d2813f95e050e87e52c8cbbae56223b9babf73b3e53b0cc6" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", @@ -410,14 +410,14 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "kani" -version = "0.49.0" +version = "0.50.0" dependencies = [ "kani_macros", ] [[package]] name = "kani-compiler" -version = "0.49.0" +version = "0.50.0" dependencies = [ "clap", "cprover_bindings", @@ -438,7 +438,7 @@ dependencies = [ [[package]] name = "kani-driver" -version = "0.49.0" +version = "0.50.0" dependencies = [ "anyhow", "cargo_metadata", @@ -466,7 +466,7 @@ dependencies = [ [[package]] name = "kani-verifier" -version = "0.49.0" +version = "0.50.0" dependencies = [ "anyhow", "home", @@ -475,17 +475,17 @@ dependencies = [ [[package]] name = "kani_macros" -version = "0.49.0" +version = "0.50.0" dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] name = "kani_metadata" -version = "0.49.0" +version = "0.50.0" dependencies = [ "clap", "cprover_bindings", @@ -571,9 +571,9 @@ dependencies = [ [[package]] name = "num" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" dependencies = [ "num-bigint", "num-complex", @@ -733,18 +733,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -909,29 +909,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -992,7 +992,7 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "std" -version = "0.49.0" +version = "0.50.0" dependencies = [ "kani", ] @@ -1030,7 +1030,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -1045,9 +1045,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.58" +version = "2.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" dependencies = [ "proc-macro2", "quote", @@ -1083,7 +1083,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -1149,7 +1149,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] [[package]] @@ -1315,7 +1315,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -1335,17 +1335,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -1356,9 +1357,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -1368,9 +1369,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -1380,9 +1381,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -1392,9 +1399,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -1404,9 +1411,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -1416,9 +1423,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -1428,15 +1435,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" dependencies = [ "memchr", ] @@ -1464,5 +1471,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.59", ] diff --git a/Cargo.toml b/Cargo.toml index e271a3650c03..93affb02856f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-verifier" -version = "0.49.0" +version = "0.50.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 ed0e57847e71..d199558ff16a 100644 --- a/cprover_bindings/Cargo.toml +++ b/cprover_bindings/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "cprover_bindings" -version = "0.49.0" +version = "0.50.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index ffc508e90866..244172715056 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-compiler" -version = "0.49.0" +version = "0.50.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index 3a476922a838..ab83c2202bc9 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-driver" -version = "0.49.0" +version = "0.50.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 582c20c7bd9f..7936d943556b 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_metadata" -version = "0.49.0" +version = "0.50.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index b87536740fcd..97952dd7ab9e 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.49.0" +version = "0.50.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 fe4279fc4366..a54fb44d8b6c 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_macros" -version = "0.49.0" +version = "0.50.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 05f5b4de5635..6f9a380fc584 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.49.0" +version = "0.50.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 525c060232a7..eabb396e0923 100644 --- a/tools/build-kani/Cargo.toml +++ b/tools/build-kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "build-kani" -version = "0.49.0" +version = "0.50.0" edition = "2021" description = "Builds Kani, Sysroot and release bundle." license = "MIT OR Apache-2.0" From 12635fd10cdc5b81ceb880c19a9cfd9856d0014e Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 18 Apr 2024 16:11:21 -0700 Subject: [PATCH 072/225] Upgrade toolchain to 2024-04-18 and improve toolchain workflow (#3149) The toolchain upgrade itself didn't require any modification, but it looks like the rust toolchain script includes any untracked files to the PR, which is the root cause of the #3146 CI failure. Thus, I made the following changes (each one of them in its own commit): 1. Moved the upgrade step to its own script. 2. Added a bit of debugging and doc to the script. 3. Added a new step that cleans the workspace before the PR creation. 4. Actually update the toolchain. 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/toolchain-upgrade.yml | 41 ++-------------- .gitignore | 4 ++ rust-toolchain.toml | 2 +- scripts/toolchain_update.sh | 65 +++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 37 deletions(-) create mode 100755 scripts/toolchain_update.sh diff --git a/.github/workflows/toolchain-upgrade.yml b/.github/workflows/toolchain-upgrade.yml index 8252bfb826e6..a4b95ea195f0 100644 --- a/.github/workflows/toolchain-upgrade.yml +++ b/.github/workflows/toolchain-upgrade.yml @@ -30,42 +30,11 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - current_toolchain_date=$(grep ^channel rust-toolchain.toml | sed 's/.*nightly-\(.*\)"/\1/') - echo "current_toolchain_date=$current_toolchain_date" >> $GITHUB_ENV - current_toolchain_epoch=$(date --date $current_toolchain_date +%s) - next_toolchain_date=$(date --date "@$(($current_toolchain_epoch + 86400))" +%Y-%m-%d) - echo "next_toolchain_date=$next_toolchain_date" >> $GITHUB_ENV - if gh issue list -S \ - "Toolchain upgrade to nightly-$next_toolchain_date failed" \ - --json number,title | grep title ; then - echo "next_step=none" >> $GITHUB_ENV - elif ! git ls-remote --exit-code origin toolchain-$next_toolchain_date ; then - echo "next_step=create_pr" >> $GITHUB_ENV - sed -i "/^channel/ s/$current_toolchain_date/$next_toolchain_date/" rust-toolchain.toml - git diff - git clone --filter=tree:0 https://github.com/rust-lang/rust rust.git - cd rust.git - current_toolchain_hash=$(curl https://static.rust-lang.org/dist/$current_toolchain_date/channel-rust-nightly-git-commit-hash.txt) - echo "current_toolchain_hash=$current_toolchain_hash" >> $GITHUB_ENV - next_toolchain_hash=$(curl https://static.rust-lang.org/dist/$next_toolchain_date/channel-rust-nightly-git-commit-hash.txt) - echo "next_toolchain_hash=$next_toolchain_hash" >> $GITHUB_ENV - EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) - echo "git_log<<$EOF" >> $GITHUB_ENV - git log --oneline $current_toolchain_hash..$next_toolchain_hash | \ - sed 's#^#https://github.com/rust-lang/rust/commit/#' >> $GITHUB_ENV - echo "$EOF" >> $GITHUB_ENV - cd .. - rm -rf rust.git - if ! cargo build-dev ; then - echo "next_step=create_issue" >> $GITHUB_ENV - else - if ! ./scripts/kani-regression.sh ; then - echo "next_step=create_issue" >> $GITHUB_ENV - fi - fi - else - echo "next_step=none" >> $GITHUB_ENV - fi + source scripts/toolchain_update.sh + + - name: Clean untracked files + run: git clean -f + - name: Create Pull Request if: ${{ env.next_step == 'create_pr' }} uses: peter-evans/create-pull-request@v6 diff --git a/.gitignore b/.gitignore index aae8f479aac9..a2defc0df119 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,10 @@ no_llvm_build /tmp/ # Created by default with `src/ci/docker/run.sh` /obj/ +# Created by kani-compiler +*.rlib +*.rmeta +*.mir ## Temporary files *~ diff --git a/rust-toolchain.toml b/rust-toolchain.toml index be42da187e10..2d65b1576d5f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-04-15" +channel = "nightly-2024-04-18" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/scripts/toolchain_update.sh b/scripts/toolchain_update.sh new file mode 100755 index 000000000000..856c346e542c --- /dev/null +++ b/scripts/toolchain_update.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +# This script is part of our CI nightly job to bump the toolchain version. +# It will potentially update the rust-toolchain.toml file, and run the +# regression. +# +# In order to manually run this script, you will need to do the following: +# +# 1. Set $GITHUB_ENV to point to an output file. +# 2. Install and configure GitHub CLI + +set -eu + +current_toolchain_date=$(grep ^channel rust-toolchain.toml | sed 's/.*nightly-\(.*\)"/\1/') +echo "current_toolchain_date=$current_toolchain_date" >> $GITHUB_ENV + +current_toolchain_epoch=$(date --date $current_toolchain_date +%s) +next_toolchain_date=$(date --date "@$(($current_toolchain_epoch + 86400))" +%Y-%m-%d) +echo "next_toolchain_date=$next_toolchain_date" >> $GITHUB_ENV + +echo "------ Start upgrade ------" +echo "- current: ${current_toolchain_date}" +echo "- next: ${next_toolchain_date}" +echo "---------------------------" + +if gh issue list -S \ + "Toolchain upgrade to nightly-$next_toolchain_date failed" \ + --json number,title | grep title +then + echo "Skip update: Found existing issue" + echo "next_step=none" >> $GITHUB_ENV +elif ! git ls-remote --exit-code origin toolchain-$next_toolchain_date +then + echo "next_step=create_pr" >> $GITHUB_ENV + + # Modify rust-toolchain file + sed -i "/^channel/ s/$current_toolchain_date/$next_toolchain_date/" rust-toolchain.toml + + git diff + git clone --filter=tree:0 https://github.com/rust-lang/rust rust.git + cd rust.git + current_toolchain_hash=$(curl https://static.rust-lang.org/dist/$current_toolchain_date/channel-rust-nightly-git-commit-hash.txt) + echo "current_toolchain_hash=$current_toolchain_hash" >> $GITHUB_ENV + + next_toolchain_hash=$(curl https://static.rust-lang.org/dist/$next_toolchain_date/channel-rust-nightly-git-commit-hash.txt) + echo "next_toolchain_hash=$next_toolchain_hash" >> $GITHUB_ENV + + EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) + echo "git_log<<$EOF" >> $GITHUB_ENV + + git log --oneline $current_toolchain_hash..$next_toolchain_hash | \ + sed 's#^#https://github.com/rust-lang/rust/commit/#' >> $GITHUB_ENV + echo "$EOF" >> $GITHUB_ENV + + cd .. + rm -rf rust.git + if ! ./scripts/kani-regression.sh ; then + echo "next_step=create_issue" >> $GITHUB_ENV + fi +else + echo "Skip update: Found existing branch" + echo "next_step=none" >> $GITHUB_ENV +fi From 72d03caa720a0f257a2ae1fe9d594eb2f7b02507 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Apr 2024 09:47:32 +0200 Subject: [PATCH 073/225] Automatic toolchain upgrade to nightly-2024-04-19 (#3150) Update Rust toolchain from nightly-2024-04-18 to nightly-2024-04-19 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/becebb3158149a115cad8a402612e25436a7e37b up to https://github.com/rust-lang/rust/commit/e3181b091e88321f5ea149afed6db0edf0a4f37b. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 2d65b1576d5f..b1cbf7169fea 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-04-18" +channel = "nightly-2024-04-19" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From c7067cf9dc35f48c6d1e7ac20ab2be107f3c4021 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Fri, 19 Apr 2024 13:41:28 -0700 Subject: [PATCH 074/225] Stabilize cover statement and update contracts RFC (#3091) I would like to propose that we stabilize the cover statement as is. Any further improvements or changes can be done separately, with or without an RFC. I am also updating the contracts RFC status since parts of it have been integrated to Kani, but it is still unstable. ### Call-out This PR requires at least 2 approvals. --- library/kani/src/mem.rs | 2 +- rfc/src/rfcs/0003-cover-statement.md | 12 ++++++++---- rfc/src/rfcs/0009-function-contracts.md | 7 ++++--- scripts/build-docs.sh | 4 +++- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/library/kani/src/mem.rs b/library/kani/src/mem.rs index 203eae2229c4..43062ebed6a1 100644 --- a/library/kani/src/mem.rs +++ b/library/kani/src/mem.rs @@ -44,7 +44,7 @@ use std::ptr::{DynMetadata, NonNull, Pointee}; /// Note that an unaligned pointer is still considered valid. /// /// TODO: Kani should automatically add those checks when a de-reference happens. -/// https://github.com/model-checking/kani/issues/2975 +/// /// /// This function will either panic or return `true`. This is to make it easier to use it in /// contracts. diff --git a/rfc/src/rfcs/0003-cover-statement.md b/rfc/src/rfcs/0003-cover-statement.md index 6839af22f929..de452654392b 100644 --- a/rfc/src/rfcs/0003-cover-statement.md +++ b/rfc/src/rfcs/0003-cover-statement.md @@ -1,8 +1,8 @@ - **Feature Name:** Cover statement (`cover-statement`) - **Feature Request Issue:** - **RFC PR:** -- **Status:** Unstable -- **Version:** 1 +- **Status:** Stable +- **Version:** 2 ------------------- @@ -138,8 +138,12 @@ However, if the condition can indeed be covered, verification would fail due to ## Open questions -Should we allow format arguments in the macro, e.g. `kani::cover!(x > y, "{} can be greater than {}", x, y)`? -Users may expect this to be supported since the macro looks similar to the `assert` macro, but Kani doesn't include the formatted string in the message description, since it's not available at compile time. +- ~Should we allow format arguments in the macro, e.g. `kani::cover!(x > y, "{} can be greater than {}", x, y)`? +Users may expect this to be supported since the macro looks similar to the `assert` macro, but Kani doesn't include the formatted string in the message description, since it's not available at compile time.~ + - For now, this macro will not accept format arguments, since this + is not well handled by Kani. + This is an extesion to this API that can be easily added later on if Kani + ever supports runtime formatting. ## Other Considerations diff --git a/rfc/src/rfcs/0009-function-contracts.md b/rfc/src/rfcs/0009-function-contracts.md index e26592080822..dae805a7db72 100644 --- a/rfc/src/rfcs/0009-function-contracts.md +++ b/rfc/src/rfcs/0009-function-contracts.md @@ -1,8 +1,8 @@ - **Feature Name:** Function Contracts - **Feature Request Issue:** [#2652](https://github.com/model-checking/kani/issues/2652) and [Milestone](https://github.com/model-checking/kani/milestone/31) - **RFC PR:** [#2620](https://github.com/model-checking/kani/pull/2620) -- **Status:** Under Review -- **Version:** 0 +- **Status:** Unstable +- **Version:** 1 - **Proof-of-concept:** [features/contracts](https://github.com/model-checking/kani/tree/features/contracts) - **Feature Gate:** `-Zfunction-contracts`, enforced by compile time error[^gate] @@ -893,4 +893,5 @@ times larger than what they expect the function will touch). [^stubcheck]: Kani cannot report the occurrence of a contract function to check in abstracted functions as errors, because the mechanism is needed to verify - mutually recursive functions. \ No newline at end of file + mutually recursive functions. + diff --git a/scripts/build-docs.sh b/scripts/build-docs.sh index cd40a2edabad..2e2c10b052f6 100755 --- a/scripts/build-docs.sh +++ b/scripts/build-docs.sh @@ -28,8 +28,10 @@ else curl -sSL -o "$FILE" "$URL" echo "$EXPECTED_HASH $FILE" | sha256sum -c - tar zxf $FILE + MDBOOK=${SCRIPT_DIR}/mdbook + else + MDBOOK=mdbook fi - MDBOOK=${SCRIPT_DIR}/mdbook fi KANI_DIR=$SCRIPT_DIR/.. From de6a255a232523357dbeae8490508e8fdd6947f2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:51:45 +0200 Subject: [PATCH 075/225] Automatic toolchain upgrade to nightly-2024-04-20 (#3154) Update Rust toolchain from nightly-2024-04-19 to nightly-2024-04-20 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/e3181b091e88321f5ea149afed6db0edf0a4f37b up to https://github.com/rust-lang/rust/commit/f9b16149208c8a8a349c32813312716f6603eb6f. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index b1cbf7169fea..f4634f5b7b43 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-04-19" +channel = "nightly-2024-04-20" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From 9b297a36d6b80e5f09dbbf0183c44b6578d9273c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:24:51 +0000 Subject: [PATCH 076/225] Bump tests/perf/s2n-quic from `2d5e891` to `5f88e54` (#3140) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `2d5e891` to `5f88e54`. --- 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 2d5e891f3fdc..5f88e5498215 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 2d5e891f3fdc8a88b2d457baceedea5751efaa0d +Subproject commit 5f88e549821518e71b550faf353a8b9970a29deb From 4ee5eb5e55302d8cbbe8475c313af6862e2b891c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 17:07:41 +0200 Subject: [PATCH 077/225] Automatic cargo update to 2024-04-22 (#3157) Dependency upgrade resulting from `cargo update`. --- Cargo.lock | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 821dbd9c346c..6f45be0e419e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,7 +174,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -480,7 +480,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -860,9 +860,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" dependencies = [ "bitflags 2.5.0", "errno", @@ -924,7 +924,7 @@ checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1030,7 +1030,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1045,9 +1045,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.59" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -1068,22 +1068,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1119,9 +1119,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.9" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap", "serde", @@ -1149,7 +1149,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1471,5 +1471,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.60", ] From 86d5f30f108a7db9894db1241cccba50b126a6d2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 15:39:21 +0000 Subject: [PATCH 078/225] Automatic toolchain upgrade to nightly-2024-04-21 (#3158) Update Rust toolchain from nightly-2024-04-20 to nightly-2024-04-21 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/f9b16149208c8a8a349c32813312716f6603eb6f up to https://github.com/rust-lang/rust/commit/dbce3b43b6cb34dd3ba12c3ec6f708fe68e9c3df. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f4634f5b7b43..70848743b686 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-04-20" +channel = "nightly-2024-04-21" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From 734f6817dc8846330c5717d6c4a7089c17f2dd28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 10:28:02 -0700 Subject: [PATCH 079/225] Bump tests/perf/s2n-quic from `5f88e54` to `9730578` (#3159) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `5f88e54` to `9730578`.
Commits
  • 9730578 chore: release 1.37.0 (#2187)
  • b862ad9 s2n-quic-dc: initial commit (#2185)
  • e0f224b feat(s2n-quic-core): allow forced PTO transmissions (#2130)
  • bfb921d feat(s2n-quic-core): Add ability to create an incremental reader initialized ...
  • 23b07e4 feat(s2n-quic): allow disabling active connection migration support (#2182)
  • 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 5f88e5498215..9730578c0d56 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 5f88e549821518e71b550faf353a8b9970a29deb +Subproject commit 9730578c0d562d80bbbe663161b3a5408ed3116c From a4e69eb0b1f55387e8543d1f27493f3979028d76 Mon Sep 17 00:00:00 2001 From: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> Date: Mon, 22 Apr 2024 19:45:21 -0400 Subject: [PATCH 080/225] Fix cargo audit error (#3160) Fixes cargo audit CI job by updating `rustix`. 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 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f45be0e419e..627755fd91b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -860,9 +860,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.33" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno", From 180e10c54199240e0648252a497916020f5924e2 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 23 Apr 2024 02:31:55 +0200 Subject: [PATCH 081/225] Fix cbmc-update CI job (#3156) We had a spurious update attempt logged in #3155 for the job prior to this fix would empty out the version strings. This was caused by use of undefined variables. Resolves: #3155 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: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Co-authored-by: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> --- .github/workflows/cbmc-update.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cbmc-update.yml b/.github/workflows/cbmc-update.yml index 6f9a6ae3a74b..5fe8a866c0e4 100644 --- a/.github/workflows/cbmc-update.yml +++ b/.github/workflows/cbmc-update.yml @@ -30,7 +30,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - grep ^CBMC_VERSION kani-dependencies >> $GITHUB_ENV + grep ^CBMC_VERSION kani-dependencies | sed 's/"//g' >> $GITHUB_ENV CBMC_LATEST=$(gh -R diffblue/cbmc release list | grep Latest | awk '{print $1}' | cut -f2 -d-) echo "CBMC_LATEST=$CBMC_LATEST" >> $GITHUB_ENV # check whether the version has changed at all @@ -47,8 +47,8 @@ jobs: elif ! git ls-remote --exit-code origin cbmc-$CBMC_LATEST ; then CBMC_LATEST_MAJOR=$(echo $CBMC_LATEST | cut -f1 -d.) CBMC_LATEST_MINOR=$(echo $CBMC_LATEST | cut -f2 -d.) - sed -i "s/^CBMC_MAJOR=.*/CBMC_MAJOR=\"$CBMC_MAJOR\"/" kani-dependencies - sed -i "s/^CBMC_MINOR=.*/CBMC_MINOR=\"$CBMC_MINOR\"/" kani-dependencies + sed -i "s/^CBMC_MAJOR=.*/CBMC_MAJOR=\"$CBMC_LATEST_MAJOR\"/" kani-dependencies + sed -i "s/^CBMC_MINOR=.*/CBMC_MINOR=\"$CBMC_LATEST_MINOR\"/" kani-dependencies sed -i "s/^CBMC_VERSION=.*/CBMC_VERSION=\"$CBMC_LATEST\"/" kani-dependencies git diff if ! ./scripts/kani-regression.sh ; then From 0685c6fa1b27081262766347c2d5faed179b64c8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 28 Apr 2024 23:24:37 -0700 Subject: [PATCH 082/225] Automatic cargo update to 2024-04-29 (#3165) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 133 ++++++++++++++--------------------------------------- 1 file changed, 35 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 627755fd91b3..62f2c913238d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,12 +83,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.5.0" @@ -277,7 +271,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.5.0", + "bitflags", "crossterm_winapi", "libc", "parking_lot", @@ -323,9 +317,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "getopts" @@ -355,9 +349,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", ] @@ -524,9 +518,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -668,9 +662,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -678,15 +672,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -801,11 +795,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] @@ -864,7 +858,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -909,18 +903,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", @@ -1213,9 +1207,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "unsafe-libyaml" @@ -1296,11 +1290,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys", ] [[package]] @@ -1315,22 +1309,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -1339,46 +1318,28 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -1391,48 +1352,24 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.5" @@ -1441,9 +1378,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" +checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" dependencies = [ "memchr", ] From df3a38344167294167555e4477ed545979330fb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 15:20:41 -0400 Subject: [PATCH 083/225] Bump tests/perf/s2n-quic from `9730578` to `1436af7` (#3166) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `9730578` to `1436af7`.
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 9730578c0d56..1436af712b6e 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 9730578c0d562d80bbbe663161b3a5408ed3116c +Subproject commit 1436af712b6e73edc11640dc7c3cae23e456c0a8 From 9e34364ff374d469938cf7519ca0705c496f5dbb Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 30 Apr 2024 22:35:51 +0200 Subject: [PATCH 084/225] Do not assume that ZST-typed symbols refer to unique objects (#3134) The Rust specification does not guarantee that ZST-typed symbols are backed by unique objects, and `rustc` appears to make use of this as can be demonstrated for both locals and statics. For parameters no such example has been found, but as there remains a lack of a guarantee we err on the safe side. Resolves: #3129 --- .../codegen/intrinsic.rs | 32 ++++++++++----- .../codegen_cprover_gotoc/codegen/place.rs | 39 +++++++++++++++++-- library/kani/src/mem.rs | 33 ++++------------ .../function-contract/valid_ptr.expected | 2 +- tests/expected/zst/expected | 4 ++ tests/expected/zst/main.rs | 19 +++++++++ tests/kani/Closure/zst_param.rs | 3 +- tests/kani/MemPredicates/thin_ptr_validity.rs | 2 +- 8 files changed, 92 insertions(+), 42 deletions(-) create mode 100644 tests/expected/zst/expected create mode 100644 tests/expected/zst/main.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 8a61d46ed518..4d201a6f8cc4 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -1221,7 +1221,9 @@ impl<'tcx> GotocCtx<'tcx> { // `raw_eq` determines whether the raw bytes of two values are equal. // https://doc.rust-lang.org/core/intrinsics/fn.raw_eq.html // - // The implementation below calls `memcmp` and returns equal if the result is zero. + // The implementation below calls `memcmp` and returns equal if the result is zero, and + // immediately returns zero when ZSTs are compared to mimic what compare_bytes and our memcmp + // hook do. // // TODO: It's UB to call `raw_eq` if any of the bytes in the first or second // arguments are uninitialized. At present, we cannot detect if there is @@ -1240,13 +1242,17 @@ impl<'tcx> GotocCtx<'tcx> { let dst = fargs.remove(0).cast_to(Type::void_pointer()); let val = fargs.remove(0).cast_to(Type::void_pointer()); let layout = self.layout_of_stable(ty); - let sz = Expr::int_constant(layout.size.bytes(), Type::size_t()) - .with_size_of_annotation(self.codegen_ty_stable(ty)); - let e = BuiltinFn::Memcmp - .call(vec![dst, val, sz], loc) - .eq(Type::c_int().zero()) - .cast_to(Type::c_bool()); - self.codegen_expr_to_place_stable(p, e) + if layout.size.bytes() == 0 { + self.codegen_expr_to_place_stable(p, Expr::int_constant(1, Type::c_bool())) + } else { + let sz = Expr::int_constant(layout.size.bytes(), Type::size_t()) + .with_size_of_annotation(self.codegen_ty_stable(ty)); + let e = BuiltinFn::Memcmp + .call(vec![dst, val, sz], loc) + .eq(Type::c_int().zero()) + .cast_to(Type::c_bool()); + self.codegen_expr_to_place_stable(p, e) + } } // This is an operation that is primarily relevant for stacked borrow @@ -1856,8 +1862,14 @@ impl<'tcx> GotocCtx<'tcx> { "`dst` must be properly aligned", loc, ); - let expr = dst.dereference().assign(src, loc); - Stmt::block(vec![align_check, expr], loc) + let deref = dst.dereference(); + if deref.typ().sizeof(&self.symbol_table) == 0 { + // do not attempt to dereference (and assign) a ZST + align_check + } else { + let expr = deref.assign(src, loc); + Stmt::block(vec![align_check, expr], loc) + } } /// Sets `count * size_of::()` bytes of memory starting at `dst` to `val` diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs index d24e5448c595..56b7da2e7628 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs @@ -11,7 +11,7 @@ use crate::codegen_cprover_gotoc::codegen::typ::std_pointee_type; use crate::codegen_cprover_gotoc::utils::{dynamic_fat_ptr, slice_fat_ptr}; use crate::codegen_cprover_gotoc::GotocCtx; use crate::unwrap_or_return_codegen_unimplemented; -use cbmc::goto_program::{Expr, Location, Type}; +use cbmc::goto_program::{Expr, ExprValue, Location, Stmt, Type}; use rustc_middle::ty::layout::LayoutOf; use rustc_smir::rustc_internal; use rustc_target::abi::{TagEncoding, Variants}; @@ -636,6 +636,14 @@ impl<'tcx> GotocCtx<'tcx> { } } + fn is_zst_object(&self, expr: &Expr) -> bool { + match expr.value() { + ExprValue::Symbol { .. } => expr.typ().sizeof(&self.symbol_table) == 0, + ExprValue::Member { lhs, .. } => self.is_zst_object(lhs), + _ => false, + } + } + /// Codegen the reference to a given place. /// We currently have a somewhat weird way of handling ZST. /// - For `*(&T)` where `T: Unsized`, the projection's `goto_expr` is a thin pointer, so we @@ -647,8 +655,33 @@ impl<'tcx> GotocCtx<'tcx> { let projection = unwrap_or_return_codegen_unimplemented!(self, self.codegen_place_stable(place)); if self.use_thin_pointer_stable(place_ty) { - // Just return the address of the place dereferenced. - projection.goto_expr.address_of() + // For ZST objects rustc does not necessarily generate any actual objects. + let need_not_be_an_object = self.is_zst_object(&projection.goto_expr); + let address_of = projection.goto_expr.clone().address_of(); + if need_not_be_an_object { + // Create a non-deterministic numeric value, assume it is non-zero and (when + // interpreted as an address) of proper alignment for the type, and cast that + // numeric value to a pointer type. + let loc = projection.goto_expr.location(); + let (var, decl) = + self.decl_temp_variable(Type::size_t(), Some(Type::size_t().nondet()), *loc); + let assume_non_zero = + Stmt::assume(var.clone().neq(Expr::int_constant(0, var.typ().clone())), *loc); + let layout = self.layout_of_stable(place_ty); + let alignment = Expr::int_constant(layout.align.abi.bytes(), var.typ().clone()); + let assume_aligned = Stmt::assume( + var.clone().rem(alignment).eq(Expr::int_constant(0, var.typ().clone())), + *loc, + ); + let cast_to_pointer_type = var.cast_to(address_of.typ().clone()).as_stmt(*loc); + Expr::statement_expression( + vec![decl, assume_non_zero, assume_aligned, cast_to_pointer_type], + address_of.typ().clone(), + ) + } else { + // Just return the address of the place dereferenced. + address_of + } } else if place_ty == pointee_type(self.local_ty_stable(place.local)).unwrap() { // Just return the fat pointer if this is a simple &(*local). projection.fat_ptr_goto_expr.unwrap() diff --git a/library/kani/src/mem.rs b/library/kani/src/mem.rs index 43062ebed6a1..b857247021ec 100644 --- a/library/kani/src/mem.rs +++ b/library/kani/src/mem.rs @@ -61,25 +61,18 @@ where crate::assert(!ptr.is_null(), "Expected valid pointer, but found `null`"); let (thin_ptr, metadata) = ptr.to_raw_parts(); - can_read(&metadata, thin_ptr) -} - -fn can_read(metadata: &M, data_ptr: *const ()) -> bool -where - M: PtrProperties, - T: ?Sized, -{ - let marker = Internal; - let sz = metadata.pointee_size(marker); - if metadata.dangling(marker) as *const _ == data_ptr { - crate::assert(sz == 0, "Dangling pointer is only valid for zero-sized access") + let sz = metadata.pointee_size(Internal); + if sz == 0 { + true // ZST pointers are always valid } else { + // Note that this branch can't be tested in concrete execution as `is_read_ok` needs to be + // stubbed. crate::assert( - is_read_ok(data_ptr, sz), + is_read_ok(thin_ptr, sz), "Expected valid pointer, but found dangling pointer", ); + true } - true } mod private { @@ -256,18 +249,6 @@ mod tests { assert_valid_ptr(vec_ptr); } - #[test] - #[should_panic(expected = "Dangling pointer is only valid for zero-sized access")] - fn test_dangling_char() { - test_dangling_of_t::(); - } - - #[test] - #[should_panic(expected = "Dangling pointer is only valid for zero-sized access")] - fn test_dangling_slice() { - test_dangling_of_t::<&str>(); - } - #[test] #[should_panic(expected = "Expected valid pointer, but found `null`")] fn test_null_fat_ptr() { diff --git a/tests/expected/function-contract/valid_ptr.expected b/tests/expected/function-contract/valid_ptr.expected index a9dc8dd5992d..f45cb4e2e826 100644 --- a/tests/expected/function-contract/valid_ptr.expected +++ b/tests/expected/function-contract/valid_ptr.expected @@ -1,5 +1,5 @@ Checking harness pre_condition::harness_invalid_ptr... -Failed Checks: Dangling pointer is only valid for zero-sized access +Failed Checks: Expected valid pointer, but found dangling pointer Checking harness pre_condition::harness_stack_ptr... VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/zst/expected b/tests/expected/zst/expected new file mode 100644 index 000000000000..bec891bea92c --- /dev/null +++ b/tests/expected/zst/expected @@ -0,0 +1,4 @@ +Status: FAILURE\ +Description: "dereference failure: pointer NULL" + +VERIFICATION:- FAILED diff --git a/tests/expected/zst/main.rs b/tests/expected/zst/main.rs new file mode 100644 index 000000000000..587e2608c870 --- /dev/null +++ b/tests/expected/zst/main.rs @@ -0,0 +1,19 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// This example demonstrates that rustc may choose not to allocate unique locations to ZST objects. +#[repr(C)] +#[derive(Copy, Clone)] +struct Z(i8, i64); + +struct Y; + +#[kani::proof] +fn test_z() -> Z { + let m = Y; + let n = Y; + let zz = Z(1, -1); + + let ptr: *const Z = if &n as *const _ == &m as *const _ { std::ptr::null() } else { &zz }; + unsafe { *ptr } +} diff --git a/tests/kani/Closure/zst_param.rs b/tests/kani/Closure/zst_param.rs index 3eee1e5ac672..7a93619a9e16 100644 --- a/tests/kani/Closure/zst_param.rs +++ b/tests/kani/Closure/zst_param.rs @@ -17,7 +17,8 @@ fn check_zst_param() { let input = kani::any(); let closure = |a: Void, out: usize, b: Void| { kani::cover!(); - assert!(&a as *const Void != &b as *const Void, "Should succeed"); + assert!(&a as *const Void != std::ptr::null(), "Should succeed"); + assert!(&b as *const Void != std::ptr::null(), "Should succeed"); out }; let output = invoke(input, closure); diff --git a/tests/kani/MemPredicates/thin_ptr_validity.rs b/tests/kani/MemPredicates/thin_ptr_validity.rs index 638b7e1d1fc8..49e9c403cda8 100644 --- a/tests/kani/MemPredicates/thin_ptr_validity.rs +++ b/tests/kani/MemPredicates/thin_ptr_validity.rs @@ -46,10 +46,10 @@ mod invalid_access { } #[kani::proof] - #[kani::should_panic] pub fn check_invalid_zst() { let raw_ptr: *const [char; 0] = unsafe { new_dead_ptr::<[char; 2]>(['a', 'b']) } as *const _; + // ZST pointer are always valid assert_valid_ptr(raw_ptr); } From 36f715c1c9848c7c46b2ed1fbe41acd92045131d Mon Sep 17 00:00:00 2001 From: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Date: Fri, 3 May 2024 14:48:09 -0400 Subject: [PATCH 085/225] Fix copyright check for `expected` tests (#3170) This PR modifies the pattern used to exclude files from the copyright check for `expected` files. This ensures we check the copyright in files under `tests/expected/` while it skips the check for `expected` and `*.expected` files. It also adds/modifies copyright headers for some files that weren't being checked until now. Resolves #3141 --- scripts/ci/copyright-exclude | 2 +- tests/expected/coroutines/main.rs | 2 +- tests/expected/coroutines/pin/main.rs | 2 +- tests/expected/function-contract/modifies/refcell_fixme.rs | 2 ++ tests/expected/offset-wraps-around/main.rs | 2 +- tests/expected/slice_c_str/c_str_fixme.rs | 2 ++ 6 files changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/ci/copyright-exclude b/scripts/ci/copyright-exclude index 358edc372af2..157d88fbc379 100644 --- a/scripts/ci/copyright-exclude +++ b/scripts/ci/copyright-exclude @@ -10,7 +10,7 @@ Cargo.lock LICENSE-APACHE LICENSE-MIT editorconfig -expected +expected$ gitattributes gitignore gitmodules diff --git a/tests/expected/coroutines/main.rs b/tests/expected/coroutines/main.rs index a49d9944d0f1..d94524c05d63 100644 --- a/tests/expected/coroutines/main.rs +++ b/tests/expected/coroutines/main.rs @@ -1,4 +1,4 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT #![feature(coroutines, coroutine_trait)] diff --git a/tests/expected/coroutines/pin/main.rs b/tests/expected/coroutines/pin/main.rs index 0052715377ec..7d33005bb138 100644 --- a/tests/expected/coroutines/pin/main.rs +++ b/tests/expected/coroutines/pin/main.rs @@ -1,4 +1,4 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // Test contains a call to a coroutine via a Pin diff --git a/tests/expected/function-contract/modifies/refcell_fixme.rs b/tests/expected/function-contract/modifies/refcell_fixme.rs index 8ae9cb390eb7..9cfac8777959 100644 --- a/tests/expected/function-contract/modifies/refcell_fixme.rs +++ b/tests/expected/function-contract/modifies/refcell_fixme.rs @@ -1,3 +1,5 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT use std::cell::RefCell; use std::ops::Deref; diff --git a/tests/expected/offset-wraps-around/main.rs b/tests/expected/offset-wraps-around/main.rs index 62ec6902020b..3e83eacc4c2d 100644 --- a/tests/expected/offset-wraps-around/main.rs +++ b/tests/expected/offset-wraps-around/main.rs @@ -1,4 +1,4 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // Check that a high offset causes a "wrapping around" behavior in CBMC. diff --git a/tests/expected/slice_c_str/c_str_fixme.rs b/tests/expected/slice_c_str/c_str_fixme.rs index 894746772100..ede6c814e1a0 100644 --- a/tests/expected/slice_c_str/c_str_fixme.rs +++ b/tests/expected/slice_c_str/c_str_fixme.rs @@ -1,3 +1,5 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT #![feature(rustc_private)] #![feature(c_str_literals)] //! FIXME: From c0e4e1bdb6fbcd1a4850e0ca674a19f568f0d918 Mon Sep 17 00:00:00 2001 From: "Felipe R. Monteiro" Date: Fri, 3 May 2024 15:25:17 -0400 Subject: [PATCH 086/225] Remove kani::Arbitrary from the modifies contract instrumentation (#3169) This is an additional fix for https://github.com/model-checking/kani/pull/3098. With this fix, Kani should be able to check for contracts using modifies clauses that contain references to types that doesn't implement `kani::Arbitrary`. The verification will still fail if the same contract is used as a verified stub. --------- Signed-off-by: Felipe R. Monteiro --- .../src/sysroot/contracts/check.rs | 54 +++++++++++++++---- ...simple_only_verification_modifies.expected | 1 + .../simple_only_verification_modifies.rs | 28 ++++++++++ 3 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 tests/expected/function-contract/modifies/simple_only_verification_modifies.expected create mode 100644 tests/expected/function-contract/modifies/simple_only_verification_modifies.rs diff --git a/library/kani_macros/src/sysroot/contracts/check.rs b/library/kani_macros/src/sysroot/contracts/check.rs index f18b56f934ea..516bd187ba7f 100644 --- a/library/kani_macros/src/sysroot/contracts/check.rs +++ b/library/kani_macros/src/sysroot/contracts/check.rs @@ -13,6 +13,8 @@ use super::{ ContractConditionsData, ContractConditionsHandler, }; +const WRAPPER_ARG_PREFIX: &str = "_wrapper_arg_"; + impl<'a> ContractConditionsHandler<'a> { /// Create the body of a check function. /// @@ -60,7 +62,11 @@ impl<'a> ContractConditionsHandler<'a> { let wrapper_args = if let Some(wrapper_call_args) = inner.iter_mut().find_map(|stmt| try_as_wrapper_call_args(stmt, &wrapper_name)) { - let wrapper_args = make_wrapper_args(wrapper_call_args.len(), attr.len()); + let wrapper_args = make_wrapper_idents( + wrapper_call_args.len(), + attr.len(), + WRAPPER_ARG_PREFIX, + ); wrapper_call_args .extend(wrapper_args.clone().map(|a| Expr::Verbatim(quote!(#a)))); wrapper_args @@ -124,20 +130,43 @@ impl<'a> ContractConditionsHandler<'a> { /// Emit a modifies wrapper, possibly augmenting a prior, existing one. /// - /// We only augment if this clause is a `modifies` clause. In that case we - /// expand its signature with one new argument of type `&impl Arbitrary` for - /// each expression in the clause. + /// We only augment if this clause is a `modifies` clause. Before, + /// we annotated the wrapper arguments with `impl kani::Arbitrary`, + /// so Rust would infer the proper types for each argument. + /// We want to remove the restriction that these arguments must + /// implement `kani::Arbitrary` for checking. Now, we annotate each + /// argument with a generic type parameter, so the compiler can + /// continue inferring the correct types. pub fn emit_augmented_modifies_wrapper(&mut self) { if let ContractConditionsData::Modifies { attr } = &self.condition_type { - let wrapper_args = make_wrapper_args(self.annotated_fn.sig.inputs.len(), attr.len()); + let wrapper_args = make_wrapper_idents( + self.annotated_fn.sig.inputs.len(), + attr.len(), + WRAPPER_ARG_PREFIX, + ); + // Generate a unique type parameter identifier + let type_params = make_wrapper_idents( + self.annotated_fn.sig.inputs.len(), + attr.len(), + "WrapperArgType", + ); let sig = &mut self.annotated_fn.sig; - for arg in wrapper_args.clone() { + for (arg, arg_type) in wrapper_args.clone().zip(type_params) { + // Add the type parameter to the function signature's generic parameters list + sig.generics.params.push(syn::GenericParam::Type(syn::TypeParam { + attrs: vec![], + ident: arg_type.clone(), + colon_token: None, + bounds: Default::default(), + eq_token: None, + default: None, + })); let lifetime = syn::Lifetime { apostrophe: Span::call_site(), ident: arg.clone() }; sig.inputs.push(FnArg::Typed(syn::PatType { attrs: vec![], colon_token: Token![:](Span::call_site()), pat: Box::new(syn::Pat::Verbatim(quote!(#arg))), - ty: Box::new(syn::Type::Verbatim(quote!(&#lifetime impl kani::Arbitrary))), + ty: Box::new(syn::parse_quote! { &#arg_type }), })); sig.generics.params.push(syn::GenericParam::Lifetime(syn::LifetimeParam { lifetime, @@ -146,6 +175,7 @@ impl<'a> ContractConditionsHandler<'a> { attrs: vec![], })); } + self.output.extend(quote!(#[kanitool::modifies(#(#wrapper_args),*)])) } self.emit_common_header(); @@ -191,10 +221,14 @@ fn try_as_wrapper_call_args<'a>( } } -/// Make `num` [`Ident`]s with the names `_wrapper_arg_{i}` with `i` starting at `low` and +/// Make `num` [`Ident`]s with the names `prefix{i}` with `i` starting at `low` and /// increasing by one each time. -fn make_wrapper_args(low: usize, num: usize) -> impl Iterator + Clone { - (low..).map(|i| Ident::new(&format!("_wrapper_arg_{i}"), Span::mixed_site())).take(num) +fn make_wrapper_idents( + low: usize, + num: usize, + prefix: &'static str, +) -> impl Iterator + Clone + 'static { + (low..).map(move |i| Ident::new(&format!("{prefix}{i}"), Span::mixed_site())).take(num) } #[cfg(test)] diff --git a/tests/expected/function-contract/modifies/simple_only_verification_modifies.expected b/tests/expected/function-contract/modifies/simple_only_verification_modifies.expected new file mode 100644 index 000000000000..34c886c358cb --- /dev/null +++ b/tests/expected/function-contract/modifies/simple_only_verification_modifies.expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/modifies/simple_only_verification_modifies.rs b/tests/expected/function-contract/modifies/simple_only_verification_modifies.rs new file mode 100644 index 000000000000..4988dcb69c56 --- /dev/null +++ b/tests/expected/function-contract/modifies/simple_only_verification_modifies.rs @@ -0,0 +1,28 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +//! Check that is possible to use `modifies` clause for verification, but not stubbing. +//! Here, we cover the case when the modifies clause contains references to function +//! parameters of generic types. Noticed that here the type T is not annotated with +//! `kani::Arbitrary` since this is no longer a requirement if the contract is only +//! use for verification. + +pub mod contracts { + #[kani::modifies(x)] + #[kani::modifies(y)] + pub fn swap(x: &mut T, y: &mut T) { + core::mem::swap(x, y) + } +} + +mod verify { + use super::*; + + #[kani::proof_for_contract(contracts::swap)] + pub fn check_swap_primitive() { + let mut x: u8 = kani::any(); + let mut y: u8 = kani::any(); + contracts::swap(&mut x, &mut y) + } +} From 1930d7205376e08d48f8057006bf50c41bc8a570 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 5 May 2024 22:47:56 -0700 Subject: [PATCH 087/225] Automatic cargo update to 2024-05-06 (#3172) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 63 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62f2c913238d..6100ab068816 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,47 +25,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys", @@ -79,9 +80,9 @@ checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" @@ -179,9 +180,9 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "comfy-table" @@ -387,6 +388,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.12.1" @@ -496,9 +503,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "linear-map" @@ -608,9 +615,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -631,9 +638,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -903,18 +910,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.199" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", @@ -1393,18 +1400,18 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "087eca3c1eaf8c47b94d02790dd086cd594b912d2043d4de4bfdd466b3befb7c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393" dependencies = [ "proc-macro2", "quote", From 712bc5b0c5627e7af26e86383ae04d12ec5d02fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 20:08:30 +0100 Subject: [PATCH 088/225] Bump tests/perf/s2n-quic from `1436af7` to `6dd41e0` (#3174) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `1436af7` to `6dd41e0`.
Commits
  • 6dd41e0 build: fix clippy warnings for 1.78 (#2199)
  • de5c33e refactor(s2n-quic-core): improve reassembler error handling (#2197)
  • b085808 chore(s2n-quic-crypto): remove custom aesgcm implementation (#2186)
  • 7188ce4 feat(dc): DcSupportedVersions transport parameter (#2193)
  • 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 1436af712b6e..6dd41e09195b 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 1436af712b6e73edc11640dc7c3cae23e456c0a8 +Subproject commit 6dd41e09195bc22dbac93a48f8ab35f8063726dc From d8d620940151dcd8213bf452503381c343bf96bc Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 7 May 2024 03:22:42 +0200 Subject: [PATCH 089/225] Avoid unnecessary uses of Location::none() (#3173) We should try to produce a source location wherever possible to ease debugging and coverage reporting. --- .../codegen/intrinsic.rs | 120 ++++++++++-------- .../codegen/statement.rs | 43 +++---- .../codegen_cprover_gotoc/overrides/hooks.rs | 10 +- 3 files changed, 93 insertions(+), 80 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 4d201a6f8cc4..3a4e84a70d98 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -37,7 +37,7 @@ impl<'tcx> GotocCtx<'tcx> { let arg1 = fargs.remove(0); let arg2 = fargs.remove(0); let expr = f(arg1, arg2); - self.codegen_expr_to_place_stable(place, expr) + self.codegen_expr_to_place_stable(place, expr, Location::none()) } /// Given a call to an compiler intrinsic, generate the call and the `goto` terminator @@ -149,7 +149,7 @@ impl<'tcx> GotocCtx<'tcx> { mm, ); let expr = BuiltinFn::$f.call(casted_fargs, loc); - self.codegen_expr_to_place_stable(place, expr) + self.codegen_expr_to_place_stable(place, expr, loc) }}; } @@ -166,7 +166,7 @@ impl<'tcx> GotocCtx<'tcx> { loc, ); let res = a.$f(b); - let expr_place = self.codegen_expr_to_place_stable(place, res); + let expr_place = self.codegen_expr_to_place_stable(place, res, loc); Stmt::block(vec![div_overflow_check, expr_place], loc) }}; } @@ -187,7 +187,7 @@ impl<'tcx> GotocCtx<'tcx> { let arg1 = fargs.remove(0); let arg2 = fargs.remove(0); let expr = arg1.$f(arg2, self.symbol_table.machine_model()); - self.codegen_expr_to_place_stable(place, expr) + self.codegen_expr_to_place_stable(place, expr, loc) }}; } @@ -196,7 +196,7 @@ impl<'tcx> GotocCtx<'tcx> { macro_rules! codegen_count_intrinsic { ($builtin: ident, $allow_zero: expr) => {{ let arg = fargs.remove(0); - self.codegen_expr_to_place_stable(place, arg.$builtin($allow_zero)) + self.codegen_expr_to_place_stable(place, arg.$builtin($allow_zero), loc) }}; } @@ -209,7 +209,7 @@ impl<'tcx> GotocCtx<'tcx> { // We assume that the intrinsic has type checked at this point, so // we can use the place type as the expression type. let expr = self.codegen_allocation(&alloc, place_ty, Some(span)); - self.codegen_expr_to_place_stable(&place, expr) + self.codegen_expr_to_place_stable(&place, expr, loc) }}; } @@ -220,7 +220,7 @@ impl<'tcx> GotocCtx<'tcx> { let target_ty = args.0[0].expect_ty(); let arg = fargs.remove(0); let size_align = self.size_and_align_of_dst(*target_ty, arg); - self.codegen_expr_to_place_stable(place, size_align.$which) + self.codegen_expr_to_place_stable(place, size_align.$which, loc) }}; } @@ -271,7 +271,7 @@ impl<'tcx> GotocCtx<'tcx> { (var1.clone()).$op(var2).with_location(loc) }; let assign_stmt = (var1.clone()).assign(op_expr, loc); - let res_stmt = self.codegen_expr_to_place_stable(place, tmp.clone()); + let res_stmt = self.codegen_expr_to_place_stable(place, tmp.clone(), loc); Stmt::atomic_block(vec![decl_stmt, assign_stmt, res_stmt], loc) }}; } @@ -284,7 +284,7 @@ impl<'tcx> GotocCtx<'tcx> { loc, "https://github.com/model-checking/kani/issues/new/choose", ); - self.codegen_expr_to_place_stable(place, expr) + self.codegen_expr_to_place_stable(place, expr, loc) }}; } @@ -387,12 +387,14 @@ impl<'tcx> GotocCtx<'tcx> { "atomic_xsub_acqrel" => codegen_atomic_binop!(sub), "atomic_xsub_release" => codegen_atomic_binop!(sub), "atomic_xsub_relaxed" => codegen_atomic_binop!(sub), - "bitreverse" => self.codegen_expr_to_place_stable(place, fargs.remove(0).bitreverse()), + "bitreverse" => { + self.codegen_expr_to_place_stable(place, fargs.remove(0).bitreverse(), loc) + } // black_box is an identity function that hints to the compiler // to be maximally pessimistic to limit optimizations - "black_box" => self.codegen_expr_to_place_stable(place, fargs.remove(0)), + "black_box" => self.codegen_expr_to_place_stable(place, fargs.remove(0), loc), "breakpoint" => Stmt::skip(loc), - "bswap" => self.codegen_expr_to_place_stable(place, fargs.remove(0).bswap()), + "bswap" => self.codegen_expr_to_place_stable(place, fargs.remove(0).bswap(), loc), "caller_location" => self.codegen_unimplemented_stmt( intrinsic, loc, @@ -423,7 +425,7 @@ impl<'tcx> GotocCtx<'tcx> { let sig = instance.ty().kind().fn_sig().unwrap().skip_binder(); let ty = pointee_type_stable(sig.inputs()[0]).unwrap(); let e = self.codegen_get_discriminant(fargs.remove(0).dereference(), ty, ret_ty); - self.codegen_expr_to_place_stable(place, e) + self.codegen_expr_to_place_stable(place, e, loc) } "exact_div" => self.codegen_exact_div(fargs, place, loc), "exp2f32" => codegen_simple_intrinsic!(Exp2f), @@ -460,9 +462,9 @@ impl<'tcx> GotocCtx<'tcx> { "is_val_statically_known" => { // Returning false is sound according do this intrinsic's documentation: // https://doc.rust-lang.org/nightly/std/intrinsics/fn.is_val_statically_known.html - self.codegen_expr_to_place_stable(place, Expr::c_false()) + self.codegen_expr_to_place_stable(place, Expr::c_false(), loc) } - "likely" => self.codegen_expr_to_place_stable(place, fargs.remove(0)), + "likely" => self.codegen_expr_to_place_stable(place, fargs.remove(0), loc), "log10f32" => unstable_codegen!(codegen_simple_intrinsic!(Log10f)), "log10f64" => unstable_codegen!(codegen_simple_intrinsic!(Log10)), "log2f32" => unstable_codegen!(codegen_simple_intrinsic!(Log2f)), @@ -490,7 +492,7 @@ impl<'tcx> GotocCtx<'tcx> { "powif32" => unstable_codegen!(codegen_simple_intrinsic!(Powif)), "powif64" => unstable_codegen!(codegen_simple_intrinsic!(Powi)), "pref_align_of" => codegen_intrinsic_const!(), - "ptr_guaranteed_cmp" => self.codegen_ptr_guaranteed_cmp(fargs, place), + "ptr_guaranteed_cmp" => self.codegen_ptr_guaranteed_cmp(fargs, place, loc), "ptr_offset_from" => self.codegen_ptr_offset_from(fargs, place, loc), "ptr_offset_from_unsigned" => self.codegen_ptr_offset_from_unsigned(fargs, place, loc), "raw_eq" => self.codegen_intrinsic_raw_eq(instance, fargs, place, loc), @@ -575,16 +577,18 @@ impl<'tcx> GotocCtx<'tcx> { place, loc, ), - "transmute" => self.codegen_intrinsic_transmute(fargs, ret_ty, place), + "transmute" => self.codegen_intrinsic_transmute(fargs, ret_ty, place, loc), "truncf32" => codegen_simple_intrinsic!(Truncf), "truncf64" => codegen_simple_intrinsic!(Trunc), "type_id" => codegen_intrinsic_const!(), "type_name" => codegen_intrinsic_const!(), "typed_swap" => self.codegen_swap(fargs, farg_types, loc), "unaligned_volatile_load" => { - unstable_codegen!( - self.codegen_expr_to_place_stable(place, fargs.remove(0).dereference()) - ) + unstable_codegen!(self.codegen_expr_to_place_stable( + place, + fargs.remove(0).dereference(), + loc + )) } "unchecked_add" | "unchecked_mul" | "unchecked_shl" | "unchecked_shr" | "unchecked_sub" => { @@ -592,7 +596,7 @@ impl<'tcx> GotocCtx<'tcx> { } "unchecked_div" => codegen_op_with_div_overflow_check!(div), "unchecked_rem" => codegen_op_with_div_overflow_check!(rem), - "unlikely" => self.codegen_expr_to_place_stable(place, fargs.remove(0)), + "unlikely" => self.codegen_expr_to_place_stable(place, fargs.remove(0), loc), "unreachable" => unreachable!( "Expected `std::intrinsics::unreachable` to be handled by `TerminatorKind::Unreachable`" ), @@ -634,7 +638,8 @@ impl<'tcx> GotocCtx<'tcx> { if !arg.typ().is_integer() { self.intrinsics_typecheck_fail(span, "ctpop", "integer type", arg_rust_ty) } else { - self.codegen_expr_to_place_stable(&target_place, arg.popcount()) + let loc = self.codegen_span_stable(span); + self.codegen_expr_to_place_stable(&target_place, arg.popcount(), loc) } } @@ -723,6 +728,7 @@ impl<'tcx> GotocCtx<'tcx> { self.codegen_expr_to_place_stable( place, Expr::statement_expression(vec![res.as_stmt(loc)], result_type), + loc, ) } @@ -756,7 +762,7 @@ impl<'tcx> GotocCtx<'tcx> { "exact_div division does not overflow", loc, ), - self.codegen_expr_to_place_stable(p, a.div(b)), + self.codegen_expr_to_place_stable(p, a.div(b), loc), ], loc, ) @@ -857,7 +863,7 @@ impl<'tcx> GotocCtx<'tcx> { self.store_concurrent_construct(intrinsic, loc); let var1_ref = fargs.remove(0); let var1 = var1_ref.dereference().with_location(loc); - let res_stmt = self.codegen_expr_to_place_stable(p, var1); + let res_stmt = self.codegen_expr_to_place_stable(p, var1, loc); Stmt::atomic_block(vec![res_stmt], loc) } @@ -897,7 +903,7 @@ impl<'tcx> GotocCtx<'tcx> { let tuple_expr = Expr::struct_expr_from_values(res_type, vec![tmp, Expr::c_true()], &self.symbol_table) .with_location(loc); - let res_stmt = self.codegen_expr_to_place_stable(p, tuple_expr); + let res_stmt = self.codegen_expr_to_place_stable(p, tuple_expr, loc); Stmt::atomic_block(vec![decl_stmt, cond_update_stmt, res_stmt], loc) } @@ -925,7 +931,7 @@ impl<'tcx> GotocCtx<'tcx> { self.decl_temp_variable(var1.typ().clone(), Some(var1.to_owned()), loc); let var2 = fargs.remove(0).with_location(loc); let assign_stmt = var1.assign(var2, loc); - let res_stmt = self.codegen_expr_to_place_stable(place, tmp); + let res_stmt = self.codegen_expr_to_place_stable(place, tmp, loc); Stmt::atomic_block(vec![decl_stmt, assign_stmt, res_stmt], loc) } @@ -1003,7 +1009,7 @@ impl<'tcx> GotocCtx<'tcx> { // fail on passing a reference to it unless we codegen this zero check. let copy_if_nontrivial = count_bytes.is_zero().ternary(dst, copy_call); let copy_expr = if let Some(p) = p { - self.codegen_expr_to_place_stable(p, copy_if_nontrivial) + self.codegen_expr_to_place_stable(p, copy_if_nontrivial, loc) } else { copy_if_nontrivial.as_stmt(loc) }; @@ -1057,14 +1063,19 @@ impl<'tcx> GotocCtx<'tcx> { // // This intrinsic replaces `ptr_guaranteed_eq` and `ptr_guaranteed_ne`: // https://doc.rust-lang.org/beta/std/primitive.pointer.html#method.guaranteed_eq - fn codegen_ptr_guaranteed_cmp(&mut self, mut fargs: Vec, p: &Place) -> Stmt { + fn codegen_ptr_guaranteed_cmp( + &mut self, + mut fargs: Vec, + p: &Place, + loc: Location, + ) -> Stmt { let a = fargs.remove(0); let b = fargs.remove(0); let place_type = self.place_ty_stable(p); let res_type = self.codegen_ty_stable(place_type); let eq_expr = a.eq(b); let cmp_expr = eq_expr.ternary(res_type.one(), res_type.zero()); - self.codegen_expr_to_place_stable(p, cmp_expr) + self.codegen_expr_to_place_stable(p, cmp_expr, loc) } /// Computes the offset from a pointer. @@ -1112,7 +1123,7 @@ impl<'tcx> GotocCtx<'tcx> { // Re-compute `dst_ptr` with standard addition to avoid conversion let dst_ptr = src_ptr.plus(offset); - let expr_place = self.codegen_expr_to_place_stable(p, dst_ptr); + let expr_place = self.codegen_expr_to_place_stable(p, dst_ptr, loc); Stmt::block(vec![bytes_overflow_check, overflow_check, expr_place], loc) } @@ -1131,7 +1142,7 @@ impl<'tcx> GotocCtx<'tcx> { loc, ); - let offset_expr = self.codegen_expr_to_place_stable(p, offset_expr); + let offset_expr = self.codegen_expr_to_place_stable(p, offset_expr, loc); Stmt::block(vec![overflow_check, offset_expr], loc) } @@ -1163,7 +1174,8 @@ impl<'tcx> GotocCtx<'tcx> { loc, ); - let offset_expr = self.codegen_expr_to_place_stable(p, offset_expr.cast_to(Type::size_t())); + let offset_expr = + self.codegen_expr_to_place_stable(p, offset_expr.cast_to(Type::size_t()), loc); Stmt::block(vec![overflow_check, non_negative_check, offset_expr], loc) } @@ -1210,12 +1222,18 @@ impl<'tcx> GotocCtx<'tcx> { /// Note(std): An earlier attempt to add alignment checks for both the argument and result types /// had catastrophic results in the regression. Hence, we don't perform any additional checks /// and only encode the transmute operation here. - fn codegen_intrinsic_transmute(&mut self, mut fargs: Vec, ret_ty: Ty, p: &Place) -> Stmt { + fn codegen_intrinsic_transmute( + &mut self, + mut fargs: Vec, + ret_ty: Ty, + p: &Place, + loc: Location, + ) -> Stmt { assert!(fargs.len() == 1, "transmute had unexpected arguments {fargs:?}"); let arg = fargs.remove(0); let cbmc_ret_ty = self.codegen_ty_stable(ret_ty); let expr = arg.transmute_to(cbmc_ret_ty, &self.symbol_table); - self.codegen_expr_to_place_stable(p, expr) + self.codegen_expr_to_place_stable(p, expr, loc) } // `raw_eq` determines whether the raw bytes of two values are equal. @@ -1243,7 +1261,7 @@ impl<'tcx> GotocCtx<'tcx> { let val = fargs.remove(0).cast_to(Type::void_pointer()); let layout = self.layout_of_stable(ty); if layout.size.bytes() == 0 { - self.codegen_expr_to_place_stable(p, Expr::int_constant(1, Type::c_bool())) + self.codegen_expr_to_place_stable(p, Expr::int_constant(1, Type::c_bool()), loc) } else { let sz = Expr::int_constant(layout.size.bytes(), Type::size_t()) .with_size_of_annotation(self.codegen_ty_stable(ty)); @@ -1251,21 +1269,16 @@ impl<'tcx> GotocCtx<'tcx> { .call(vec![dst, val, sz], loc) .eq(Type::c_int().zero()) .cast_to(Type::c_bool()); - self.codegen_expr_to_place_stable(p, e) + self.codegen_expr_to_place_stable(p, e, loc) } } // This is an operation that is primarily relevant for stacked borrow // checks. For Kani, we simply return the pointer. - fn codegen_retag_box_to_raw( - &mut self, - mut fargs: Vec, - p: &Place, - _loc: Location, - ) -> Stmt { + fn codegen_retag_box_to_raw(&mut self, mut fargs: Vec, p: &Place, loc: Location) -> Stmt { assert_eq!(fargs.len(), 1, "raw_box_to_box expected one argument"); let arg = fargs.remove(0); - self.codegen_expr_to_place_stable(p, arg) + self.codegen_expr_to_place_stable(p, arg, loc) } fn vtable_info( @@ -1273,7 +1286,7 @@ impl<'tcx> GotocCtx<'tcx> { info: VTableInfo, mut fargs: Vec, place: &Place, - _loc: Location, + loc: Location, ) -> Stmt { assert_eq!(fargs.len(), 1, "vtable intrinsics expects one raw pointer argument"); let vtable_obj = fargs @@ -1285,7 +1298,7 @@ impl<'tcx> GotocCtx<'tcx> { VTableInfo::Size => vtable_obj.member(typ::VTABLE_SIZE_FIELD, &self.symbol_table), VTableInfo::Align => vtable_obj.member(typ::VTABLE_ALIGN_FIELD, &self.symbol_table), }; - self.codegen_expr_to_place_stable(place, expr) + self.codegen_expr_to_place_stable(place, expr, loc) } /// Gets the length for a `simd_shuffle*` instance, which comes in two @@ -1465,7 +1478,8 @@ impl<'tcx> GotocCtx<'tcx> { } self.tcx.dcx().abort_if_errors(); - self.codegen_expr_to_place_stable(p, vec.index_array(index)) + let loc = self.codegen_span_stable(span); + self.codegen_expr_to_place_stable(p, vec.index_array(index), loc) } /// Insert is a generic update of a single value in a SIMD vector. @@ -1512,7 +1526,7 @@ impl<'tcx> GotocCtx<'tcx> { vec![ decl, tmp.clone().index_array(index).assign(newval.cast_to(elem_ty), loc), - self.codegen_expr_to_place_stable(p, tmp), + self.codegen_expr_to_place_stable(p, tmp, loc), ], loc, ) @@ -1585,7 +1599,8 @@ impl<'tcx> GotocCtx<'tcx> { // Create the vector comparison expression let e = f(arg1, arg2, ret_typ); - self.codegen_expr_to_place_stable(p, e) + let loc = self.codegen_span_stable(span); + self.codegen_expr_to_place_stable(p, e, loc) } /// Codegen for `simd_div` and `simd_rem` intrinsics. @@ -1655,7 +1670,7 @@ impl<'tcx> GotocCtx<'tcx> { loc, ); let res = op_fun(a, b); - let expr_place = self.codegen_expr_to_place_stable(p, res); + let expr_place = self.codegen_expr_to_place_stable(p, res, loc); Stmt::block(vec![check_stmt, expr_place], loc) } @@ -1713,7 +1728,7 @@ impl<'tcx> GotocCtx<'tcx> { _ => unreachable!("expected a simd shift intrinsic"), }; let res = op_fun(values, distances); - let expr_place = self.codegen_expr_to_place_stable(p, res); + let expr_place = self.codegen_expr_to_place_stable(p, res, loc); if distance_is_signed { let negative_check_stmt = self.codegen_assert_assume( @@ -1806,7 +1821,8 @@ impl<'tcx> GotocCtx<'tcx> { .collect(); self.tcx.dcx().abort_if_errors(); let cbmc_ret_ty = self.codegen_ty_stable(rust_ret_type); - self.codegen_expr_to_place_stable(p, Expr::vector_expr(cbmc_ret_ty, elems)) + let loc = self.codegen_span_stable(span); + self.codegen_expr_to_place_stable(p, Expr::vector_expr(cbmc_ret_ty, elems), loc) } /// A volatile load of a memory location: @@ -1836,7 +1852,7 @@ impl<'tcx> GotocCtx<'tcx> { loc, ); let expr = src.dereference(); - let res_stmt = self.codegen_expr_to_place_stable(p, expr); + let res_stmt = self.codegen_expr_to_place_stable(p, expr, loc); Stmt::block(vec![align_check, res_stmt], loc) } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 9a78515fde90..ba00ff0a3dbf 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -151,7 +151,7 @@ impl<'tcx> GotocCtx<'tcx> { TerminatorKind::Return => { let rty = self.current_fn().instance_stable().fn_abi().unwrap().ret.ty; if rty.kind().is_unit() { - self.codegen_ret_unit() + self.codegen_ret_unit(loc) } else { let place = Place::from(RETURN_LOCAL); let place_expr = unwrap_or_return_codegen_unimplemented_stmt!( @@ -311,17 +311,12 @@ impl<'tcx> GotocCtx<'tcx> { } /// A special case handler to codegen `return ();` - fn codegen_ret_unit(&mut self) -> Stmt { + fn codegen_ret_unit(&mut self, loc: Location) -> Stmt { let is_file_local = false; let ty = self.codegen_ty_unit(); - let var = self.ensure_global_var( - FN_RETURN_VOID_VAR_NAME, - is_file_local, - ty, - Location::none(), - |_, _| None, - ); - Stmt::ret(Some(var), Location::none()) + let var = + self.ensure_global_var(FN_RETURN_VOID_VAR_NAME, is_file_local, ty, loc, |_, _| None); + Stmt::ret(Some(var), loc) } /// Generates Goto-C for MIR [TerminatorKind::Drop] calls. We only handle code _after_ Rust's "drop elaboration" @@ -572,13 +567,11 @@ impl<'tcx> GotocCtx<'tcx> { if instance.is_foreign_item() { vec![self.codegen_foreign_call(func_exp, fargs, destination, loc)] } else { - vec![ - self.codegen_expr_to_place_stable( - destination, - func_exp.call(fargs), - ) - .with_location(loc), - ] + vec![self.codegen_expr_to_place_stable( + destination, + func_exp.call(fargs), + loc, + )] } } }; @@ -600,8 +593,7 @@ impl<'tcx> GotocCtx<'tcx> { // Actually generate the function call and return. Stmt::block( vec![ - self.codegen_expr_to_place_stable(destination, func_expr.call(fargs)) - .with_location(loc), + self.codegen_expr_to_place_stable(destination, func_expr.call(fargs), loc), Stmt::goto(bb_label(target.unwrap()), loc), ], loc, @@ -698,7 +690,7 @@ impl<'tcx> GotocCtx<'tcx> { // Virtual function call and corresponding nonnull assertion. let call = fn_ptr.dereference().call(fargs.to_vec()); - let call_stmt = self.codegen_expr_to_place_stable(place, call).with_location(loc); + let call_stmt = self.codegen_expr_to_place_stable(place, call, loc); let call_stmt = if self.vtable_ctx.emit_vtable_restrictions { self.virtual_call_with_restricted_fn_ptr(trait_fat_ptr.typ().clone(), idx, call_stmt) } else { @@ -713,13 +705,18 @@ impl<'tcx> GotocCtx<'tcx> { /// A MIR [Place] is an L-value (i.e. the LHS of an assignment). /// /// In Kani, we slightly optimize the special case for Unit and don't assign anything. - pub(crate) fn codegen_expr_to_place_stable(&mut self, place: &Place, expr: Expr) -> Stmt { + pub(crate) fn codegen_expr_to_place_stable( + &mut self, + place: &Place, + expr: Expr, + loc: Location, + ) -> Stmt { if self.place_ty_stable(place).kind().is_unit() { - expr.as_stmt(Location::none()) + expr.as_stmt(loc) } else { unwrap_or_return_codegen_unimplemented_stmt!(self, self.codegen_place_stable(place)) .goto_expr - .assign(expr, Location::none()) + .assign(expr, loc) } } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index 18cc44b7b20d..d5f10adeec32 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -12,7 +12,7 @@ use crate::codegen_cprover_gotoc::codegen::{bb_label, PropertyClass}; use crate::codegen_cprover_gotoc::GotocCtx; use crate::kani_middle::attributes::matches_diagnostic as matches_function; use crate::unwrap_or_return_codegen_unimplemented_stmt; -use cbmc::goto_program::{BuiltinFn, Expr, Location, Stmt, Type}; +use cbmc::goto_program::{BuiltinFn, Expr, Stmt, Type}; use rustc_middle::ty::TyCtxt; use rustc_smir::rustc_internal; use stable_mir::mir::mono::Instance; @@ -235,9 +235,9 @@ impl GotocHook for IsReadOk { Stmt::block( vec![ ret_place.goto_expr.assign(Expr::read_ok(ptr, size).cast_to(ret_type), loc), - Stmt::goto(bb_label(target), Location::none()), + Stmt::goto(bb_label(target), loc), ], - Location::none(), + loc, ) } } @@ -277,9 +277,9 @@ impl GotocHook for RustAlloc { .cast_to(Type::unsigned_int(8).to_pointer()), loc, ), - Stmt::goto(bb_label(target), Location::none()), + Stmt::goto(bb_label(target), loc), ], - Location::none(), + loc, ) } } From 9419fea9791923820bce3d469e4d7f06ab40e1d7 Mon Sep 17 00:00:00 2001 From: Kareem Khazem Date: Tue, 7 May 2024 17:29:54 +0100 Subject: [PATCH 090/225] Update Rust dependencies (#3175) Update Cargo.lock with the following package version changes: ``` anyhow 1.0.82 -> 1.0.83 getrandom 0.2.14 -> 0.2.15 num-bigint 0.4.4 -> 0.4.5 ryu 1.0.17 -> 1.0.18 syn 2.0.60 -> 2.0.61 winnow 0.6.7 -> 0.6.8 ``` 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 | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6100ab068816..b66cdbe5ac37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,9 +74,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "autocfg" @@ -169,7 +169,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -333,9 +333,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -481,7 +481,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -586,11 +586,10 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -880,9 +879,9 @@ checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -925,7 +924,7 @@ checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -1031,7 +1030,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -1046,9 +1045,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" dependencies = [ "proc-macro2", "quote", @@ -1084,7 +1083,7 @@ checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -1150,7 +1149,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -1385,9 +1384,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ "memchr", ] @@ -1415,5 +1414,5 @@ checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] From bb3e71a5364d356c4bb5692d1287e21ca14c6e9c Mon Sep 17 00:00:00 2001 From: Kareem Khazem Date: Tue, 7 May 2024 23:51:52 +0100 Subject: [PATCH 091/225] Bump Kani version to 0.51.0 (#3176) For reference, here is the auto-generated changelog ## What's Changed * Upgrade toolchain to 2024-04-18 and improve toolchain workflow by @celinval in https://github.com/model-checking/kani/pull/3149 * Automatic toolchain upgrade to nightly-2024-04-19 by @github-actions in https://github.com/model-checking/kani/pull/3150 * Stabilize cover statement and update contracts RFC by @celinval in https://github.com/model-checking/kani/pull/3091 * Automatic toolchain upgrade to nightly-2024-04-20 by @github-actions in https://github.com/model-checking/kani/pull/3154 * Bump tests/perf/s2n-quic from `2d5e891` to `5f88e54` by @dependabot in https://github.com/model-checking/kani/pull/3140 * Automatic cargo update to 2024-04-22 by @github-actions in https://github.com/model-checking/kani/pull/3157 * Automatic toolchain upgrade to nightly-2024-04-21 by @github-actions in https://github.com/model-checking/kani/pull/3158 * Bump tests/perf/s2n-quic from `5f88e54` to `9730578` by @dependabot in https://github.com/model-checking/kani/pull/3159 * Fix cargo audit error by @jaisnan in https://github.com/model-checking/kani/pull/3160 * Fix cbmc-update CI job by @tautschnig in https://github.com/model-checking/kani/pull/3156 * Automatic cargo update to 2024-04-29 by @github-actions in https://github.com/model-checking/kani/pull/3165 * Bump tests/perf/s2n-quic from `9730578` to `1436af7` by @dependabot in https://github.com/model-checking/kani/pull/3166 * Do not assume that ZST-typed symbols refer to unique objects by @tautschnig in https://github.com/model-checking/kani/pull/3134 * Fix copyright check for `expected` tests by @adpaco-aws in https://github.com/model-checking/kani/pull/3170 * Remove kani::Arbitrary from the modifies contract instrumentation by @feliperodri in https://github.com/model-checking/kani/pull/3169 * Automatic cargo update to 2024-05-06 by @github-actions in https://github.com/model-checking/kani/pull/3172 * Bump tests/perf/s2n-quic from `1436af7` to `6dd41e0` by @dependabot in https://github.com/model-checking/kani/pull/3174 * Avoid unnecessary uses of Location::none() by @tautschnig in https://github.com/model-checking/kani/pull/3173 **Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.50.0...kani-0.51.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: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> --- CHANGELOG.md | 13 +++++++++++++ Cargo.lock | 18 +++++++++--------- 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_macros/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- tools/build-kani/Cargo.toml | 2 +- 11 files changed, 31 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61e06601dfc3..7fc4cac54063 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,19 @@ 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.51.0] + +## What's Changed + +* Do not assume that ZST-typed symbols refer to unique objects by @tautschnig in https://github.com/model-checking/kani/pull/3134 +* Remove `kani::Arbitrary` from the `modifies` contract instrumentation by @feliperodri in https://github.com/model-checking/kani/pull/3169 +* Emit source locations whenever possible to ease debugging and coverage reporting by @tautschnig in https://github.com/model-checking/kani/pull/3173 +* Rust toolchain upgraded to `nightly-2024-04-21` by @celinval + + +**Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.50.0...kani-0.51.0 + + ## [0.50.0] ### Major Changes diff --git a/Cargo.lock b/Cargo.lock index b66cdbe5ac37..f3df3618f7b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,7 +92,7 @@ checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "build-kani" -version = "0.50.0" +version = "0.51.0" dependencies = [ "anyhow", "cargo_metadata", @@ -228,7 +228,7 @@ dependencies = [ [[package]] name = "cprover_bindings" -version = "0.50.0" +version = "0.51.0" dependencies = [ "lazy_static", "linear-map", @@ -411,14 +411,14 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "kani" -version = "0.50.0" +version = "0.51.0" dependencies = [ "kani_macros", ] [[package]] name = "kani-compiler" -version = "0.50.0" +version = "0.51.0" dependencies = [ "clap", "cprover_bindings", @@ -439,7 +439,7 @@ dependencies = [ [[package]] name = "kani-driver" -version = "0.50.0" +version = "0.51.0" dependencies = [ "anyhow", "cargo_metadata", @@ -467,7 +467,7 @@ dependencies = [ [[package]] name = "kani-verifier" -version = "0.50.0" +version = "0.51.0" dependencies = [ "anyhow", "home", @@ -476,7 +476,7 @@ dependencies = [ [[package]] name = "kani_macros" -version = "0.50.0" +version = "0.51.0" dependencies = [ "proc-macro-error", "proc-macro2", @@ -486,7 +486,7 @@ dependencies = [ [[package]] name = "kani_metadata" -version = "0.50.0" +version = "0.51.0" dependencies = [ "clap", "cprover_bindings", @@ -992,7 +992,7 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "std" -version = "0.50.0" +version = "0.51.0" dependencies = [ "kani", ] diff --git a/Cargo.toml b/Cargo.toml index 93affb02856f..7b7d5cbe60a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-verifier" -version = "0.50.0" +version = "0.51.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 d199558ff16a..844accd3914b 100644 --- a/cprover_bindings/Cargo.toml +++ b/cprover_bindings/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "cprover_bindings" -version = "0.50.0" +version = "0.51.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index 244172715056..7315ff224bd8 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-compiler" -version = "0.50.0" +version = "0.51.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index ab83c2202bc9..6b25fe02a263 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-driver" -version = "0.50.0" +version = "0.51.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 7936d943556b..645ed4da5fd2 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_metadata" -version = "0.50.0" +version = "0.51.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index 97952dd7ab9e..4b158e41daa0 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.50.0" +version = "0.51.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 a54fb44d8b6c..0f9dda7136ad 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_macros" -version = "0.50.0" +version = "0.51.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 6f9a380fc584..b83c9d3dcba0 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.50.0" +version = "0.51.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 eabb396e0923..21ad6be2a4b0 100644 --- a/tools/build-kani/Cargo.toml +++ b/tools/build-kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "build-kani" -version = "0.50.0" +version = "0.51.0" edition = "2021" description = "Builds Kani, Sysroot and release bundle." license = "MIT OR Apache-2.0" From b6126864f152185cebbb7af441a59a89470db388 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 11:32:26 -0700 Subject: [PATCH 092/225] Bump tests/perf/s2n-quic from `6dd41e0` to `bd37960` (#3178) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `6dd41e0` to `bd37960`.
Commits
  • bd37960 feat(s2n-quic-rustls): update rustls from 0.21 to 0.23 (#2200)
  • 286adde feat(s2n-quic): provider-crypto-fips feature flag (#2194)
  • c5ba081 chore(s2n-quic-core): update frame round trip fuzz corpus (#2205)
  • 8c3248b feat(s2n-quic-core): add state contstruction (#2202)
  • f05dc62 feat(s2n-quic-dc): DC_STATELESS_RESET_TOKENS frame (#2198)
  • 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 6dd41e09195b..bd37960965d5 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 6dd41e09195bc22dbac93a48f8ab35f8063726dc +Subproject commit bd37960965d5bc8ab9990dc400c886a7a83fb57e From b34d1177676cbaacb6e77eeb878c4f887186e68e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 14:15:32 -0700 Subject: [PATCH 093/225] Automatic cargo update to 2024-05-13 (#3177) Dependency upgrade resulting from `cargo update`. --------- Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> Co-authored-by: Zyad Hassan --- Cargo.lock | 79 +++++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3df3618f7b8..51d119effd65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,7 +169,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.63", ] [[package]] @@ -308,9 +308,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys", @@ -481,7 +481,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.63", ] [[package]] @@ -572,9 +572,9 @@ dependencies = [ [[package]] name = "num" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ "num-bigint", "num-complex", @@ -596,9 +596,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] @@ -625,11 +625,10 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", @@ -733,9 +732,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -854,9 +853,9 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" @@ -873,9 +872,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" [[package]] name = "ryu" @@ -900,38 +899,38 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.200" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.200" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.63", ] [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -1030,7 +1029,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.61", + "syn 2.0.63", ] [[package]] @@ -1045,9 +1044,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.61" +version = "2.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" dependencies = [ "proc-macro2", "quote", @@ -1068,22 +1067,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.63", ] [[package]] @@ -1149,7 +1148,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.63", ] [[package]] @@ -1399,20 +1398,20 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "zerocopy" -version = "0.7.33" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "087eca3c1eaf8c47b94d02790dd086cd594b912d2043d4de4bfdd466b3befb7c" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.33" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.63", ] From 997a54cc55d8eea26ec021e541582280da184712 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Tue, 14 May 2024 10:51:51 -0700 Subject: [PATCH 094/225] Upgrade toolchain to 2024-04-22 (#3171) Addresses the new `AggregateKind::RawPtr` added in https://github.com/rust-lang/rust/pull/123840. Resolves #3161 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: Kareem Khazem Co-authored-by: Michael Tautschnig --- .../codegen_cprover_gotoc/codegen/rvalue.rs | 37 +++++++++++++++++++ .../src/kani_middle/transform/check_values.rs | 1 + rust-toolchain.toml | 2 +- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index cb78163fe99b..8a9aa4821ab6 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -674,6 +674,43 @@ impl<'tcx> GotocCtx<'tcx> { &self.symbol_table, ) } + AggregateKind::RawPtr(pointee_ty, _) => { + // We expect two operands: "data" and "meta" + 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()); + let data = self.codegen_operand_stable(&operands[0]); + match pointee_ty.kind() { + TyKind::RigidTy(RigidTy::Slice(inner_ty)) => { + let pointee_goto_typ = self.codegen_ty_stable(inner_ty); + // cast data to pointer with specified type + let data_cast = + data.cast_to(Type::Pointer { typ: Box::new(pointee_goto_typ) }); + let meta = self.codegen_operand_stable(&operands[1]); + slice_fat_ptr(typ, data_cast, meta, &self.symbol_table) + } + TyKind::RigidTy(RigidTy::Adt(..)) => { + let pointee_goto_typ = self.codegen_ty_stable(pointee_ty); + let data_cast = + data.cast_to(Type::Pointer { typ: Box::new(pointee_goto_typ) }); + let meta = self.codegen_operand_stable(&operands[1]); + if meta.typ().sizeof(&self.symbol_table) == 0 { + data_cast + } else { + let vtable_expr = + meta.member("vtable_ptr", &self.symbol_table).cast_to( + typ.lookup_field_type("vtable", &self.symbol_table).unwrap(), + ); + dynamic_fat_ptr(typ, data_cast, vtable_expr, &self.symbol_table) + } + } + _ => { + let pointee_goto_typ = self.codegen_ty_stable(pointee_ty); + data.cast_to(Type::Pointer { typ: Box::new(pointee_goto_typ) }) + } + } + } AggregateKind::Coroutine(_, _, _) => self.codegen_rvalue_coroutine(&operands, res_ty), } } diff --git a/kani-compiler/src/kani_middle/transform/check_values.rs b/kani-compiler/src/kani_middle/transform/check_values.rs index d62b5807319e..4697c139b294 100644 --- a/kani-compiler/src/kani_middle/transform/check_values.rs +++ b/kani-compiler/src/kani_middle/transform/check_values.rs @@ -678,6 +678,7 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> { AggregateKind::Array(_) | AggregateKind::Closure(_, _) | AggregateKind::Coroutine(_, _, _) + | AggregateKind::RawPtr(_, _) | AggregateKind::Tuple => {} }, Rvalue::AddressOf(_, _) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 70848743b686..e571e9eaff4f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-04-21" +channel = "nightly-2024-04-22" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From 52a5b77d18cae347160d5eac7a53acb69a207311 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Wed, 15 May 2024 00:28:34 -0700 Subject: [PATCH 095/225] Upgrade toolchain to 2024-05-14 (#3183) Relevant upstream PRs: https://github.com/rust-lang/rust/pull/124797 https://github.com/rust-lang/rust/pull/124957 https://github.com/rust-lang/rust/pull/124003 --- cprover_bindings/src/goto_program/expr.rs | 4 +- .../src/codegen_cprover_gotoc/codegen/typ.rs | 17 ++-- kani-compiler/src/kani_middle/coercion.rs | 4 +- library/kani_macros/build.rs | 7 ++ rust-toolchain.toml | 2 +- .../expected/coroutines/as_parameter/main.rs | 11 ++- tests/expected/coroutines/main.rs | 4 +- tests/expected/coroutines/pin/main.rs | 4 +- .../intrinsics/ctpop-ice/ctpop_ice.rs | 14 --- tests/expected/intrinsics/ctpop-ice/expected | 6 -- tests/kani/Coroutines/main.rs | 4 +- .../rustc-coroutine-tests/conditional-drop.rs | 7 +- .../rustc-coroutine-tests/control-flow.rs | 76 ++++++++------ .../rustc-coroutine-tests/env-drop.rs | 10 +- .../rustc-coroutine-tests/iterator-count.rs | 2 + .../live-upvar-across-yield.rs | 4 +- .../moved-locals-size.rs | 4 + .../rustc-coroutine-tests/moved-locals.rs | 4 + .../nested-generators.rs | 7 +- .../niche-in-generator-size.rs | 4 +- .../niche-in-generator.rs | 4 +- .../overlap-locals-size.rs | 4 +- .../rustc-coroutine-tests/overlap-locals.rs | 4 +- .../rustc-coroutine-tests/resume-arg-size.rs | 7 +- .../rustc-coroutine-tests/resume-arg.rs | 7 +- .../resume-live-across-yield.rs | 4 +- .../smoke-resume-args.rs | 29 ++++-- .../Coroutines/rustc-coroutine-tests/smoke.rs | 98 ++++++++++++------- .../rustc-coroutine-tests/static-generator.rs | 4 +- .../rustc-coroutine-tests/yield-in-box.rs | 5 +- tests/kani/Intrinsics/Count/ctlz.rs | 2 +- tests/kani/Intrinsics/Count/ctpop.rs | 2 +- tests/kani/Intrinsics/Count/cttz.rs | 2 +- tests/kani/Intrinsics/Rotate/rotate_left.rs | 2 +- tests/kani/Intrinsics/Rotate/rotate_right.rs | 2 +- 35 files changed, 241 insertions(+), 130 deletions(-) create mode 100644 library/kani_macros/build.rs delete mode 100644 tests/expected/intrinsics/ctpop-ice/ctpop_ice.rs delete mode 100644 tests/expected/intrinsics/ctpop-ice/expected diff --git a/cprover_bindings/src/goto_program/expr.rs b/cprover_bindings/src/goto_program/expr.rs index 9f97e7a0a599..905e47bc6034 100644 --- a/cprover_bindings/src/goto_program/expr.rs +++ b/cprover_bindings/src/goto_program/expr.rs @@ -1331,11 +1331,11 @@ impl Expr { fn unop_return_type(op: UnaryOperator, arg: &Expr) -> Type { match op { Bitnot | BitReverse | Bswap | UnaryMinus => arg.typ.clone(), - CountLeadingZeros { .. } | CountTrailingZeros { .. } => arg.typ.clone(), + CountLeadingZeros { .. } | CountTrailingZeros { .. } => Type::unsigned_int(32), ObjectSize | PointerObject => Type::size_t(), PointerOffset => Type::ssize_t(), IsDynamicObject | IsFinite | Not => Type::bool(), - Popcount => arg.typ.clone(), + Popcount => Type::unsigned_int(32), } } /// Private helper function to make unary operators diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index 64c36dd76cfb..df36189f4606 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::{List, TypeFoldable}; use rustc_smir::rustc_internal; use rustc_span::def_id::DefId; use rustc_target::abi::{ - Abi::Vector, FieldIdx, FieldsShape, Integer, LayoutS, Primitive, Size, TagEncoding, + Abi::Vector, FieldIdx, FieldsShape, Float, Integer, LayoutS, Primitive, Size, TagEncoding, TyAndLayout, VariantIdx, Variants, }; use stable_mir::abi::{ArgAbi, FnAbi, PassMode}; @@ -1354,13 +1354,18 @@ impl<'tcx> GotocCtx<'tcx> { } } }, + Primitive::Float(f) => self.codegen_float_type(f), + Primitive::Pointer(_) => Ty::new_ptr(self.tcx, self.tcx.types.u8, Mutability::Not), + } + } - Primitive::F32 => self.tcx.types.f32, - Primitive::F64 => self.tcx.types.f64, + pub fn codegen_float_type(&self, f: Float) -> Ty<'tcx> { + match f { + Float::F32 => self.tcx.types.f32, + Float::F64 => self.tcx.types.f64, // `F16` and `F128` are not yet handled. // Tracked here: - Primitive::F16 | Primitive::F128 => unimplemented!(), - Primitive::Pointer(_) => Ty::new_ptr(self.tcx, self.tcx.types.u8, Mutability::Not), + Float::F16 | Float::F128 => unimplemented!(), } } @@ -1672,7 +1677,7 @@ pub fn pointee_type(mir_type: Ty) -> Option { /// Extracts the pointee type if the given mir type is either a known smart pointer (Box, Rc, ..) /// or a regular pointer. pub fn std_pointee_type(mir_type: Ty) -> Option { - mir_type.builtin_deref(true).map(|tm| tm.ty) + mir_type.builtin_deref(true) } /// This is a place holder function that should normalize the given type. diff --git a/kani-compiler/src/kani_middle/coercion.rs b/kani-compiler/src/kani_middle/coercion.rs index 10b8c1275da6..822f32631e0c 100644 --- a/kani-compiler/src/kani_middle/coercion.rs +++ b/kani-compiler/src/kani_middle/coercion.rs @@ -16,8 +16,8 @@ use rustc_hir::lang_items::LangItem; use rustc_middle::traits::{ImplSource, ImplSourceUserDefinedData}; use rustc_middle::ty::adjustment::CustomCoerceUnsized; +use rustc_middle::ty::TraitRef; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; -use rustc_middle::ty::{TraitRef, TypeAndMut}; use rustc_smir::rustc_internal; use stable_mir::ty::{RigidTy, Ty as TyStable, TyKind}; use stable_mir::Symbol; @@ -263,5 +263,5 @@ fn custom_coerce_unsize_info<'tcx>( /// Extract pointee type from builtin pointer types. fn extract_pointee(tcx: TyCtxt<'_>, typ: TyStable) -> Option> { - rustc_internal::internal(tcx, typ).builtin_deref(true).map(|TypeAndMut { ty, .. }| ty) + rustc_internal::internal(tcx, typ).builtin_deref(true) } diff --git a/library/kani_macros/build.rs b/library/kani_macros/build.rs new file mode 100644 index 000000000000..c094cc0254c6 --- /dev/null +++ b/library/kani_macros/build.rs @@ -0,0 +1,7 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +fn main() { + // Make sure `kani_sysroot` is a recognized config + println!("cargo::rustc-check-cfg=cfg(kani_sysroot)"); +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e571e9eaff4f..021cbbf7a8ff 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-04-22" +channel = "nightly-2024-05-14" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/expected/coroutines/as_parameter/main.rs b/tests/expected/coroutines/as_parameter/main.rs index 7c0b9aed0373..33f56d04b2ae 100644 --- a/tests/expected/coroutines/as_parameter/main.rs +++ b/tests/expected/coroutines/as_parameter/main.rs @@ -21,8 +21,11 @@ where #[kani::proof] fn main() { - foo(|| { - yield 1; - return 2; - }); + foo( + #[coroutine] + || { + yield 1; + return 2; + }, + ); } diff --git a/tests/expected/coroutines/main.rs b/tests/expected/coroutines/main.rs index d94524c05d63..9b76e6e4302e 100644 --- a/tests/expected/coroutines/main.rs +++ b/tests/expected/coroutines/main.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT #![feature(coroutines, coroutine_trait)] +#![feature(stmt_expr_attributes)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; @@ -10,7 +11,8 @@ use std::pin::Pin; #[kani::unwind(2)] fn main() { let val: bool = kani::any(); - let mut coroutine = move || { + let mut coroutine = #[coroutine] + move || { let x = val; yield x; return !x; diff --git a/tests/expected/coroutines/pin/main.rs b/tests/expected/coroutines/pin/main.rs index 7d33005bb138..fc97e57761e0 100644 --- a/tests/expected/coroutines/pin/main.rs +++ b/tests/expected/coroutines/pin/main.rs @@ -5,13 +5,15 @@ // from https://github.com/model-checking/kani/issues/416 #![feature(coroutines, coroutine_trait)] +#![feature(stmt_expr_attributes)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; #[kani::proof] fn main() { - let mut coroutine = || { + let mut coroutine = #[coroutine] + || { yield 1; return true; }; diff --git a/tests/expected/intrinsics/ctpop-ice/ctpop_ice.rs b/tests/expected/intrinsics/ctpop-ice/ctpop_ice.rs deleted file mode 100644 index ea11400e8161..000000000000 --- a/tests/expected/intrinsics/ctpop-ice/ctpop_ice.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT -//! Check that we correctly handle type mistmatch when the argument is a ZST type. -//! The compiler crashes today: https://github.com/model-checking/kani/issues/2121 - -#![feature(core_intrinsics)] -use std::intrinsics::ctpop; - -// These shouldn't compile. -#[kani::proof] -pub fn check_zst_ctpop() { - let n = ctpop(()); - assert!(n == ()); -} diff --git a/tests/expected/intrinsics/ctpop-ice/expected b/tests/expected/intrinsics/ctpop-ice/expected deleted file mode 100644 index 1ac989525f36..000000000000 --- a/tests/expected/intrinsics/ctpop-ice/expected +++ /dev/null @@ -1,6 +0,0 @@ -error: Type check failed for intrinsic `ctpop`: Expected integer type, found () - | -12 | let n = ctpop(()); - | ^^^^^^^^^ - -error: aborting due to 1 previous error \ No newline at end of file diff --git a/tests/kani/Coroutines/main.rs b/tests/kani/Coroutines/main.rs index 14cbeb426321..e059305a6da2 100644 --- a/tests/kani/Coroutines/main.rs +++ b/tests/kani/Coroutines/main.rs @@ -4,6 +4,7 @@ // This tests that coroutines work, even with a non-() resume type. #![feature(coroutines, coroutine_trait)] +#![feature(stmt_expr_attributes)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; @@ -11,7 +12,8 @@ use std::pin::Pin; #[kani::proof] #[kani::unwind(3)] fn main() { - let mut add_one = |mut resume: u8| { + let mut add_one = #[coroutine] + |mut resume: u8| { loop { resume = yield resume.saturating_add(1); } diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/conditional-drop.rs b/tests/kani/Coroutines/rustc-coroutine-tests/conditional-drop.rs index 81036a8f1238..a52f711fa52b 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/conditional-drop.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/conditional-drop.rs @@ -12,6 +12,7 @@ //[nomiropt]compile-flags: -Z mir-opt-level=0 #![feature(coroutines, coroutine_trait)] +#![feature(stmt_expr_attributes)] use std::ops::Coroutine; use std::pin::Pin; @@ -41,7 +42,8 @@ fn main() { } fn t1() { - let mut a = || { + let mut a = #[coroutine] + || { let b = B; if test() { drop(b); @@ -57,7 +59,8 @@ fn t1() { } fn t2() { - let mut a = || { + let mut a = #[coroutine] + || { let b = B; if test2() { drop(b); diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/control-flow.rs b/tests/kani/Coroutines/rustc-coroutine-tests/control-flow.rs index 6e48b96e1d2a..af8d8a2250a4 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/control-flow.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/control-flow.rs @@ -35,32 +35,52 @@ where #[kani::proof] #[kani::unwind(16)] fn main() { - finish(1, || yield); - finish(8, || { - for _ in 0..8 { - yield; - } - }); - finish(1, || { - if true { - yield; - } else { - } - }); - finish(1, || { - if false { - } else { - yield; - } - }); - finish(2, || { - if { - yield; - false - } { - yield; - panic!() - } - yield - }); + finish( + 1, + #[coroutine] + || yield, + ); + finish( + 8, + #[coroutine] + || { + for _ in 0..8 { + yield; + } + }, + ); + finish( + 1, + #[coroutine] + || { + if true { + yield; + } else { + } + }, + ); + finish( + 1, + #[coroutine] + || { + if false { + } else { + yield; + } + }, + ); + finish( + 2, + #[coroutine] + || { + if { + yield; + false + } { + yield; + panic!() + } + yield + }, + ); } diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/env-drop.rs b/tests/kani/Coroutines/rustc-coroutine-tests/env-drop.rs index a6420aca283b..e1de81c9b081 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/env-drop.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/env-drop.rs @@ -12,6 +12,7 @@ //[nomiropt]compile-flags: -Z mir-opt-level=0 #![feature(coroutines, coroutine_trait)] +#![feature(stmt_expr_attributes)] use std::ops::Coroutine; use std::pin::Pin; @@ -36,7 +37,8 @@ fn main() { fn t1() { let b = B; - let mut foo = || { + let mut foo = #[coroutine] + || { yield; drop(b); }; @@ -50,7 +52,8 @@ fn t1() { fn t2() { let b = B; - let mut foo = || { + let mut foo = #[coroutine] + || { yield b; }; @@ -63,7 +66,8 @@ fn t2() { fn t3() { let b = B; - let foo = || { + let foo = #[coroutine] + || { yield; drop(b); }; diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/iterator-count.rs b/tests/kani/Coroutines/rustc-coroutine-tests/iterator-count.rs index caa2efec216f..8a9b26262e5d 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/iterator-count.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/iterator-count.rs @@ -30,6 +30,7 @@ impl + Unpin> Iterator for W { } fn test() -> impl Coroutine<(), Return = (), Yield = u8> + Unpin { + #[coroutine] || { for i in 1..6 { yield i @@ -43,6 +44,7 @@ fn main() { let end = 11; let closure_test = |start| { + #[coroutine] move || { for i in start..end { yield i diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/live-upvar-across-yield.rs b/tests/kani/Coroutines/rustc-coroutine-tests/live-upvar-across-yield.rs index 9bce8679464f..d9aae2e95322 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/live-upvar-across-yield.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/live-upvar-across-yield.rs @@ -9,6 +9,7 @@ // run-pass #![feature(coroutines, coroutine_trait)] +#![feature(stmt_expr_attributes)] use std::ops::Coroutine; use std::pin::Pin; @@ -16,7 +17,8 @@ use std::pin::Pin; #[kani::proof] fn main() { let b = |_| 3; - let mut a = || { + let mut a = #[coroutine] + || { b(yield); }; Pin::new(&mut a).resume(()); diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/moved-locals-size.rs b/tests/kani/Coroutines/rustc-coroutine-tests/moved-locals-size.rs index d63d34427ca6..3c2b9dc16ae1 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/moved-locals-size.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/moved-locals-size.rs @@ -41,6 +41,7 @@ impl Drop for Foo { } fn move_before_yield() -> impl Coroutine { + #[coroutine] static || { let first = Foo([0; FOO_SIZE]); let _second = first; @@ -52,6 +53,7 @@ fn move_before_yield() -> impl Coroutine { fn noop() {} fn move_before_yield_with_noop() -> impl Coroutine { + #[coroutine] static || { let first = Foo([0; FOO_SIZE]); noop(); @@ -64,6 +66,7 @@ fn move_before_yield_with_noop() -> impl Coroutine { // Today we don't have NRVO (we allocate space for both `first` and `second`,) // but we can overlap `first` with `_third`. fn overlap_move_points() -> impl Coroutine { + #[coroutine] static || { let first = Foo([0; FOO_SIZE]); yield; @@ -75,6 +78,7 @@ fn overlap_move_points() -> impl Coroutine { } fn overlap_x_and_y() -> impl Coroutine { + #[coroutine] static || { let x = Foo([0; FOO_SIZE]); yield; diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/moved-locals.rs b/tests/kani/Coroutines/rustc-coroutine-tests/moved-locals.rs index b3b6c4cc6767..6d54a54190e1 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/moved-locals.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/moved-locals.rs @@ -34,6 +34,7 @@ impl Drop for Foo { } fn move_before_yield() -> impl Coroutine { + #[coroutine] static || { let first = Foo([0; FOO_SIZE]); let _second = first; @@ -45,6 +46,7 @@ fn move_before_yield() -> impl Coroutine { fn noop() {} fn move_before_yield_with_noop() -> impl Coroutine { + #[coroutine] static || { let first = Foo([0; FOO_SIZE]); noop(); @@ -57,6 +59,7 @@ fn move_before_yield_with_noop() -> impl Coroutine { // Today we don't have NRVO (we allocate space for both `first` and `second`,) // but we can overlap `first` with `_third`. fn overlap_move_points() -> impl Coroutine { + #[coroutine] static || { let first = Foo([0; FOO_SIZE]); yield; @@ -68,6 +71,7 @@ fn overlap_move_points() -> impl Coroutine { } fn overlap_x_and_y() -> impl Coroutine { + #[coroutine] static || { let x = Foo([0; FOO_SIZE]); yield; diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/nested-generators.rs b/tests/kani/Coroutines/rustc-coroutine-tests/nested-generators.rs index 0d770380e2b9..8dfd8cdf065c 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/nested-generators.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/nested-generators.rs @@ -9,14 +9,17 @@ // run-pass #![feature(coroutines, coroutine_trait)] +#![feature(stmt_expr_attributes)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; #[kani::proof] fn main() { - let _coroutine = || { - let mut sub_coroutine = || { + let _coroutine = #[coroutine] + || { + let mut sub_coroutine = #[coroutine] + || { yield 2; }; diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/niche-in-generator-size.rs b/tests/kani/Coroutines/rustc-coroutine-tests/niche-in-generator-size.rs index 5de21166c318..68b0a8589d3c 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/niche-in-generator-size.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/niche-in-generator-size.rs @@ -11,6 +11,7 @@ // run-pass #![feature(coroutines)] +#![feature(stmt_expr_attributes)] use std::mem::size_of_val; @@ -19,7 +20,8 @@ fn take(_: T) {} #[kani::proof] fn main() { let x = false; - let gen1 = || { + let gen1 = #[coroutine] + || { yield; take(x); }; diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/niche-in-generator.rs b/tests/kani/Coroutines/rustc-coroutine-tests/niche-in-generator.rs index 170a356fb318..f882459910ec 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/niche-in-generator.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/niche-in-generator.rs @@ -11,6 +11,7 @@ // run-pass #![feature(coroutines, coroutine_trait)] +#![feature(stmt_expr_attributes)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; @@ -22,7 +23,8 @@ fn take(_: T) {} #[kani::proof] fn main() { let x = false; - let mut gen1 = || { + let mut gen1 = #[coroutine] + || { yield; take(x); }; diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/overlap-locals-size.rs b/tests/kani/Coroutines/rustc-coroutine-tests/overlap-locals-size.rs index d22ed53f8b60..d7be6ba706f5 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/overlap-locals-size.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/overlap-locals-size.rs @@ -9,10 +9,12 @@ // run-pass #![feature(coroutines)] +#![feature(stmt_expr_attributes)] #[kani::proof] fn main() { - let a = || { + let a = #[coroutine] + || { { let w: i32 = 4; yield; diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/overlap-locals.rs b/tests/kani/Coroutines/rustc-coroutine-tests/overlap-locals.rs index 6033b2f06a99..56ba9d346d33 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/overlap-locals.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/overlap-locals.rs @@ -9,13 +9,15 @@ // run-pass #![feature(coroutines, coroutine_trait)] +#![feature(stmt_expr_attributes)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; #[kani::proof] fn main() { - let mut a = || { + let mut a = #[coroutine] + || { { let w: i32 = 4; yield; diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/resume-arg-size.rs b/tests/kani/Coroutines/rustc-coroutine-tests/resume-arg-size.rs index f59ef260bcda..958c3ea81d22 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/resume-arg-size.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/resume-arg-size.rs @@ -7,6 +7,7 @@ // See GitHub history for details. #![feature(coroutines)] +#![feature(stmt_expr_attributes)] // run-pass @@ -15,7 +16,8 @@ use std::mem::size_of_val; #[kani::proof] fn main() { // Coroutine taking a `Copy`able resume arg. - let gen_copy = |mut x: usize| { + let gen_copy = #[coroutine] + |mut x: usize| { loop { drop(x); x = yield; @@ -23,7 +25,8 @@ fn main() { }; // Coroutine taking a non-`Copy` resume arg. - let gen_move = |mut x: Box| { + let gen_move = #[coroutine] + |mut x: Box| { loop { drop(x); x = yield; diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/resume-arg.rs b/tests/kani/Coroutines/rustc-coroutine-tests/resume-arg.rs index 0c2c87f5eb2e..c514ebf03eb7 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/resume-arg.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/resume-arg.rs @@ -7,6 +7,7 @@ // See GitHub history for details. #![feature(coroutines, coroutine_trait)] +#![feature(stmt_expr_attributes)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; @@ -18,7 +19,8 @@ use std::mem::size_of_val; #[kani::proof] fn main() { // Coroutine taking a `Copy`able resume arg. - let mut gen_copy = |mut x: usize| { + let mut gen_copy = #[coroutine] + |mut x: usize| { loop { drop(x); x = yield; @@ -26,7 +28,8 @@ fn main() { }; // Coroutine taking a non-`Copy` resume arg. - let mut gen_move = |mut x: Box| { + let mut gen_move = #[coroutine] + |mut x: Box| { loop { drop(x); x = yield; diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/resume-live-across-yield.rs b/tests/kani/Coroutines/rustc-coroutine-tests/resume-live-across-yield.rs index 10d4d36223d5..48c07a1a1fe8 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/resume-live-across-yield.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/resume-live-across-yield.rs @@ -9,6 +9,7 @@ // run-pass #![feature(coroutines, coroutine_trait)] +#![feature(stmt_expr_attributes)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; @@ -28,7 +29,8 @@ impl Drop for Dropper { #[kani::proof] #[kani::unwind(16)] fn main() { - let mut g = |mut _d| { + let mut g = #[coroutine] + |mut _d| { _d = yield; _d }; diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/smoke-resume-args.rs b/tests/kani/Coroutines/rustc-coroutine-tests/smoke-resume-args.rs index 85c75bc8147d..cc63b6b21186 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/smoke-resume-args.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/smoke-resume-args.rs @@ -12,6 +12,7 @@ //[nomiropt]compile-flags: -Z mir-opt-level=0 #![feature(coroutines, coroutine_trait)] +#![feature(stmt_expr_attributes)] use std::fmt::Debug; use std::marker::Unpin; @@ -61,7 +62,8 @@ fn expect_drops(expected_drops: usize, f: impl FnOnce() -> T) -> T { #[kani::unwind(8)] fn main() { drain( - &mut |mut b| { + &mut #[coroutine] + |mut b| { while b != 0 { b = yield (b + 1); } @@ -70,21 +72,35 @@ fn main() { vec![(1, Yielded(2)), (-45, Yielded(-44)), (500, Yielded(501)), (0, Complete(-1))], ); - expect_drops(2, || drain(&mut |a| yield a, vec![(DropMe, Yielded(DropMe))])); + expect_drops(2, || { + drain( + &mut #[coroutine] + |a| yield a, + vec![(DropMe, Yielded(DropMe))], + ) + }); expect_drops(6, || { drain( - &mut |a| yield yield a, + &mut #[coroutine] + |a| yield yield a, vec![(DropMe, Yielded(DropMe)), (DropMe, Yielded(DropMe)), (DropMe, Complete(DropMe))], ) }); #[allow(unreachable_code)] - expect_drops(2, || drain(&mut |a| yield return a, vec![(DropMe, Complete(DropMe))])); + expect_drops(2, || { + drain( + &mut #[coroutine] + |a| yield return a, + vec![(DropMe, Complete(DropMe))], + ) + }); expect_drops(2, || { drain( - &mut |a: DropMe| { + &mut #[coroutine] + |a: DropMe| { if false { yield () } else { a } }, vec![(DropMe, Complete(DropMe))], @@ -94,7 +110,8 @@ fn main() { expect_drops(4, || { drain( #[allow(unused_assignments, unused_variables)] - &mut |mut a: DropMe| { + &mut #[coroutine] + |mut a: DropMe| { a = yield; a = yield; a = yield; diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/smoke.rs b/tests/kani/Coroutines/rustc-coroutine-tests/smoke.rs index 2b9aa40a06c0..c69513d00d99 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/smoke.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/smoke.rs @@ -15,6 +15,7 @@ // compile-flags: --test #![feature(coroutines, coroutine_trait)] +#![feature(stmt_expr_attributes)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; @@ -22,7 +23,8 @@ use std::thread; #[kani::proof] fn simple() { - let mut foo = || { + let mut foo = #[coroutine] + || { if false { yield; } @@ -38,7 +40,8 @@ fn simple() { #[kani::unwind(4)] fn return_capture() { let a = String::from("foo"); - let mut foo = || { + let mut foo = #[coroutine] + || { if false { yield; } @@ -53,7 +56,8 @@ fn return_capture() { #[kani::proof] fn simple_yield() { - let mut foo = || { + let mut foo = #[coroutine] + || { yield; }; @@ -71,7 +75,8 @@ fn simple_yield() { #[kani::unwind(4)] fn yield_capture() { let b = String::from("foo"); - let mut foo = || { + let mut foo = #[coroutine] + || { yield b; }; @@ -88,7 +93,8 @@ fn yield_capture() { #[kani::proof] #[kani::unwind(4)] fn simple_yield_value() { - let mut foo = || { + let mut foo = #[coroutine] + || { yield String::from("bar"); return String::from("foo"); }; @@ -107,7 +113,8 @@ fn simple_yield_value() { #[kani::unwind(4)] fn return_after_yield() { let a = String::from("foo"); - let mut foo = || { + let mut foo = #[coroutine] + || { yield; return a; }; @@ -124,43 +131,65 @@ fn return_after_yield() { // This test is useless for Kani fn send_and_sync() { - assert_send_sync(|| yield); - assert_send_sync(|| { - yield String::from("foo"); - }); - assert_send_sync(|| { - yield; - return String::from("foo"); - }); + assert_send_sync( + #[coroutine] + || yield, + ); + assert_send_sync( + #[coroutine] + || { + yield String::from("foo"); + }, + ); + assert_send_sync( + #[coroutine] + || { + yield; + return String::from("foo"); + }, + ); let a = 3; - assert_send_sync(|| { - yield a; - return; - }); + assert_send_sync( + #[coroutine] + || { + yield a; + return; + }, + ); let a = 3; - assert_send_sync(move || { - yield a; - return; - }); + assert_send_sync( + #[coroutine] + move || { + yield a; + return; + }, + ); let a = String::from("a"); - assert_send_sync(|| { - yield; - drop(a); - return; - }); + assert_send_sync( + #[coroutine] + || { + yield; + drop(a); + return; + }, + ); let a = String::from("a"); - assert_send_sync(move || { - yield; - drop(a); - return; - }); + assert_send_sync( + #[coroutine] + move || { + yield; + drop(a); + return; + }, + ); fn assert_send_sync(_: T) {} } // Kani does not support threads, so we cannot run this test: fn send_over_threads() { - let mut foo = || yield; + let mut foo = #[coroutine] + || yield; thread::spawn(move || { match Pin::new(&mut foo).resume(()) { CoroutineState::Yielded(()) => {} @@ -175,7 +204,8 @@ fn send_over_threads() { .unwrap(); let a = String::from("a"); - let mut foo = || yield a; + let mut foo = #[coroutine] + || yield a; thread::spawn(move || { match Pin::new(&mut foo).resume(()) { CoroutineState::Yielded(ref s) if *s == "a" => {} diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/static-generator.rs b/tests/kani/Coroutines/rustc-coroutine-tests/static-generator.rs index 52f89438255a..9b8ca468f9ee 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/static-generator.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/static-generator.rs @@ -9,13 +9,15 @@ // run-pass #![feature(coroutines, coroutine_trait)] +#![feature(stmt_expr_attributes)] use std::ops::{Coroutine, CoroutineState}; use std::pin::Pin; #[kani::proof] fn main() { - let mut coroutine = static || { + let mut coroutine = #[coroutine] + static || { let a = true; let b = &a; yield; diff --git a/tests/kani/Coroutines/rustc-coroutine-tests/yield-in-box.rs b/tests/kani/Coroutines/rustc-coroutine-tests/yield-in-box.rs index 5dbf580f5f57..79d6798855ff 100644 --- a/tests/kani/Coroutines/rustc-coroutine-tests/yield-in-box.rs +++ b/tests/kani/Coroutines/rustc-coroutine-tests/yield-in-box.rs @@ -10,6 +10,7 @@ // Test that box-statements with yields in them work. #![feature(coroutines, coroutine_trait)] +#![feature(stmt_expr_attributes)] use std::ops::Coroutine; use std::ops::CoroutineState; use std::pin::Pin; @@ -17,6 +18,7 @@ use std::pin::Pin; #[kani::proof] fn main() { let x = 0i32; + #[coroutine] || { //~ WARN unused coroutine that must be used let y = 2u32; @@ -28,7 +30,8 @@ fn main() { } }; - let mut g = |_| Box::new(yield); + let mut g = #[coroutine] + |_| Box::new(yield); assert_eq!(Pin::new(&mut g).resume(1), CoroutineState::Yielded(())); assert_eq!(Pin::new(&mut g).resume(2), CoroutineState::Complete(Box::new(2))); } diff --git a/tests/kani/Intrinsics/Count/ctlz.rs b/tests/kani/Intrinsics/Count/ctlz.rs index 6c709137e5f4..9149b5bc8f54 100644 --- a/tests/kani/Intrinsics/Count/ctlz.rs +++ b/tests/kani/Intrinsics/Count/ctlz.rs @@ -11,7 +11,7 @@ use std::intrinsics::{ctlz, ctlz_nonzero}; // the same for any value macro_rules! test_ctlz { ( $fn_name:ident, $ty:ty ) => { - fn $fn_name(x: $ty) -> $ty { + fn $fn_name(x: $ty) -> u32 { let mut count = 0; let num_bits = <$ty>::BITS; for i in 0..num_bits { diff --git a/tests/kani/Intrinsics/Count/ctpop.rs b/tests/kani/Intrinsics/Count/ctpop.rs index d2eb972f83aa..19d3ac4fd5a6 100644 --- a/tests/kani/Intrinsics/Count/ctpop.rs +++ b/tests/kani/Intrinsics/Count/ctpop.rs @@ -10,7 +10,7 @@ use std::intrinsics::ctpop; // the same for any value macro_rules! test_ctpop { ( $fn_name:ident, $ty:ty ) => { - fn $fn_name(x: $ty) -> $ty { + fn $fn_name(x: $ty) -> u32 { let mut count = 0; let num_bits = <$ty>::BITS; for i in 0..num_bits { diff --git a/tests/kani/Intrinsics/Count/cttz.rs b/tests/kani/Intrinsics/Count/cttz.rs index fa7c5f6a03e5..0429c6efce08 100644 --- a/tests/kani/Intrinsics/Count/cttz.rs +++ b/tests/kani/Intrinsics/Count/cttz.rs @@ -11,7 +11,7 @@ use std::intrinsics::{cttz, cttz_nonzero}; // the same for any value macro_rules! test_cttz { ( $fn_name:ident, $ty:ty ) => { - fn $fn_name(x: $ty) -> $ty { + fn $fn_name(x: $ty) -> u32 { let mut count = 0; let num_bits = <$ty>::BITS; for i in 0..num_bits { diff --git a/tests/kani/Intrinsics/Rotate/rotate_left.rs b/tests/kani/Intrinsics/Rotate/rotate_left.rs index d44ef1347745..eac46f968b03 100644 --- a/tests/kani/Intrinsics/Rotate/rotate_left.rs +++ b/tests/kani/Intrinsics/Rotate/rotate_left.rs @@ -24,7 +24,7 @@ macro_rules! test_rotate_left { let n: u32 = kani::any(); // Limit `n` to `u8::MAX` to avoid overflows kani::assume(n <= u8::MAX as u32); - let y: $ty = rotate_left(x, n as $ty); + let y: $ty = rotate_left(x, n); // Check that the rotation is correct $fn_name(x, y, n); // Check that the stable version returns the same value diff --git a/tests/kani/Intrinsics/Rotate/rotate_right.rs b/tests/kani/Intrinsics/Rotate/rotate_right.rs index 584821e5784f..9b88b1250b2f 100644 --- a/tests/kani/Intrinsics/Rotate/rotate_right.rs +++ b/tests/kani/Intrinsics/Rotate/rotate_right.rs @@ -31,7 +31,7 @@ macro_rules! test_rotate_right { let n: u32 = kani::any(); // Limit `n` to `u8::MAX` to avoid overflows kani::assume(n <= u8::MAX as u32); - let y: $ty = rotate_right(x, n as $ty); + let y: $ty = rotate_right(x, n); // Check that the rotation is correct $fn_name(x, y, n); // Check that the stable version returns the same value From 33b7d853c274aa4dc24cddd57fec196087225f01 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 May 2024 08:13:03 -0400 Subject: [PATCH 096/225] Automatic toolchain upgrade to nightly-2024-05-15 (#3185) Update Rust toolchain from nightly-2024-05-14 to nightly-2024-05-15 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/ab14f944afe4234db378ced3801e637eae6c0f30 up to https://github.com/rust-lang/rust/commit/8387315ab3c26a57a1f53a90f188f0bc88514bca. The log for this commit range is: https://github.com/rust-lang/rust/commit/8387315ab3 Auto merge of #125125 - lovesegfault:opt-dist-specify-rustc-perf, r=Mark-Simulacrum https://github.com/rust-lang/rust/commit/c3c9783de2 feat(tools/opt-dist): allow local builds to specify a rustc-perf checkout https://github.com/rust-lang/rust/commit/ac385a5af6 Auto merge of #125120 - compiler-errors:rollup-mnjybwv, r=compiler-errors https://github.com/rust-lang/rust/commit/31016d5879 Rollup merge of #125118 - GuillaumeGomez:cleanup-run-make, r=jieyouxu https://github.com/rust-lang/rust/commit/d59f430eec Rollup merge of #125100 - compiler-errors:faster, r=nnethercote https://github.com/rust-lang/rust/commit/712e7c37f7 Rollup merge of #125088 - compiler-errors:uplift-alias-ty, r=lcnr https://github.com/rust-lang/rust/commit/8c64acdbdc Rollup merge of #125080 - bvanjoi:fix-124946, r=nnethercote https://github.com/rust-lang/rust/commit/844c7e826e Rollup merge of #125047 - Oneirical:test5, r=jieyouxu https://github.com/rust-lang/rust/commit/0458d8a53b Rollup merge of #124844 - compiler-errors:shadow-probe, r=lcnr https://github.com/rust-lang/rust/commit/36287830a2 Rollup merge of #119838 - joshtriplett:style-guide-binop-indent, r=compiler-errors https://github.com/rust-lang/rust/commit/ade33b02f2 only find segs chain for missing methods when no available candidates https://github.com/rust-lang/rust/commit/bdfd941f4d Auto merge of #123816 - tgross35:f16-f128-mangling, r=michaelwoerister https://github.com/rust-lang/rust/commit/f97d915173 Use new utility functions/methods in run-make tests https://github.com/rust-lang/rust/commit/792a9bdd4b Enable v0 mangling tests and add checks for `f16`/`f128` https://github.com/rust-lang/rust/commit/809b84edba Add v0 symbol mangling for `f16` and `f128` https://github.com/rust-lang/rust/commit/31026b7fe3 Auto merge of #125023 - morr0ne:linux-none-target, r=Nilstrieb https://github.com/rust-lang/rust/commit/68407f9049 fix typo in x86_64-unknown-linux-none docs https://github.com/rust-lang/rust/commit/c45e831d8f Auto merge of #124228 - compiler-errors:lint-overcaptures, r=oli-obk https://github.com/rust-lang/rust/commit/58426f4a5b Auto merge of #125026 - Oneirical:clink-tests, r=jieyouxu https://github.com/rust-lang/rust/commit/e098eb14ae Wording improvement https://github.com/rust-lang/rust/commit/e2d9c0d938 Fix missing word https://github.com/rust-lang/rust/commit/57c32a193f style-guide: When breaking binops handle multi-line first operand better https://github.com/rust-lang/rust/commit/dbd2ca6478 Use a proper probe for shadowing impl https://github.com/rust-lang/rust/commit/052de1da4f And finally add tests https://github.com/rust-lang/rust/commit/1529c661e4 Warn against redundant use<...> https://github.com/rust-lang/rust/commit/f3fb727b08 Don't suggest using use<> syntax to capture APITs https://github.com/rust-lang/rust/commit/6afe1352d9 Suggest adding use<> syntax https://github.com/rust-lang/rust/commit/554becc180 Add some commenting https://github.com/rust-lang/rust/commit/d57e57ca1f Implement initial IMPL_TRAIT_OVERCAPTURES lint https://github.com/rust-lang/rust/commit/8f97a2588c Add test to make sure suggestions are still quick https://github.com/rust-lang/rust/commit/fba5f44bd8 Auto merge of #125098 - jhpratt:rollup-2qm4gga, r=jhpratt https://github.com/rust-lang/rust/commit/45b50d303c lto function, static_library call, rename https://github.com/rust-lang/rust/commit/9f8cdb286e Remove to_term https://github.com/rust-lang/rust/commit/1ad28a6f53 Uplift AliasTy https://github.com/rust-lang/rust/commit/812f89728a fix fmt https://github.com/rust-lang/rust/commit/2e4c90c3f7 Don't do post-method-probe error reporting steps if we're in a suggestion https://github.com/rust-lang/rust/commit/32d74f1800 Rollup merge of #125090 - erickt:bump-fuchsia, r=tmandry https://github.com/rust-lang/rust/commit/209703af85 Rollup merge of #125072 - Darksonn:pin-dyn-dispatch-sound, r=jhpratt https://github.com/rust-lang/rust/commit/18d9c039bb Rollup merge of #124997 - gurry:124848-ice-should-be-sized, r=Nadrieril https://github.com/rust-lang/rust/commit/74a78af0e2 Rollup merge of #116675 - joshlf:patch-10, r=scottmcm https://github.com/rust-lang/rust/commit/9105c57b7f Auto merge of #124256 - nnethercote:rm-NtIdent-NtLifetime, r=petrochenkov https://github.com/rust-lang/rust/commit/34582118af Auto merge of #125076 - compiler-errors:alias-term, r=lcnr https://github.com/rust-lang/rust/commit/95e519ecbf Remove `NtIdent` and `NtLifetime`. https://github.com/rust-lang/rust/commit/fa84018c2e Apply nits https://github.com/rust-lang/rust/commit/58ee9192e0 Migrate fuchsia docs from `pm` to `ffx` https://github.com/rust-lang/rust/commit/293b5cb1ca [ptr] Document maximum allocation size https://github.com/rust-lang/rust/commit/3bcdf3058e split out AliasTy -> AliasTerm https://github.com/rust-lang/rust/commit/b3a78c1d09 Add test for dynamic dispatch + Pin::new soundness https://github.com/rust-lang/rust/commit/9a63a42cb7 Remove a `Span` from `TokenKind::Interpolated`. https://github.com/rust-lang/rust/commit/71fd2cf5b4 fix function call and import https://github.com/rust-lang/rust/commit/a1b5ea0cc2 make tidy happy https://github.com/rust-lang/rust/commit/f2de5fb2ae rewrite issue-14500 to rmake https://github.com/rust-lang/rust/commit/a6f237ca85 docs: fix typo in platform-support docs https://github.com/rust-lang/rust/commit/923cdb35aa test: Add assembly tests for x86_64-unknown-linux-none target https://github.com/rust-lang/rust/commit/10c358f111 Make tidy happy https://github.com/rust-lang/rust/commit/e37d2989c1 remove trailing whitespace https://github.com/rust-lang/rust/commit/a2e7e79a13 Port c-link-to-rust-va-list-fn to Rust https://github.com/rust-lang/rust/commit/9cf080099d docs: Document x86_64-unknown-linux-none target https://github.com/rust-lang/rust/commit/a3ef01b1fc Add x86_64-unknown-linux-none target https://github.com/rust-lang/rust/commit/fb619ec208 FIx ICE while casting a type with error 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 021cbbf7a8ff..b0aa16a36627 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-05-14" +channel = "nightly-2024-05-15" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From 9b9e473f7c253b8141d45ec4d67bae0efc06e2e4 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Wed, 15 May 2024 19:42:58 -0700 Subject: [PATCH 097/225] Include `--check-cfg=cfg(kani)` in the rust flags to avoid a warning about an unknown `cfg`. (#3187) Starting with the 2024-05-05 toolchain (and the upcoming Rust 1.80 release), the `unexpected_cfgs` lint has been turned on by default. As a result, running `cargo kani` on a crate that has a `#[cfg(kani)]` results in a warning (see #3186). To avoid this warning, this PR adds `--check-cfg=cfg(kani)` to `RUSTFLAGS` when Kani invokes `cargo`. Call-outs: On such packages, doing a `cargo build` will also result in this warning, unless: ```rust println!("cargo::rustc-check-cfg=cfg(kani)"); ``` is added to the package's `build.rs` file. However, this warning would only occur with `cargo build` if the package uses the 2024-05-05 toolchain (or newer), or the Rust version used in the package is upgraded to 1.80 (when it's released at the end of July 2024). Since we're likely to release a new version of Kani sooner than the 1.80 release, this PR mitigates the issue that is more likely to impact users (a warning from `cargo kani`). Resolves #3186 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_single_file.rs | 1 + tests/cargo-kani/unexpected_cfgs/Cargo.toml | 9 +++++++++ tests/cargo-kani/unexpected_cfgs/expected | 1 + tests/cargo-kani/unexpected_cfgs/src/main.rs | 19 +++++++++++++++++++ 4 files changed, 30 insertions(+) create mode 100644 tests/cargo-kani/unexpected_cfgs/Cargo.toml create mode 100644 tests/cargo-kani/unexpected_cfgs/expected create mode 100644 tests/cargo-kani/unexpected_cfgs/src/main.rs diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index 07899c8c4899..1c8d63d6d87a 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -133,6 +133,7 @@ impl KaniSession { "panic_abort_tests=yes", "-Z", "mir-enable-passes=-RemoveStorageMarkers", + "--check-cfg=cfg(kani)", ] .map(OsString::from), ); diff --git a/tests/cargo-kani/unexpected_cfgs/Cargo.toml b/tests/cargo-kani/unexpected_cfgs/Cargo.toml new file mode 100644 index 000000000000..c70a4cbec7dc --- /dev/null +++ b/tests/cargo-kani/unexpected_cfgs/Cargo.toml @@ -0,0 +1,9 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +[package] +name = "unexpected_cfgs" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/tests/cargo-kani/unexpected_cfgs/expected b/tests/cargo-kani/unexpected_cfgs/expected new file mode 100644 index 000000000000..34c886c358cb --- /dev/null +++ b/tests/cargo-kani/unexpected_cfgs/expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL diff --git a/tests/cargo-kani/unexpected_cfgs/src/main.rs b/tests/cargo-kani/unexpected_cfgs/src/main.rs new file mode 100644 index 000000000000..e864756c6be9 --- /dev/null +++ b/tests/cargo-kani/unexpected_cfgs/src/main.rs @@ -0,0 +1,19 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// This test checks that the `unexpected_cfgs` lint (enabled by default as of +// the 2024-05-05 toolchain) does not cause `cargo kani` to emit warnings when +// the code has `#[cfg(kani)]`. Kani avoids the warning by adding +// `--check-cfg=cfg(kani)` to the rust flags. + +#![deny(unexpected_cfgs)] + +fn main() {} + +#[cfg(kani)] +mod kani_checks { + #[kani::proof] + fn check_unexpected_cfg() { + assert_eq!(1, 1); + } +} From 695bdfc2242dd3767bb5e0fd759a398ea8057b57 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 May 2024 22:44:42 -0700 Subject: [PATCH 098/225] Automatic toolchain upgrade to nightly-2024-05-16 (#3189) Update Rust toolchain from nightly-2024-05-15 to nightly-2024-05-16 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/8387315ab3c26a57a1f53a90f188f0bc88514bca up to https://github.com/rust-lang/rust/commit/1871252fc8bb672d40787e67404e6eaae7059369. The log for this commit range is: https://github.com/rust-lang/rust/commit/1871252fc8 Auto merge of #125164 - fmease:rollup-s5vwzlg, r=fmease https://github.com/rust-lang/rust/commit/734a109998 Rollup merge of #125159 - fmease:allow-unauth-labels-l-pg-z, r=jieyouxu https://github.com/rust-lang/rust/commit/601e5d199f Rollup merge of #125154 - FractalFir:fnabi_doc, r=compiler-errors https://github.com/rust-lang/rust/commit/09156291e5 Rollup merge of #125146 - Oneirical:panic-impl, r=jieyouxu https://github.com/rust-lang/rust/commit/80f991e09b Rollup merge of #125142 - GuillaumeGomez:migrate-rustdoc-themes, r=jieyouxu https://github.com/rust-lang/rust/commit/c5b17ec9d2 Rollup merge of #125003 - RalfJung:aligned_alloc, r=cuviper https://github.com/rust-lang/rust/commit/257d222e4b Improved the documentation of the FnAbi struct https://github.com/rust-lang/rust/commit/72a48fc68c Allow unauthenticated users to modify `L-*`, `PG-*` and `-Z*` labels https://github.com/rust-lang/rust/commit/b21b74b5e6 Auto merge of #125134 - compiler-errors:negative-traits-are-not-notable, r=fmease https://github.com/rust-lang/rust/commit/a7484d2e49 fix tidy https://github.com/rust-lang/rust/commit/cae17ff42b rewrite panic-impl-transitive https://github.com/rust-lang/rust/commit/ade234d574 Auto merge of #125144 - fmease:rollup-4uft293, r=fmease https://github.com/rust-lang/rust/commit/8d38f2fb11 Rollup merge of #125137 - RalfJung:mir-sh, r=scottmcm https://github.com/rust-lang/rust/commit/2659ff3882 Rollup merge of #125104 - Oneirical:test6, r=jieyouxu https://github.com/rust-lang/rust/commit/4f7d9d4ad8 Rollup merge of #125038 - ivan-shrimp:checked_sub, r=joboet https://github.com/rust-lang/rust/commit/2804d4223b Rollup merge of #125027 - Oneirical:c-test-with-remove, r=jieyouxu https://github.com/rust-lang/rust/commit/2e70bea168 Rollup merge of #124975 - lu-zero:move_file, r=clubby789 https://github.com/rust-lang/rust/commit/3873a74f8a Rollup merge of #124307 - reitermarkus:escape-debug-size-hint-inline, r=joboet https://github.com/rust-lang/rust/commit/3cb0030fe9 Auto merge of #123413 - petrochenkov:delegmulti2, r=fmease https://github.com/rust-lang/rust/commit/c765480efe Migrate `run-make/rustdoc-themes` to new rmake https://github.com/rust-lang/rust/commit/c87ae947eb Add new `htmldocck` function to `run-make-support` https://github.com/rust-lang/rust/commit/a71c3ffce9 Auto merge of #125032 - compiler-errors:crash-dump-dir, r=onur-ozkan https://github.com/rust-lang/rust/commit/0afd50e852 MIR operators: clarify Shl/Shr handling of negative offsets https://github.com/rust-lang/rust/commit/44fa5fd39a Auto merge of #125136 - matthiaskrgr:rollup-ljm15m3, r=matthiaskrgr https://github.com/rust-lang/rust/commit/5f1a120ee5 Rollup merge of #125135 - chenyukang:yukang-fix-116502, r=compiler-errors https://github.com/rust-lang/rust/commit/f7c2934420 Rollup merge of #125132 - mejrs:diag, r=compiler-errors https://github.com/rust-lang/rust/commit/a8ff937b07 Rollup merge of #125108 - Zalathar:info-bitmap-bytes, r=nnethercote https://github.com/rust-lang/rust/commit/03ff673dcc Rollup merge of #124990 - fmease:expand-weak-aliases-within-cts, r=compiler-errors https://github.com/rust-lang/rust/commit/75895f59b0 Fix the dedup error because of spans from suggestion https://github.com/rust-lang/rust/commit/9e7aff7945 Auto merge of #125031 - Oneirical:dynamic-libs, r=jieyouxu https://github.com/rust-lang/rust/commit/8994840f7e rustdoc: Negative impls are not notable https://github.com/rust-lang/rust/commit/91a3f04a3f fix the test https://github.com/rust-lang/rust/commit/0160bff4b1 Auto merge of #125084 - Jules-Bertholet:fix-125058, r=Nadrieril https://github.com/rust-lang/rust/commit/c30b41012d delegation: Implement list delegation https://github.com/rust-lang/rust/commit/18d7411719 Add `on_unimplemented" typo suggestions https://github.com/rust-lang/rust/commit/81f7e54962 Port issue-11908 to rmake https://github.com/rust-lang/rust/commit/1f61cc3078 port no-cdylib-as-rdylib test https://github.com/rust-lang/rust/commit/b1e5e5161a remove cxx_flags https://github.com/rust-lang/rust/commit/1f5837ae25 rewrite c-link-to-rust-staticlib https://github.com/rust-lang/rust/commit/5cc020d3df avoid using aligned_alloc; posix_memalign is better-behaved https://github.com/rust-lang/rust/commit/c81be68fb4 coverage: Remove confusing comments from `CoverageKind` https://github.com/rust-lang/rust/commit/bfadc3a9b9 coverage: `CoverageIdsInfo::mcdc_bitmap_bytes` is never needed https://github.com/rust-lang/rust/commit/fe8f66e4bc `rustc_hir_typeck`: Account for `skipped_ref_pats` in `expr_use_visitor` https://github.com/rust-lang/rust/commit/4db00fe229 Use an helper to move the files https://github.com/rust-lang/rust/commit/7fde7308bf reverse condition in `uN::checked_sub` https://github.com/rust-lang/rust/commit/848f3c2c6e Make crashes dump mir to build dir https://github.com/rust-lang/rust/commit/35a5be2833 Also expand weak alias tys inside consts inside `expand_weak_alias_tys` https://github.com/rust-lang/rust/commit/4edf12d33e Improve escape methods. https://github.com/rust-lang/rust/commit/16981ba406 Avoid panicking branch in `EscapeIterInner`. https://github.com/rust-lang/rust/commit/e3fc97be2b Inline `EscapeDebug::size_hint`. Co-authored-by: celinval <35149715+celinval@users.noreply.github.com> Co-authored-by: Zyad Hassan <88045115+zhassan-aws@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 b0aa16a36627..458b35d6877b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-05-15" +channel = "nightly-2024-05-16" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From 6161bfdf6a191cc5297e5792cea9951d5e3a492c Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Fri, 17 May 2024 10:20:42 -0700 Subject: [PATCH 099/225] Perform cargo update because of yanked libc version (#3192) Our `Cargo.lock` uses https://crates.io/crates/libc/0.2.154, which has been yanked. Do a `cargo update` to use a different version. 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 | 66 +++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 51d119effd65..e4faadc00d33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,9 +102,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] @@ -169,7 +169,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -290,9 +290,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "encode_unicode" @@ -333,9 +333,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", @@ -481,7 +481,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -503,9 +503,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "linear-map" @@ -519,9 +519,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" @@ -872,9 +872,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" @@ -908,22 +908,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -939,9 +939,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -1029,7 +1029,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -1044,9 +1044,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.63" +version = "2.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f" dependencies = [ "proc-macro2", "quote", @@ -1082,7 +1082,7 @@ checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -1097,9 +1097,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.12" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" dependencies = [ "serde", "serde_spanned", @@ -1109,18 +1109,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.12" +version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" dependencies = [ "indexmap", "serde", @@ -1148,7 +1148,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] [[package]] @@ -1413,5 +1413,5 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.64", ] From a8202c231f333df2e999253e9eae2a334b33b7ed Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 May 2024 12:37:04 -0700 Subject: [PATCH 100/225] Automatic toolchain upgrade to nightly-2024-05-17 (#3191) Update Rust toolchain from nightly-2024-05-16 to nightly-2024-05-17 without any other source changes. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 458b35d6877b..78849f9db7f3 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-05-16" +channel = "nightly-2024-05-17" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From 14fc7b79acf306a3ce83259765080864057a93a2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 10:36:43 -0700 Subject: [PATCH 101/225] Automatic cargo update to 2024-05-20 (#3195) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e4faadc00d33..570118bbc17f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,9 +74,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "autocfg" @@ -169,7 +169,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -262,9 +262,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crossterm" @@ -333,9 +333,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -481,7 +481,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -503,9 +503,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "linear-map" @@ -732,9 +732,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" dependencies = [ "unicode-ident", ] @@ -923,7 +923,7 @@ checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -1029,7 +1029,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -1044,9 +1044,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.64" +version = "2.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" dependencies = [ "proc-macro2", "quote", @@ -1067,22 +1067,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -1148,7 +1148,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -1413,5 +1413,5 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] From 1ba6cb23a4a373c60ec5fed78b97d3b5d387f523 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 12:16:08 -0700 Subject: [PATCH 102/225] Bump tests/perf/s2n-quic from `bd37960` to `f5d9d74` (#3196) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `bd37960` to `f5d9d74`.
Commits
  • f5d9d74 build(deps): bump JamesIves/github-pages-deploy-action (#2183)
  • 02a47b7 feat(s2n-quic): unstable dc provider (#2210)
  • 919ae6a feat(s2n-quic-tls, s2n-quic-rustls): pass fips flag to tls backend (#2209)
  • 30ef24c feat(s2n-quic-core): add buffer::Deque implemention (#2207)
  • 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> Co-authored-by: Zyad Hassan <88045115+zhassan-aws@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 bd37960965d5..f5d9d747805d 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit bd37960965d5bc8ab9990dc400c886a7a83fb57e +Subproject commit f5d9d747805de846181536ecc2c0a6754a98e9ea From 5dc5e0bf25572d93917c43d00e94a022b3557b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Delmas?= Date: Wed, 22 May 2024 15:59:58 -0400 Subject: [PATCH 103/225] New section about linter configuraton checking in the doc. (#3198) Resolves #3197 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 --- docs/src/usage.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/src/usage.md b/docs/src/usage.md index 77def63d3651..fced7a047804 100644 --- a/docs/src/usage.md +++ b/docs/src/usage.md @@ -68,6 +68,16 @@ default-unwind = 1 The options here are the same as on the command line (`cargo kani --help`), and flags (that is, command line arguments that don't take a value) are enabled by setting them to `true`. +Starting with Rust 1.80 (or nightly-2024-05-05), every reachable #[cfg] will be automatically checked that they match the expected config names and values. +To avoid warnings on `cfg(kani)`, we recommend adding the `check-cfg` lint config in your crate's `Cargo.toml` as follows: + +```toml +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } +``` + +For more information please consult this [blog post](https://blog.rust-lang.org/2024/05/06/check-cfg.html). + ## The build process When Kani builds your code, it does two important things: From 299a1fbe84c1abe775c6ca3a8f4010782e654279 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 06:49:48 -0700 Subject: [PATCH 104/225] Automatic cargo update to 2024-05-27 (#3201) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 570118bbc17f..a1ef1f8d8c05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,7 +169,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -481,7 +481,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -667,9 +667,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -732,9 +732,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.83" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" dependencies = [ "unicode-ident", ] @@ -908,22 +908,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1029,7 +1029,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1044,9 +1044,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.65" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -1082,7 +1082,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1148,7 +1148,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1413,5 +1413,5 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] From f6ab6bfcb721288a6bb17cb20b1317fd922418d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 12:31:34 -0700 Subject: [PATCH 105/225] Bump tests/perf/s2n-quic from `f5d9d74` to `d03cc47` (#3202) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `f5d9d74` to `d03cc47`.
Commits
  • d03cc47 docs(s2n-quic): fips mode usage with rustls TLS provider (#2224)
  • 275dbfd chore(s2n-quic-transport): update dc manager dot snapshot (#2223)
  • 41ebf0c test(s2n-quic-core): add microwave state example (#2222)
  • 391068e feat(s2n-quic-transport): dc Manager (#2218)
  • c96e57c feat(s2n-quic-core): generate state reactions to events (#2221)
  • f23d87b feat(s2n-quic-core): allow for branching states on common events (#2219)
  • 5cac7ea build(deps): bump JamesIves/github-pages-deploy-action (#2215)
  • 0f17963 --- (#2216)
  • 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 f5d9d747805d..d03cc470fa98 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit f5d9d747805de846181536ecc2c0a6754a98e9ea +Subproject commit d03cc470fa9812d06d204e312e4ada00079e96df From f10e61c232c9f76d920c5aaab822014ecc97685f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Delmas?= Date: Tue, 28 May 2024 11:11:03 -0400 Subject: [PATCH 106/225] Update Rust toolchain from nightly-2024-05-17 to nightly-2024-05-23 (#3199) - Linter fixes in documentation, - Added the `lld` package for ubuntu builds, - Propagated a `TyCtx` API change. 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/expr.rs | 3 ++- cprover_bindings/src/irep/goto_binary_serde.rs | 8 +++----- .../src/codegen_cprover_gotoc/codegen/function.rs | 2 +- .../src/codegen_cprover_gotoc/codegen/intrinsic.rs | 11 +++++++---- .../src/codegen_cprover_gotoc/codegen/operand.rs | 2 +- .../src/codegen_cprover_gotoc/codegen/place.rs | 5 +++-- .../src/codegen_cprover_gotoc/codegen/typ.rs | 4 ++-- .../src/codegen_cprover_gotoc/context/goto_ctx.rs | 1 + kani-compiler/src/kani_compiler.rs | 7 ++++--- kani-compiler/src/kani_middle/reachability.rs | 5 +++-- kani-driver/src/args_toml.rs | 5 ++++- kani-driver/src/call_cargo.rs | 13 ++++++++----- kani-driver/src/cbmc_property_renderer.rs | 4 +++- rust-toolchain.toml | 2 +- scripts/setup/ubuntu/install_deps.sh | 1 + .../build-rs-conditional/Cargo.toml | 2 ++ .../cargo_playback_build/sample_crate/Cargo.toml | 3 +++ .../cargo_playback_opts/sample_crate/Cargo.toml | 3 +++ .../cargo_playback_target/sample_crate/Cargo.toml | 2 ++ .../concrete_playback_e2e/sample_crate/Cargo.toml | 3 +++ tools/compiletest/src/main.rs | 4 ++-- 21 files changed, 59 insertions(+), 31 deletions(-) diff --git a/cprover_bindings/src/goto_program/expr.rs b/cprover_bindings/src/goto_program/expr.rs index 905e47bc6034..c76b58023784 100644 --- a/cprover_bindings/src/goto_program/expr.rs +++ b/cprover_bindings/src/goto_program/expr.rs @@ -1055,6 +1055,7 @@ impl Expr { /// ). /// The signedness doesn't matter, as the result for each element is /// either "all ones" (true) or "all zeros" (false). + /// /// For example, one can use `simd_eq` on two `f64x4` vectors and assign the /// result to a `u64x4` vector. But it's not possible to assign it to: (1) a /// `u64x2` because they don't have the same length; or (2) another `f64x4` @@ -1665,7 +1666,7 @@ impl Expr { continue; } let name = field.name(); - exprs.insert(name, self.clone().member(&name.to_string(), symbol_table)); + exprs.insert(name, self.clone().member(name.to_string(), symbol_table)); } } } diff --git a/cprover_bindings/src/irep/goto_binary_serde.rs b/cprover_bindings/src/irep/goto_binary_serde.rs index 4eb1a0720f22..324c5e764d1a 100644 --- a/cprover_bindings/src/irep/goto_binary_serde.rs +++ b/cprover_bindings/src/irep/goto_binary_serde.rs @@ -78,11 +78,9 @@ pub fn read_goto_binary_file(filename: &Path) -> io::Result<()> { /// [NumberedIrep] from its unique number. /// /// In practice: -/// - the forward directon from [IrepKey] to unique numbers is -/// implemented using a `HashMap` -/// - the inverse direction from unique numbers to [NumberedIrep] is implemented -/// using a `Vec` called the `index` that stores [NumberedIrep] -/// under their unique number. +/// - the forward directon from [IrepKey] to unique numbers is implemented using a `HashMap` +/// - the inverse direction from unique numbers to [NumberedIrep] is implemented usign a `Vec` +/// called the `index` that stores [NumberedIrep] under their unique number. /// /// Earlier we said that an [NumberedIrep] is conceptually a pair formed of /// an [IrepKey] and its unique number. It is represented using only diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs index d55696bfdc87..33ec70294d04 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs @@ -204,7 +204,7 @@ impl<'tcx> GotocCtx<'tcx> { 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(&self.symbol_name_stable(instance), |ctx, fname| { + self.ensure(self.symbol_name_stable(instance), |ctx, fname| { Symbol::function( fname, ctx.fn_typ(instance, &body), diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 3a4e84a70d98..4e965b4105ef 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -871,6 +871,7 @@ impl<'tcx> GotocCtx<'tcx> { /// its primary argument and returns a tuple that contains: /// * the previous value /// * a boolean value indicating whether the operation was successful or not + /// /// In a sequential context, the update is always sucessful so we assume the /// second value to be true. /// ------------------------- @@ -955,9 +956,10 @@ impl<'tcx> GotocCtx<'tcx> { /// * Both `src`/`dst` must be valid for reads/writes of `count * /// size_of::()` bytes (done by calls to `memmove`) /// * (Exclusive to nonoverlapping copy) The region of memory beginning - /// at `src` with a size of `count * size_of::()` bytes must *not* - /// overlap with the region of memory beginning at `dst` with the same - /// size. + /// at `src` with a size of `count * size_of::()` bytes must *not* + /// overlap with the region of memory beginning at `dst` with the same + /// size. + /// /// In addition, we check that computing `count` in bytes (i.e., the third /// argument of the copy built-in call) would not overflow. pub fn codegen_copy( @@ -1834,7 +1836,7 @@ impl<'tcx> GotocCtx<'tcx> { /// /// TODO: Add a check for the condition: /// * `src` must point to a properly initialized value of type `T` - /// See for more details + /// See for more details fn codegen_volatile_load( &mut self, mut fargs: Vec, @@ -1894,6 +1896,7 @@ impl<'tcx> GotocCtx<'tcx> { /// Undefined behavior if any of these conditions are violated: /// * `dst` must be valid for writes (done by memset writable check) /// * `dst` must be properly aligned (done by `align_check` below) + /// /// In addition, we check that computing `bytes` (i.e., the third argument /// for the `memset` call) would not overflow fn codegen_write_bytes( diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs index 6c75c6a5ad5a..c8aba08ba5ed 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs @@ -393,7 +393,7 @@ impl<'tcx> GotocCtx<'tcx> { /// Generate a goto expression for a pointer to a static or thread-local variable. fn codegen_instance_pointer(&mut self, instance: Instance, is_thread_local: bool) -> Expr { - let sym = self.ensure(&instance.mangled_name(), |ctx, name| { + let sym = self.ensure(instance.mangled_name(), |ctx, name| { // Rust has a notion of "extern static" variables. These are in an "extern" block, // and so aren't initialized in the current codegen unit. For example (from std): // extern "C" { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs index 56b7da2e7628..771a38ac922d 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs @@ -333,8 +333,9 @@ impl<'tcx> GotocCtx<'tcx> { /// assert!(v.0 == [1, 2]); // refers to the entire array /// } /// ``` - /// * Note that projection inside SIMD structs may eventually become illegal. - /// See thread. + /// + /// Note that projection inside SIMD structs may eventually become illegal. + /// See thread . /// /// Since the goto representation for both is the same, we use the expected type to decide /// what to return. diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index df36189f4606..c091d0b74ead 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -73,7 +73,7 @@ impl TypeExt for Type { && components.iter().any(|x| x.name() == "vtable" && x.typ().is_pointer()) } Type::StructTag(tag) => { - st.lookup(&tag.to_string()).unwrap().typ.is_rust_trait_fat_ptr(st) + st.lookup(tag.to_string()).unwrap().typ.is_rust_trait_fat_ptr(st) } _ => false, } @@ -1140,7 +1140,7 @@ impl<'tcx> GotocCtx<'tcx> { /// Mapping enums to CBMC types is rather complicated. There are a few cases to consider: /// 1. When there is only 0 or 1 variant, this is straightforward as the code shows /// 2. When there are more variants, rust might decides to apply the typical encoding which - /// regard enums as tagged union, or an optimized form, called niche encoding. + /// regard enums as tagged union, or an optimized form, called niche encoding. /// /// The direct encoding is straightforward. Enums are just mapped to C as a struct of union of structs. /// e.g. 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 095f907228a4..9a81838b7fc2 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs @@ -11,6 +11,7 @@ //! This file is for defining the data-structure itself. //! 1. Defines `GotocCtx<'tcx>` //! 2. Provides constructors, getters and setters for the context. +//! //! Any MIR specific functionality (e.g. codegen etc) should live in specialized files that use //! this structure as input. use super::current_fn::CurrentFnCtx; diff --git a/kani-compiler/src/kani_compiler.rs b/kani-compiler/src/kani_compiler.rs index fc5f5891ecae..1793165bdf81 100644 --- a/kani-compiler/src/kani_compiler.rs +++ b/kani-compiler/src/kani_compiler.rs @@ -110,9 +110,10 @@ struct CrateInfo { /// compilation. The crate metadata is stored here (even if no codegen was actually performed). /// - [CompilationStage::CompilationSkipped] no compilation was actually performed. /// No work needs to be done. -/// - Note: In a scenario where the compilation fails, the compiler will exit immediately, -/// independent on the stage. Any artifact produced shouldn't be used. -/// I.e.: +/// +/// Note: In a scenario where the compilation fails, the compiler will exit immediately, +/// independent on the stage. Any artifact produced shouldn't be used. I.e.: +/// /// ```dot /// graph CompilationStage { /// Init -> {CodegenNoStubs, CompilationSkipped} diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 456d5425b245..71eb4931aad4 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -303,7 +303,7 @@ impl<'a, 'tcx> MonoItemsFnCollector<'a, 'tcx> { /// 1. Every function / method / closures that may be directly invoked. /// 2. Every function / method / closures that may have their address taken. /// 3. Every method that compose the impl of a trait for a given type when there's a conversion -/// from the type to the trait. +/// from the type to the trait. /// - I.e.: If we visit the following code: /// ``` /// let var = MyType::new(); @@ -313,7 +313,8 @@ impl<'a, 'tcx> MonoItemsFnCollector<'a, 'tcx> { /// 4. Every Static variable that is referenced in the function or constant used in the function. /// 5. Drop glue. /// 6. Static Initialization -/// This code has been mostly taken from `rustc_monomorphize::collector::MirNeighborCollector`. +/// +/// Remark: This code has been mostly taken from `rustc_monomorphize::collector::MirNeighborCollector`. impl<'a, 'tcx> MirVisitor for MonoItemsFnCollector<'a, 'tcx> { /// Collect the following: /// - Trait implementations when casting from concrete to dyn Trait. diff --git a/kani-driver/src/args_toml.rs b/kani-driver/src/args_toml.rs index aaa5b3260083..b9e994b38fec 100644 --- a/kani-driver/src/args_toml.rs +++ b/kani-driver/src/args_toml.rs @@ -90,8 +90,11 @@ fn cargo_locate_project(input_args: &[OsString]) -> Result { /// We currently support the following entries: /// - flags: Flags that get directly passed to Kani. /// - unstable: Unstable features (it will be passed using `-Z` flag). +/// /// The tables supported are: -/// "workspace.metadata.kani", "package.metadata.kani", "kani" +/// - "workspace.metadata.kani" +/// - "package.metadata.kani" +/// - "kani" fn toml_to_args(tomldata: &str) -> Result<(Vec, Vec)> { let config = tomldata.parse::()?; // To make testing easier, our function contract is to produce a stable ordering of flags for a given input. diff --git a/kani-driver/src/call_cargo.rs b/kani-driver/src/call_cargo.rs index 9d27e5853696..2d5d36306e21 100644 --- a/kani-driver/src/call_cargo.rs +++ b/kani-driver/src/call_cargo.rs @@ -112,7 +112,7 @@ impl KaniSession { let mut cmd = setup_cargo_command()?; cmd.args(&cargo_args) .args(vec!["-p", &package.name]) - .args(&verification_target.to_args()) + .args(verification_target.to_args()) .args(&pkg_args) .env("RUSTC", &self.kani_compiler) // Use CARGO_ENCODED_RUSTFLAGS instead of RUSTFLAGS is preferred. See @@ -315,13 +315,16 @@ fn validate_package_names(package_names: &[String], packages: &[Package]) -> Res } /// Extract the packages that should be verified. -/// If `--package ` is given, return the list of packages selected. -/// If `--exclude ` is given, return the list of packages not excluded. -/// If `--workspace` is given, return the list of workspace members. -/// If no argument provided, return the root package if there's one or all members. +/// +/// The result is build following these rules: +/// - If `--package ` is given, return the list of packages selected. +/// - If `--exclude ` is given, return the list of packages not excluded. +/// - If `--workspace` is given, return the list of workspace members. +/// - If no argument provided, return the root package if there's one or all members. /// - I.e.: Do whatever cargo does when there's no `default_members`. /// - This is because `default_members` is not available in cargo metadata. /// See . +/// /// In addition, if either `--package ` or `--exclude ` is given, /// validate that `` is a package name in the workspace, or return an error /// otherwise. diff --git a/kani-driver/src/cbmc_property_renderer.rs b/kani-driver/src/cbmc_property_renderer.rs index c233959abd6a..7e4fffe42812 100644 --- a/kani-driver/src/cbmc_property_renderer.rs +++ b/kani-driver/src/cbmc_property_renderer.rs @@ -700,6 +700,7 @@ fn update_properties_with_reach_status( /// Update the results of `code_coverage` (NOT `cover`) properties. /// - `SUCCESS` -> `UNCOVERED` /// - `FAILURE` -> `COVERED` +/// /// Note that these statuses are intermediate statuses that aren't reported to /// users but rather internally consumed and reported finally as `PARTIAL`, `FULL` /// or `NONE` based on aggregated line coverage results. @@ -720,9 +721,10 @@ fn update_results_of_code_covererage_checks(mut properties: Vec) -> Ve /// Update the results of cover properties. /// We encode cover(cond) as assert(!cond), so if the assertion -/// fails, then the cover property is satisfied and vice versa. +/// fails, then the cover property is satisfied and vice versa: /// - SUCCESS -> UNSATISFIABLE /// - FAILURE -> SATISFIED +/// /// Note that if the cover property was unreachable, its status at this point /// will be `CheckStatus::Unreachable` and not `CheckStatus::Success` since /// `update_properties_with_reach_status` is called beforehand diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 78849f9db7f3..8cc564b125c4 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-05-17" +channel = "nightly-2024-05-23" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/scripts/setup/ubuntu/install_deps.sh b/scripts/setup/ubuntu/install_deps.sh index 2830d0280498..b93602691222 100755 --- a/scripts/setup/ubuntu/install_deps.sh +++ b/scripts/setup/ubuntu/install_deps.sh @@ -16,6 +16,7 @@ DEPS=( gpg-agent jq libssl-dev + lld lsb-release make ninja-build diff --git a/tests/script-based-pre/build-rs-conditional/Cargo.toml b/tests/script-based-pre/build-rs-conditional/Cargo.toml index 136bae92250e..ecf681268dfe 100644 --- a/tests/script-based-pre/build-rs-conditional/Cargo.toml +++ b/tests/script-based-pre/build-rs-conditional/Cargo.toml @@ -7,3 +7,5 @@ edition = "2021" [dependencies] +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tests/script-based-pre/cargo_playback_build/sample_crate/Cargo.toml b/tests/script-based-pre/cargo_playback_build/sample_crate/Cargo.toml index c00d0ccc4bdb..99dfd67443f5 100644 --- a/tests/script-based-pre/cargo_playback_build/sample_crate/Cargo.toml +++ b/tests/script-based-pre/cargo_playback_build/sample_crate/Cargo.toml @@ -4,3 +4,6 @@ name = "sample_crate" version = "0.1.0" edition = "2021" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tests/script-based-pre/cargo_playback_opts/sample_crate/Cargo.toml b/tests/script-based-pre/cargo_playback_opts/sample_crate/Cargo.toml index c00d0ccc4bdb..99dfd67443f5 100644 --- a/tests/script-based-pre/cargo_playback_opts/sample_crate/Cargo.toml +++ b/tests/script-based-pre/cargo_playback_opts/sample_crate/Cargo.toml @@ -4,3 +4,6 @@ name = "sample_crate" version = "0.1.0" edition = "2021" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tests/script-based-pre/cargo_playback_target/sample_crate/Cargo.toml b/tests/script-based-pre/cargo_playback_target/sample_crate/Cargo.toml index 30949391c0c7..5ece6c5e0f60 100644 --- a/tests/script-based-pre/cargo_playback_target/sample_crate/Cargo.toml +++ b/tests/script-based-pre/cargo_playback_target/sample_crate/Cargo.toml @@ -15,3 +15,5 @@ doctest = false name = "bar" doctest = false +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tests/script-based-pre/concrete_playback_e2e/sample_crate/Cargo.toml b/tests/script-based-pre/concrete_playback_e2e/sample_crate/Cargo.toml index 4a7016cb51ed..39cfab289878 100644 --- a/tests/script-based-pre/concrete_playback_e2e/sample_crate/Cargo.toml +++ b/tests/script-based-pre/concrete_playback_e2e/sample_crate/Cargo.toml @@ -10,3 +10,6 @@ concrete-playback = "inplace" [package.metadata.kani.unstable] concrete-playback = true + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } diff --git a/tools/compiletest/src/main.rs b/tools/compiletest/src/main.rs index 5418ad47f7fa..94cbd561aa51 100644 --- a/tools/compiletest/src/main.rs +++ b/tools/compiletest/src/main.rs @@ -388,7 +388,7 @@ fn collect_expected_tests_from_dir( && (file_path.to_str().unwrap().ends_with(".expected") || "expected" == file_path.file_name().unwrap()) { - fs::create_dir_all(&build_dir.join(file_path.file_stem().unwrap())).unwrap(); + fs::create_dir_all(build_dir.join(file_path.file_stem().unwrap())).unwrap(); let paths = TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() }; tests.push(make_test(config, &paths, inputs)); @@ -446,7 +446,7 @@ fn collect_exec_tests_from_dir( } // Create directory for test and add it to the tests to be run - fs::create_dir_all(&build_dir.join(file_path.file_stem().unwrap())).unwrap(); + fs::create_dir_all(build_dir.join(file_path.file_stem().unwrap())).unwrap(); let paths = TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() }; tests.push(make_test(config, &paths, inputs)); } From 7eb6ce72829c86f21d216a13918575195b91264b Mon Sep 17 00:00:00 2001 From: GrigorenkoPV Date: Wed, 29 May 2024 19:23:01 +0300 Subject: [PATCH 107/225] Fix `{,e}println!()` (#3209) GitHub closed the previous PR (#3205) after I renamed a branch, hooray. Change the implementation of `println!` & `eprintln!` with no arguments to call `print!("\n")` & `eprint!("\n")` respectively instead of producing no tokens. This is what std does. [^println][^eprintln] [^println]: https://github.com/rust-lang/rust/blob/8c127df75fde3d5ad8ef9af664962a7676288b52/library/std/src/macros.rs#L140 [^eprintln]: https://github.com/rust-lang/rust/blob/8c127df75fde3d5ad8ef9af664962a7676288b52/library/std/src/macros.rs#L218 Resolves #3204. A test included, as per https://github.com/model-checking/kani/pull/3205#issuecomment-2135702685 --- library/std/src/lib.rs | 4 ++-- tests/kani/Print/no_semicolon.rs | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 tests/kani/Print/no_semicolon.rs diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 37e0e5a21518..3f8427ffff13 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -135,14 +135,14 @@ macro_rules! eprint { #[cfg(not(feature = "concrete_playback"))] #[macro_export] macro_rules! println { - () => { }; + () => { $crate::print!("\n") }; ($($x:tt)*) => {{ let _ = format_args!($($x)*); }}; } #[cfg(not(feature = "concrete_playback"))] #[macro_export] macro_rules! eprintln { - () => { }; + () => { $crate::eprint!("\n") }; ($($x:tt)*) => {{ let _ = format_args!($($x)*); }}; } diff --git a/tests/kani/Print/no_semicolon.rs b/tests/kani/Print/no_semicolon.rs new file mode 100644 index 000000000000..bee09e628472 --- /dev/null +++ b/tests/kani/Print/no_semicolon.rs @@ -0,0 +1,17 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// This test checks that the (e)println with no arguments do not require a trailing semicolon + +fn println() { + println!() +} +fn eprintln() { + eprintln!() +} + +#[kani::proof] +fn main() { + println(); + eprintln(); +} From 8a2b7e5b33cb80023075711dea0725e51334b874 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 29 May 2024 10:16:36 -0700 Subject: [PATCH 108/225] Contracts for a few core functions (#3107) Adds a new regression test suite that includes safety contracts for `core` functions. For that, we've also improved the memory intrinsics, and added a few new ones. One last change was to add a new unsupported check to the invalid value workflow for a case that we were missing. --- Cargo.toml | 1 + .../codegen_cprover_gotoc/overrides/hooks.rs | 10 +- .../src/kani_middle/transform/body.rs | 15 ++ .../src/kani_middle/transform/check_values.rs | 172 ++++++++-------- .../kani_middle/transform/kani_intrinsics.rs | 138 +++++++++++++ .../src/kani_middle/transform/mod.rs | 7 +- library/kani/src/mem.rs | 186 +++++++++++++++--- scripts/kani-regression.sh | 3 +- .../function-contract/valid_ptr.expected | 6 +- tests/expected/function-contract/valid_ptr.rs | 8 +- tests/kani/MemPredicates/fat_ptr_validity.rs | 22 +-- tests/kani/MemPredicates/thin_ptr_validity.rs | 16 +- tests/kani/ValidValues/custom_niche.rs | 21 +- tests/std-checks/core/Cargo.toml | 13 ++ tests/std-checks/core/mem.expected | 3 + tests/std-checks/core/ptr.expected | 3 + tests/std-checks/core/src/lib.rs | 9 + tests/std-checks/core/src/mem.rs | 75 +++++++ tests/std-checks/core/src/ptr.rs | 112 +++++++++++ 19 files changed, 667 insertions(+), 153 deletions(-) create mode 100644 kani-compiler/src/kani_middle/transform/kani_intrinsics.rs create mode 100644 tests/std-checks/core/Cargo.toml create mode 100644 tests/std-checks/core/mem.expected create mode 100644 tests/std-checks/core/ptr.expected create mode 100644 tests/std-checks/core/src/lib.rs create mode 100644 tests/std-checks/core/src/mem.rs create mode 100644 tests/std-checks/core/src/ptr.rs diff --git a/Cargo.toml b/Cargo.toml index 7b7d5cbe60a2..24812a157ae5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,7 @@ exclude = [ "tests/perf", "tests/cargo-ui", "tests/slow", + "tests/std-checks", "tests/assess-scan-test-scaffold", "tests/script-based-pre", "tests/script-based-pre/build-cache-bin/target/new_dep", diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index d5f10adeec32..9f70c16430e5 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -207,11 +207,11 @@ impl GotocHook for Panic { } } -/// Encodes __CPROVER_r_ok -struct IsReadOk; -impl GotocHook for IsReadOk { +/// Encodes __CPROVER_r_ok(ptr, size) +struct IsAllocated; +impl GotocHook for IsAllocated { fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { - matches_function(tcx, instance.def, "KaniIsReadOk") + matches_function(tcx, instance.def, "KaniIsAllocated") } fn handle( @@ -398,7 +398,7 @@ pub fn fn_hooks() -> GotocHooks { Rc::new(Assert), Rc::new(Cover), Rc::new(Nondet), - Rc::new(IsReadOk), + Rc::new(IsAllocated), Rc::new(RustAlloc), Rc::new(MemCmp), Rc::new(UntrackedDeref), diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index d14aca3a7c5b..9402835c2ff2 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -12,6 +12,7 @@ use stable_mir::mir::{ VarDebugInfo, }; use stable_mir::ty::{Const, GenericArgs, Span, Ty, UintTy}; +use std::fmt::Debug; use std::mem; /// This structure mimics a Body that can actually be modified. @@ -224,6 +225,20 @@ impl MutableBody { } } } + + /// Clear all the existing logic of this body and turn it into a simple `return`. + /// + /// This function can be used when a new implementation of the body is needed. + /// For example, Kani intrinsics usually have a dummy body, which is replaced + /// by the compiler. This function allow us to delete the dummy body before + /// creating a new one. + /// + /// Note: We do not prune the local variables today for simplicity. + pub fn clear_body(&mut self) { + self.blocks.clear(); + let terminator = Terminator { kind: TerminatorKind::Return, span: self.span }; + self.blocks.push(BasicBlock { statements: Vec::default(), terminator }) + } } #[derive(Clone, Debug)] diff --git a/kani-compiler/src/kani_middle/transform/check_values.rs b/kani-compiler/src/kani_middle/transform/check_values.rs index 4697c139b294..2c05c07be7f2 100644 --- a/kani-compiler/src/kani_middle/transform/check_values.rs +++ b/kani-compiler/src/kani_middle/transform/check_values.rs @@ -19,7 +19,6 @@ use crate::kani_middle::transform::check_values::SourceOp::UnsupportedCheck; use crate::kani_middle::transform::{TransformPass, TransformationType}; use crate::kani_queries::QueryDb; use rustc_middle::ty::TyCtxt; -use rustc_smir::rustc_internal; use stable_mir::abi::{FieldsShape, Scalar, TagEncoding, ValueAbi, VariantsShape, WrappingRange}; use stable_mir::mir::mono::{Instance, InstanceKind}; use stable_mir::mir::visit::{Location, PlaceContext, PlaceRef}; @@ -31,19 +30,14 @@ use stable_mir::mir::{ use stable_mir::target::{MachineInfo, MachineSize}; use stable_mir::ty::{AdtKind, Const, IndexedVal, RigidTy, Ty, TyKind, UintTy}; use stable_mir::CrateDef; -use std::fmt::{Debug, Formatter}; +use std::fmt::Debug; use strum_macros::AsRefStr; use tracing::{debug, trace}; /// Instrument the code with checks for invalid values. +#[derive(Debug)] pub struct ValidValuePass { - check_type: CheckType, -} - -impl ValidValuePass { - pub fn new(tcx: TyCtxt) -> Self { - ValidValuePass { check_type: CheckType::new(tcx) } - } + pub check_type: CheckType, } impl TransformPass for ValidValuePass { @@ -81,13 +75,6 @@ impl TransformPass for ValidValuePass { } } -impl Debug for ValidValuePass { - /// Implement manually since MachineInfo doesn't currently derive Debug. - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - "ValidValuePass".fmt(f) - } -} - impl ValidValuePass { fn build_check(&self, tcx: TyCtxt, body: &mut MutableBody, instruction: UnsafeInstruction) { debug!(?instruction, "build_check"); @@ -98,31 +85,23 @@ impl ValidValuePass { let value = body.new_assignment(rvalue, &mut source); let rvalue_ptr = Rvalue::AddressOf(Mutability::Not, Place::from(value)); for range in ranges { - let result = - self.build_limits(body, &range, rvalue_ptr.clone(), &mut source); - let msg = format!( - "Undefined Behavior: Invalid value of type `{}`", - // TODO: Fix pretty_ty - rustc_internal::internal(tcx, target_ty) - ); + let result = build_limits(body, &range, rvalue_ptr.clone(), &mut source); + let msg = + format!("Undefined Behavior: Invalid value of type `{target_ty}`",); body.add_check(tcx, &self.check_type, &mut source, result, &msg); } } SourceOp::DerefValidity { pointee_ty, rvalue, ranges } => { for range in ranges { - let result = self.build_limits(body, &range, rvalue.clone(), &mut source); - let msg = format!( - "Undefined Behavior: Invalid value of type `{}`", - // TODO: Fix pretty_ty - rustc_internal::internal(tcx, pointee_ty) - ); + let result = build_limits(body, &range, rvalue.clone(), &mut source); + let msg = + format!("Undefined Behavior: Invalid value of type `{pointee_ty}`",); body.add_check(tcx, &self.check_type, &mut source, result, &msg); } } SourceOp::UnsupportedCheck { check, ty } => { let reason = format!( - "Kani currently doesn't support checking validity of `{check}` for `{}` type", - rustc_internal::internal(tcx, ty) + "Kani currently doesn't support checking validity of `{check}` for `{ty}`", ); self.unsupported_check(tcx, body, &mut source, &reason); } @@ -130,57 +109,6 @@ impl ValidValuePass { } } - fn build_limits( - &self, - body: &mut MutableBody, - req: &ValidValueReq, - rvalue_ptr: Rvalue, - source: &mut SourceInstruction, - ) -> Local { - let span = source.span(body.blocks()); - debug!(?req, ?rvalue_ptr, ?span, "build_limits"); - let primitive_ty = uint_ty(req.size.bytes()); - let start_const = body.new_const_operand(req.valid_range.start, primitive_ty, span); - let end_const = body.new_const_operand(req.valid_range.end, primitive_ty, span); - let orig_ptr = if req.offset != 0 { - let start_ptr = move_local(body.new_assignment(rvalue_ptr, source)); - let byte_ptr = move_local(body.new_cast_ptr( - start_ptr, - Ty::unsigned_ty(UintTy::U8), - Mutability::Not, - source, - )); - let offset_const = body.new_const_operand(req.offset as _, UintTy::Usize, span); - let offset = move_local(body.new_assignment(Rvalue::Use(offset_const), source)); - move_local(body.new_binary_op(BinOp::Offset, byte_ptr, offset, source)) - } else { - move_local(body.new_assignment(rvalue_ptr, source)) - }; - let value_ptr = - body.new_cast_ptr(orig_ptr, Ty::unsigned_ty(primitive_ty), Mutability::Not, source); - let value = - Operand::Copy(Place { local: value_ptr, projection: vec![ProjectionElem::Deref] }); - let start_result = body.new_binary_op(BinOp::Ge, value.clone(), start_const, source); - let end_result = body.new_binary_op(BinOp::Le, value, end_const, source); - if req.valid_range.wraps_around() { - // valid >= start || valid <= end - body.new_binary_op( - BinOp::BitOr, - move_local(start_result), - move_local(end_result), - source, - ) - } else { - // valid >= start && valid <= end - body.new_binary_op( - BinOp::BitAnd, - move_local(start_result), - move_local(end_result), - source, - ) - } - } - fn unsupported_check( &self, tcx: TyCtxt, @@ -216,7 +144,7 @@ fn uint_ty(bytes: usize) -> UintTy { /// Represent a requirement for the value stored in the given offset. #[derive(Clone, Debug, Eq, PartialEq, Hash)] -struct ValidValueReq { +pub struct ValidValueReq { /// Offset in bytes. offset: usize, /// Size of this requirement. @@ -384,9 +312,13 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> { } else if self.target.is_none() { // Leave it as an exhaustive match to be notified when a new kind is added. match &stmt.kind { - StatementKind::Intrinsic(NonDivergingIntrinsic::CopyNonOverlapping(_)) => { - // Source and destination have the same type, so no invalid value cannot be - // generated. + StatementKind::Intrinsic(NonDivergingIntrinsic::CopyNonOverlapping(copy)) => { + // Source is a *const T and it must be safe for read. + // TODO: Implement value check. + self.push_target(SourceOp::UnsupportedCheck { + check: "copy_nonoverlapping".to_string(), + ty: copy.src.ty(self.locals).unwrap(), + }); } StatementKind::Assign(place, rvalue) => { // First check rvalue. @@ -573,11 +505,27 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> { // We only care about *mut T as *mut U return; }; + if dest_pointee_ty.kind().is_unit() { + // Ignore cast to *mut () since nothing can be written to it. + // This is a common pattern + return; + } + let src_ty = op.ty(self.locals).unwrap(); debug!(?src_ty, ?dest_ty, "visit_rvalue mutcast"); let TyKind::RigidTy(RigidTy::RawPtr(src_pointee_ty, _)) = src_ty.kind() else { unreachable!() }; + + if src_pointee_ty.kind().is_unit() { + // We cannot track what was the initial type. Thus, fail. + self.push_target(SourceOp::UnsupportedCheck { + check: "mutable cast".to_string(), + ty: src_ty, + }); + return; + } + if let Ok(src_validity) = ty_validity_per_offset(&self.machine, src_pointee_ty, 0) { @@ -626,6 +574,8 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> { }) } } + // `DynStar` is not currently supported in Kani. + // TODO: https://github.com/model-checking/kani/issues/1784 CastKind::DynStar => self.push_target(UnsupportedCheck { check: "Dyn*".to_string(), ty: (rvalue.ty(self.locals).unwrap()), @@ -784,10 +734,58 @@ fn expect_instance(locals: &[LocalDecl], func: &Operand) -> Instance { } } +/// Instrument MIR to check the value pointed by `rvalue_ptr` satisfies requirement `req`. +/// +/// The MIR will do something equivalent to: +/// ```rust +/// let ptr = rvalue_ptr.byte_offset(req.offset); +/// let typed_ptr = ptr as *const Unsigned; // Some unsigned type with length req.size +/// let value = unsafe { *typed_ptr }; +/// req.valid_range.contains(value) +/// ``` +pub fn build_limits( + body: &mut MutableBody, + req: &ValidValueReq, + rvalue_ptr: Rvalue, + source: &mut SourceInstruction, +) -> Local { + let span = source.span(body.blocks()); + debug!(?req, ?rvalue_ptr, ?span, "build_limits"); + let primitive_ty = uint_ty(req.size.bytes()); + let start_const = body.new_const_operand(req.valid_range.start, primitive_ty, span); + let end_const = body.new_const_operand(req.valid_range.end, primitive_ty, span); + let orig_ptr = if req.offset != 0 { + let start_ptr = move_local(body.new_assignment(rvalue_ptr, source)); + let byte_ptr = move_local(body.new_cast_ptr( + start_ptr, + Ty::unsigned_ty(UintTy::U8), + Mutability::Not, + source, + )); + let offset_const = body.new_const_operand(req.offset as _, UintTy::Usize, span); + let offset = move_local(body.new_assignment(Rvalue::Use(offset_const), source)); + move_local(body.new_binary_op(BinOp::Offset, byte_ptr, offset, source)) + } else { + move_local(body.new_assignment(rvalue_ptr, source)) + }; + let value_ptr = + body.new_cast_ptr(orig_ptr, Ty::unsigned_ty(primitive_ty), Mutability::Not, source); + let value = Operand::Copy(Place { local: value_ptr, projection: vec![ProjectionElem::Deref] }); + let start_result = body.new_binary_op(BinOp::Ge, value.clone(), start_const, source); + let end_result = body.new_binary_op(BinOp::Le, value, end_const, source); + if req.valid_range.wraps_around() { + // valid >= start || valid <= end + body.new_binary_op(BinOp::BitOr, move_local(start_result), move_local(end_result), source) + } else { + // valid >= start && valid <= end + body.new_binary_op(BinOp::BitAnd, move_local(start_result), move_local(end_result), source) + } +} + /// Traverse the type and find all invalid values and their location in memory. /// /// Not all values are currently supported. For those not supported, we return Error. -fn ty_validity_per_offset( +pub fn ty_validity_per_offset( machine_info: &MachineInfo, ty: Ty, current_offset: usize, diff --git a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs new file mode 100644 index 000000000000..6354e3777aca --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs @@ -0,0 +1,138 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +//! Module responsible for generating code for a few Kani intrinsics. +//! +//! These intrinsics have code that depend on information from the compiler, such as type layout +//! information; thus, they are implemented as a transformation pass where their body get generated +//! by the transformation. + +use crate::kani_middle::attributes::matches_diagnostic; +use crate::kani_middle::transform::body::{CheckType, MutableBody, SourceInstruction}; +use crate::kani_middle::transform::check_values::{build_limits, ty_validity_per_offset}; +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::{ + BinOp, Body, Constant, Operand, Place, Rvalue, Statement, StatementKind, RETURN_LOCAL, +}; +use stable_mir::target::MachineInfo; +use stable_mir::ty::{Const, RigidTy, TyKind}; +use std::fmt::Debug; +use strum_macros::AsRefStr; +use tracing::trace; + +/// Generate the body for a few Kani intrinsics. +#[derive(Debug)] +pub struct IntrinsicGeneratorPass { + pub check_type: CheckType, +} + +impl TransformPass for IntrinsicGeneratorPass { + fn transformation_type() -> TransformationType + where + Self: Sized, + { + TransformationType::Instrumentation + } + + fn is_enabled(&self, _query_db: &QueryDb) -> bool + where + Self: Sized, + { + true + } + + /// Transform the function body by inserting checks one-by-one. + /// For every unsafe dereference or a transmute operation, we check all values are valid. + fn transform(&self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + trace!(function=?instance.name(), "transform"); + if matches_diagnostic(tcx, instance.def, Intrinsics::KaniValidValue.as_ref()) { + (true, self.valid_value_body(tcx, body)) + } else { + (false, body) + } + } +} + +impl IntrinsicGeneratorPass { + /// Generate the body for valid value. Which should be something like: + /// + /// ``` + /// pub fn has_valid_value(ptr: *const T) -> bool { + /// let mut ret = true; + /// let bytes = ptr as *const u8; + /// for req in requirements { + /// ret &= in_range(bytes, req); + /// } + /// ret + /// } + /// ``` + fn valid_value_body(&self, tcx: TyCtxt, body: Body) -> Body { + let mut new_body = MutableBody::from(body); + new_body.clear_body(); + + // Initialize return variable with True. + let ret_var = RETURN_LOCAL; + let mut terminator = SourceInstruction::Terminator { bb: 0 }; + let span = new_body.locals()[ret_var].span; + let assign = StatementKind::Assign( + Place::from(ret_var), + Rvalue::Use(Operand::Constant(Constant { + span, + user_ty: None, + literal: Const::from_bool(true), + })), + ); + let stmt = Statement { kind: assign, span }; + new_body.insert_stmt(stmt, &mut terminator); + let machine_info = MachineInfo::target(); + + // The first and only argument type. + let arg_ty = new_body.locals()[1].ty; + let TyKind::RigidTy(RigidTy::RawPtr(target_ty, _)) = arg_ty.kind() else { unreachable!() }; + let validity = ty_validity_per_offset(&machine_info, target_ty, 0); + match validity { + Ok(ranges) if ranges.is_empty() => { + // Nothing to check + } + Ok(ranges) => { + // Given the pointer argument, check for possible invalid ranges. + let rvalue = Rvalue::Use(Operand::Move(Place::from(1))); + for range in ranges { + let result = + build_limits(&mut new_body, &range, rvalue.clone(), &mut terminator); + let rvalue = Rvalue::BinaryOp( + BinOp::BitAnd, + Operand::Move(Place::from(ret_var)), + Operand::Move(Place::from(result)), + ); + let assign = StatementKind::Assign(Place::from(ret_var), rvalue); + let stmt = Statement { kind: assign, span }; + new_body.insert_stmt(stmt, &mut terminator); + } + } + Err(msg) => { + // We failed to retrieve all the valid ranges. + let rvalue = Rvalue::Use(Operand::Constant(Constant { + literal: Const::from_bool(false), + span, + user_ty: None, + })); + let result = new_body.new_assignment(rvalue, &mut terminator); + let reason = format!( + "Kani currently doesn't support checking validity of `{target_ty}`. {msg}" + ); + new_body.add_check(tcx, &self.check_type, &mut terminator, result, &reason); + } + } + new_body.into() + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, AsRefStr)] +#[strum(serialize_all = "PascalCase")] +enum Intrinsics { + KaniValidValue, +} diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index a6cd17e8c7db..d4e06a785fb3 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -16,7 +16,9 @@ //! //! For all instrumentation passes, always use exhaustive matches to ensure soundness in case a new //! case is added. +use crate::kani_middle::transform::body::CheckType; use crate::kani_middle::transform::check_values::ValidValuePass; +use crate::kani_middle::transform::kani_intrinsics::IntrinsicGeneratorPass; use crate::kani_queries::QueryDb; use rustc_middle::ty::TyCtxt; use stable_mir::mir::mono::Instance; @@ -26,6 +28,7 @@ use std::fmt::Debug; mod body; mod check_values; +mod kani_intrinsics; /// Object used to retrieve a transformed instance body. /// The transformations to be applied may be controlled by user options. @@ -50,7 +53,9 @@ impl BodyTransformation { inst_passes: vec![], cache: Default::default(), }; - transformer.add_pass(queries, ValidValuePass::new(tcx)); + let check_type = CheckType::new(tcx); + transformer.add_pass(queries, ValidValuePass { check_type: check_type.clone() }); + transformer.add_pass(queries, IntrinsicGeneratorPass { check_type }); transformer } diff --git a/library/kani/src/mem.rs b/library/kani/src/mem.rs index b857247021ec..c485ee16a562 100644 --- a/library/kani/src/mem.rs +++ b/library/kani/src/mem.rs @@ -10,8 +10,10 @@ //! 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. +//! ~~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 @@ -39,39 +41,137 @@ use crate::mem::private::Internal; use std::mem::{align_of, size_of}; use std::ptr::{DynMetadata, NonNull, Pointee}; -/// Assert that the pointer is valid for access according to [crate::mem] conditions 1, 2 and 3. +/// Check if the pointer is valid for write access according to [crate::mem] conditions 1, 2 +/// and 3. /// -/// Note that an unaligned pointer is still considered valid. +/// Note this function also checks for pointer alignment. Use [self::can_write_unaligned] +/// if you don't want to fail for unaligned pointers. +/// +/// This function does not check if the value stored is valid for the given type. Use +/// [self::can_dereference] for that. +/// +/// This function will panic today if the pointer is not null, and it points to an unallocated or +/// deallocated memory location. This is an existing Kani limitation. +/// See for more details. +#[crate::unstable( + feature = "mem-predicates", + issue = 2690, + reason = "experimental memory predicate API" +)] +pub fn can_write(ptr: *mut T) -> bool +where + T: ?Sized, + ::Metadata: PtrProperties, +{ + // The interface takes a mutable pointer to improve readability of the signature. + // However, using constant pointer avoid unnecessary instrumentation, and it is as powerful. + // Hence, cast to `*const T`. + let ptr: *const T = ptr; + let (thin_ptr, metadata) = ptr.to_raw_parts(); + metadata.is_ptr_aligned(thin_ptr, Internal) && is_inbounds(&metadata, thin_ptr) +} + +/// Check if the pointer is valid for unaligned write access according to [crate::mem] conditions +/// 1, 2 and 3. +/// +/// Note this function succeeds for unaligned pointers. See [self::can_write] if you also +/// want to check pointer alignment. +/// +/// This function will panic today if the pointer is not null, and it points to an unallocated or +/// deallocated memory location. This is an existing Kani limitation. +/// See for more details. +#[crate::unstable( + feature = "mem-predicates", + issue = 2690, + reason = "experimental memory predicate API" +)] +pub fn can_write_unaligned(ptr: *const T) -> bool +where + T: ?Sized, + ::Metadata: PtrProperties, +{ + let (thin_ptr, metadata) = ptr.to_raw_parts(); + is_inbounds(&metadata, thin_ptr) +} + +/// Checks that pointer `ptr` point to a valid value of type `T`. +/// +/// For that, the pointer has to be a valid pointer according to [crate::mem] conditions 1, 2 +/// and 3, +/// and the value stored must respect the validity invariants for type `T`. /// /// TODO: Kani should automatically add those checks when a de-reference happens. /// /// -/// This function will either panic or return `true`. This is to make it easier to use it in -/// contracts. +/// This function will panic today if the pointer is not null, and it points to an unallocated or +/// deallocated memory location. This is an existing Kani limitation. +/// See for more details. #[crate::unstable( feature = "mem-predicates", issue = 2690, reason = "experimental memory predicate API" )] -pub fn assert_valid_ptr(ptr: *const T) -> bool +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn can_dereference(ptr: *const T) -> bool where T: ?Sized, ::Metadata: PtrProperties, { - crate::assert(!ptr.is_null(), "Expected valid pointer, but found `null`"); + let (thin_ptr, metadata) = ptr.to_raw_parts(); + metadata.is_ptr_aligned(thin_ptr, Internal) + && is_inbounds(&metadata, thin_ptr) + && unsafe { has_valid_value(ptr) } +} +/// Checks that pointer `ptr` point to a valid value of type `T`. +/// +/// For that, the pointer has to be a valid pointer according to [crate::mem] conditions 1, 2 +/// and 3, +/// and the value stored must respect the validity invariants for type `T`. +/// +/// Note this function succeeds for unaligned pointers. See [self::can_dereference] if you also +/// want to check pointer alignment. +/// +/// This function will panic today if the pointer is not null, and it points to an unallocated or +/// deallocated memory location. This is an existing Kani limitation. +/// See for more details. +#[crate::unstable( + feature = "mem-predicates", + issue = 2690, + reason = "experimental memory predicate API" +)] +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn can_read_unaligned(ptr: *const T) -> bool +where + T: ?Sized, + ::Metadata: PtrProperties, +{ let (thin_ptr, metadata) = ptr.to_raw_parts(); + is_inbounds(&metadata, thin_ptr) && unsafe { has_valid_value(ptr) } +} + +/// Checks that `data_ptr` points to an allocation that can hold data of size calculated from `T`. +/// +/// This will panic if `data_ptr` points to an invalid `non_null` +fn is_inbounds(metadata: &M, data_ptr: *const ()) -> bool +where + M: PtrProperties, + T: ?Sized, +{ let sz = metadata.pointee_size(Internal); if sz == 0 { - true // ZST pointers are always valid + true // ZST pointers are always valid including nullptr. + } else if data_ptr.is_null() { + false } 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 crate::assert( - is_read_ok(thin_ptr, sz), - "Expected valid pointer, but found dangling pointer", + unsafe { is_allocated(data_ptr, 0) }, + "Kani does not support reasoning about pointer to unallocated memory", ); - true + unsafe { is_allocated(data_ptr, sz) } } } @@ -86,6 +186,12 @@ mod private { pub trait PtrProperties { fn pointee_size(&self, _: Internal) -> usize; + /// A pointer is aligned if its address is a multiple of its minimum alignment. + fn is_ptr_aligned(&self, ptr: *const (), internal: Internal) -> bool { + let min = self.min_alignment(internal); + ptr as usize % min == 0 + } + fn min_alignment(&self, _: Internal) -> usize; fn dangling(&self, _: Internal) -> *const (); @@ -160,18 +266,33 @@ where /// Check if the pointer `_ptr` contains an allocated address of size equal or greater than `_size`. /// -/// This function should only be called to ensure a pointer is valid. The opposite isn't true. +/// # Safety +/// +/// This function should only be called to ensure a pointer is always valid, i.e., in an assertion +/// context. +/// /// I.e.: This function always returns `true` if the pointer is valid. /// Otherwise, it returns non-det boolean. -#[rustc_diagnostic_item = "KaniIsReadOk"] +#[rustc_diagnostic_item = "KaniIsAllocated"] #[inline(never)] -fn is_read_ok(_ptr: *const (), _size: usize) -> bool { +unsafe fn is_allocated(_ptr: *const (), _size: usize) -> bool { + kani_intrinsic() +} + +/// Check if the value stored in the given location satisfies type `T` validity requirements. +/// +/// # Safety +/// +/// - Users have to ensure that the pointer is aligned the pointed memory is allocated. +#[rustc_diagnostic_item = "KaniValidValue"] +#[inline(never)] +unsafe fn has_valid_value(_ptr: *const T) -> bool { kani_intrinsic() } #[cfg(test)] mod tests { - use super::{assert_valid_ptr, PtrProperties}; + use super::{can_dereference, can_write, PtrProperties}; use crate::mem::private::Internal; use std::fmt::Debug; use std::intrinsics::size_of; @@ -225,39 +346,42 @@ mod tests { #[test] pub fn test_empty_slice() { - let slice_ptr = Vec::::new().as_slice() as *const [char]; - assert_valid_ptr(slice_ptr); + let slice_ptr = Vec::::new().as_mut_slice() as *mut [char]; + assert!(can_write(slice_ptr)); } #[test] pub fn test_empty_str() { - let slice_ptr = String::new().as_str() as *const str; - assert_valid_ptr(slice_ptr); + let slice_ptr = String::new().as_mut_str() as *mut str; + assert!(can_write(slice_ptr)); } #[test] fn test_dangling_zst() { - test_dangling_of_t::<()>(); - test_dangling_of_t::<[(); 10]>(); + test_dangling_of_zst::<()>(); + test_dangling_of_zst::<[(); 10]>(); } - fn test_dangling_of_t() { - let dangling: *const T = NonNull::::dangling().as_ptr(); - assert_valid_ptr(dangling); + fn test_dangling_of_zst() { + let dangling: *mut T = NonNull::::dangling().as_ptr(); + assert!(can_write(dangling)); - let vec_ptr = Vec::::new().as_ptr(); - assert_valid_ptr(vec_ptr); + let vec_ptr = Vec::::new().as_mut_ptr(); + assert!(can_write(vec_ptr)); } #[test] - #[should_panic(expected = "Expected valid pointer, but found `null`")] fn test_null_fat_ptr() { - assert_valid_ptr(ptr::null::() as *const dyn Debug); + assert!(!can_dereference(ptr::null::() as *const dyn Debug)); } #[test] - #[should_panic(expected = "Expected valid pointer, but found `null`")] fn test_null_char() { - assert_valid_ptr(ptr::null::()); + assert!(!can_dereference(ptr::null::())); + } + + #[test] + fn test_null_mut() { + assert!(!can_write(ptr::null_mut::())); } } diff --git a/scripts/kani-regression.sh b/scripts/kani-regression.sh index 3b029c475d5f..dcced313ce0b 100755 --- a/scripts/kani-regression.sh +++ b/scripts/kani-regression.sh @@ -50,15 +50,16 @@ RUSTFLAGS=--cfg=kani_sysroot cargo test -p kani_macros --features syn/extra-trai # Declare testing suite information (suite and mode) TESTS=( "script-based-pre exec" - "coverage coverage-based" "kani kani" "expected expected" "ui expected" + "std-checks cargo-kani" "firecracker kani" "prusti kani" "smack kani" "cargo-kani cargo-kani" "cargo-ui cargo-kani" + "coverage coverage-based" "kani-docs cargo-kani" "kani-fixme kani-fixme" ) diff --git a/tests/expected/function-contract/valid_ptr.expected b/tests/expected/function-contract/valid_ptr.expected index f45cb4e2e826..1b62781adaaf 100644 --- a/tests/expected/function-contract/valid_ptr.expected +++ b/tests/expected/function-contract/valid_ptr.expected @@ -1,5 +1,6 @@ Checking harness pre_condition::harness_invalid_ptr... -Failed Checks: Expected valid pointer, but found dangling pointer +Failed Checks: Kani does not support reasoning about pointer to unallocated memory +VERIFICATION:- SUCCESSFUL (encountered one or more panics as expected) Checking harness pre_condition::harness_stack_ptr... VERIFICATION:- SUCCESSFUL @@ -7,5 +8,4 @@ VERIFICATION:- SUCCESSFUL Checking harness pre_condition::harness_head_ptr... VERIFICATION:- SUCCESSFUL -Verification failed for - pre_condition::harness_invalid_ptr -Complete - 2 successfully verified harnesses, 1 failures, 3 total +Complete - 3 successfully verified harnesses, 0 failures, 3 total diff --git a/tests/expected/function-contract/valid_ptr.rs b/tests/expected/function-contract/valid_ptr.rs index da212c7fa63e..2047a46caf4f 100644 --- a/tests/expected/function-contract/valid_ptr.rs +++ b/tests/expected/function-contract/valid_ptr.rs @@ -2,14 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zfunction-contracts -Zmem-predicates -//! Test that it is sound to use `assert_valid_ptr` inside a contract pre-condition. +//! Test that it is sound to use memory predicates inside a contract pre-condition. //! We cannot validate post-condition yet. This can be done once we fix: //! -#![feature(pointer_is_aligned)] mod pre_condition { /// This contract should succeed only if the input is a valid pointer. - #[kani::requires(kani::mem::assert_valid_ptr(ptr) && ptr.is_aligned())] + #[kani::requires(kani::mem::can_dereference(ptr))] unsafe fn read_ptr(ptr: *const i32) -> i32 { *ptr } @@ -29,6 +28,7 @@ mod pre_condition { } #[kani::proof_for_contract(read_ptr)] + #[kani::should_panic] fn harness_invalid_ptr() { let ptr = std::ptr::NonNull::::dangling().as_ptr(); assert_eq!(unsafe { read_ptr(ptr) }, -20); @@ -40,7 +40,7 @@ mod pre_condition { mod post_condition { /// This contract should fail since we are creating a dangling pointer. - #[kani::ensures(kani::mem::assert_valid_ptr(result.0))] + #[kani::ensures(kani::mem::can_dereference(result.0))] unsafe fn new_invalid_ptr() -> PtrWrapper { let var = 'c'; PtrWrapper(&var as *const _) diff --git a/tests/kani/MemPredicates/fat_ptr_validity.rs b/tests/kani/MemPredicates/fat_ptr_validity.rs index 51d784f06566..c4f037f3a646 100644 --- a/tests/kani/MemPredicates/fat_ptr_validity.rs +++ b/tests/kani/MemPredicates/fat_ptr_validity.rs @@ -5,24 +5,22 @@ extern crate kani; -use kani::mem::assert_valid_ptr; +use kani::mem::{can_dereference, can_write}; mod valid_access { use super::*; #[kani::proof] pub fn check_valid_dyn_ptr() { - let boxed: Box> = Box::new(10u8); - let raw_ptr = Box::into_raw(boxed); - assert_valid_ptr(raw_ptr); - let boxed = unsafe { Box::from_raw(raw_ptr) }; - assert_ne!(*boxed, 0); + let mut var = 10u8; + let fat_ptr: *mut dyn PartialEq = &mut var as *mut _; + assert!(can_write(fat_ptr)); } #[kani::proof] pub fn check_valid_slice_ptr() { let array = ['a', 'b', 'c']; let slice = &array as *const [char]; - assert_valid_ptr(slice); + assert!(can_dereference(slice)); assert_eq!(unsafe { &*slice }[0], 'a'); assert_eq!(unsafe { &*slice }[2], 'c'); } @@ -30,10 +28,10 @@ mod valid_access { #[kani::proof] pub fn check_valid_zst() { let slice_ptr = Vec::::new().as_slice() as *const [char]; - assert_valid_ptr(slice_ptr); + assert!(can_dereference(slice_ptr)); let str_ptr = String::new().as_str() as *const str; - assert_valid_ptr(str_ptr); + assert!(can_dereference(str_ptr)); } } @@ -43,14 +41,14 @@ mod invalid_access { #[kani::should_panic] pub fn check_invalid_dyn_ptr() { let raw_ptr: *const dyn PartialEq = unsafe { new_dead_ptr::(0) }; - assert_valid_ptr(raw_ptr); + 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_valid_ptr(raw_ptr); + assert!(can_dereference(raw_ptr)); } #[kani::proof] @@ -59,7 +57,7 @@ mod invalid_access { let array = [10usize; 10]; let invalid: *const [usize; 11] = &array as *const [usize; 10] as *const [usize; 11]; let ptr: *const [usize] = invalid as *const _; - assert_valid_ptr(ptr); + assert!(can_dereference(ptr)); } unsafe fn new_dead_ptr(val: T) -> *const T { diff --git a/tests/kani/MemPredicates/thin_ptr_validity.rs b/tests/kani/MemPredicates/thin_ptr_validity.rs index 49e9c403cda8..553c5beab9f8 100644 --- a/tests/kani/MemPredicates/thin_ptr_validity.rs +++ b/tests/kani/MemPredicates/thin_ptr_validity.rs @@ -5,7 +5,7 @@ extern crate kani; -use kani::mem::assert_valid_ptr; +use kani::mem::can_dereference; use std::ptr::NonNull; mod valid_access { @@ -13,19 +13,19 @@ mod valid_access { #[kani::proof] pub fn check_dangling_zst() { let dangling = NonNull::<()>::dangling().as_ptr(); - assert_valid_ptr(dangling); + assert!(can_dereference(dangling)); let vec_ptr = Vec::<()>::new().as_ptr(); - assert_valid_ptr(vec_ptr); + assert!(can_dereference(vec_ptr)); let dangling = NonNull::<[char; 0]>::dangling().as_ptr(); - assert_valid_ptr(dangling); + assert!(can_dereference(dangling)); } #[kani::proof] pub fn check_valid_array() { let array = ['a', 'b', 'c']; - assert_valid_ptr(&array); + assert!(can_dereference(&array)); } } @@ -35,14 +35,14 @@ mod invalid_access { #[kani::should_panic] pub fn check_invalid_ptr() { let raw_ptr = unsafe { new_dead_ptr::(0) }; - assert_valid_ptr(raw_ptr); + 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_valid_ptr(raw_ptr); + assert!(can_dereference(raw_ptr)); } #[kani::proof] @@ -50,7 +50,7 @@ mod invalid_access { let raw_ptr: *const [char; 0] = unsafe { new_dead_ptr::<[char; 2]>(['a', 'b']) } as *const _; // ZST pointer are always valid - assert_valid_ptr(raw_ptr); + assert!(can_dereference(raw_ptr)); } unsafe fn new_dead_ptr(val: T) -> *const T { diff --git a/tests/kani/ValidValues/custom_niche.rs b/tests/kani/ValidValues/custom_niche.rs index 02b5b87bd092..b282fec3c645 100644 --- a/tests/kani/ValidValues/custom_niche.rs +++ b/tests/kani/ValidValues/custom_niche.rs @@ -4,8 +4,8 @@ //! Check that Kani can identify UB when using niche attribute for a custom operation. #![feature(rustc_attrs)] -use std::mem; use std::mem::size_of; +use std::{mem, ptr}; /// A possible implementation for a system of rating that defines niche. /// A Rating represents the number of stars of a given product (1..=5). @@ -78,6 +78,25 @@ pub fn check_invalid_transmute_copy() { let _rating: Rating = unsafe { mem::transmute_copy(&any) }; } +/// This code does not trigger UB, and verification should succeed. +/// +/// 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(); + unsafe { ptr::copy_nonoverlapping(&stars as *const _ as *const Rating, &mut rating, 1) }; +} + +#[kani::proof] +#[kani::should_panic] +pub fn check_copy_nonoverlap_ub() { + let any: u8 = kani::any(); + let mut rating: Rating = kani::any(); + unsafe { ptr::copy_nonoverlapping(&any as *const _ as *const Rating, &mut rating, 1) }; +} + #[kani::proof] #[kani::should_panic] pub fn check_invalid_increment() { diff --git a/tests/std-checks/core/Cargo.toml b/tests/std-checks/core/Cargo.toml new file mode 100644 index 000000000000..9cacaa1368e3 --- /dev/null +++ b/tests/std-checks/core/Cargo.toml @@ -0,0 +1,13 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +[package] +name = "verify-core" +version = "0.1.0" +edition = "2021" +description = "This crate contains contracts and harnesses for core library" + +[package.metadata.kani] +unstable = { function-contracts = true, mem-predicates = true } + +[package.metadata.kani.flags] +output-format = "terse" diff --git a/tests/std-checks/core/mem.expected b/tests/std-checks/core/mem.expected new file mode 100644 index 000000000000..8a3b89a8f66a --- /dev/null +++ b/tests/std-checks/core/mem.expected @@ -0,0 +1,3 @@ +Summary: +Verification failed for - mem::verify::check_swap_unit +Complete - 3 successfully verified harnesses, 1 failures, 4 total. diff --git a/tests/std-checks/core/ptr.expected b/tests/std-checks/core/ptr.expected new file mode 100644 index 000000000000..337174141ff0 --- /dev/null +++ b/tests/std-checks/core/ptr.expected @@ -0,0 +1,3 @@ +Summary: +Verification failed for - ptr::verify::check_replace_unit +Complete - 5 successfully verified harnesses, 1 failures, 6 total. diff --git a/tests/std-checks/core/src/lib.rs b/tests/std-checks/core/src/lib.rs new file mode 100644 index 000000000000..f0e5b480a2d2 --- /dev/null +++ b/tests/std-checks/core/src/lib.rs @@ -0,0 +1,9 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Top file that includes all sub-modules mimicking core structure. + +extern crate kani; + +pub mod mem; +pub mod ptr; diff --git a/tests/std-checks/core/src/mem.rs b/tests/std-checks/core/src/mem.rs new file mode 100644 index 000000000000..5ca439a18e1c --- /dev/null +++ b/tests/std-checks/core/src/mem.rs @@ -0,0 +1,75 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +//! Verify a few std::ptr::NonNull functions. + +/// Create wrapper functions to standard library functions that contains their contract. +pub mod contracts { + /// Swaps the values at two mutable locations, without deinitializing either one. + /// + /// TODO: Once history variable has been added, add a post-condition. + /// Also add a function to do a bitwise value comparison between arguments (ignore paddings). + /// + /// ```ignore + /// #[kani::ensures(kani::equals(kani::old(x), y))] + /// #[kani::ensures(kani::equals(kani::old(y), x))] + /// ``` + #[kani::modifies(x)] + #[kani::modifies(y)] + pub fn swap(x: &mut T, y: &mut T) { + std::mem::swap(x, y) + } +} + +#[cfg(kani)] +mod verify { + use super::*; + + /// Use this type to ensure that mem swap does not drop the value. + #[derive(kani::Arbitrary)] + struct CannotDrop { + inner: T, + } + + impl Drop for CannotDrop { + fn drop(&mut self) { + unreachable!("Cannot drop") + } + } + + #[kani::proof_for_contract(contracts::swap)] + pub fn check_swap_primitive() { + let mut x: u8 = kani::any(); + let mut y: u8 = kani::any(); + contracts::swap(&mut x, &mut y) + } + + /// FIX-ME: Modifies clause fail with pointer to ZST. + /// + /// FIX-ME: `typed_swap` intrisic fails for ZST. + /// + #[kani::proof_for_contract(contracts::swap)] + pub fn check_swap_unit() { + let mut x: () = kani::any(); + let mut y: () = kani::any(); + contracts::swap(&mut x, &mut y) + } + + #[kani::proof_for_contract(contracts::swap)] + pub fn check_swap_adt_no_drop() { + let mut x: CannotDrop = kani::any(); + let mut y: CannotDrop = kani::any(); + contracts::swap(&mut x, &mut y); + std::mem::forget(x); + std::mem::forget(y); + } + + /// Memory swap logic is optimized according to the size and alignment of a type. + #[kani::proof_for_contract(contracts::swap)] + pub fn check_swap_large_adt_no_drop() { + let mut x: CannotDrop<[u128; 5]> = kani::any(); + let mut y: CannotDrop<[u128; 5]> = kani::any(); + contracts::swap(&mut x, &mut y); + std::mem::forget(x); + std::mem::forget(y); + } +} diff --git a/tests/std-checks/core/src/ptr.rs b/tests/std-checks/core/src/ptr.rs new file mode 100644 index 000000000000..847b8fd1c138 --- /dev/null +++ b/tests/std-checks/core/src/ptr.rs @@ -0,0 +1,112 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +//! Add contracts for functions from std::ptr. + +use std::ptr::NonNull; + +/// Create wrapper functions to standard library functions that contains their contract. +pub mod contracts { + use super::*; + use kani::{ensures, implies, mem::*, modifies, requires}; + + #[ensures(implies!(ptr.is_null() => result.is_none()))] + #[ensures(implies!(!ptr.is_null() => result.is_some()))] + pub fn new(ptr: *mut T) -> Option> { + NonNull::new(ptr) + } + + /// # Safety: + /// When calling this method, you have to ensure that all the following is true: + /// + /// - The pointer must be properly aligned. + /// - It must be “dereferenceable” in the sense defined in the module documentation. + /// - TODO: The pointer must point to an initialized instance of T. + /// - We check for value validity, but not initialization yet. + /// + /// TODO: How to ensure aliasing rules?? + /// You must enforce Rust’s aliasing rules, since the returned lifetime 'a is arbitrarily chosen and does not + /// necessarily reflect the actual lifetime of the data. In particular, while this reference exists, the memory + /// the pointer points to must not get mutated (except inside UnsafeCell). + /// Taken from: + #[requires(can_dereference(obj.as_ptr()))] + pub unsafe fn as_ref<'a, T>(obj: &NonNull) -> &'a T { + obj.as_ref() + } + + #[requires(!ptr.is_null())] + pub unsafe fn new_unchecked(ptr: *mut T) -> NonNull { + NonNull::::new_unchecked(ptr) + } + + /// Safety + /// + /// Behavior is undefined if any of the following conditions are violated: + /// - `dst` must be valid for both reads and writes. + /// - `dst` must be properly aligned. + /// - TODO: `dst` must point to a properly initialized value of type `T`. + /// - We check validity but not initialization. + /// + /// Note that even if `T` has size 0, the pointer must be non-null and properly aligned. + #[requires(can_dereference(dst))] + #[modifies(dst)] + pub unsafe fn replace(dst: *mut T, src: T) -> T { + std::ptr::replace(dst, src) + } +} + +#[cfg(kani)] +mod verify { + use super::*; + use kani::cover; + + #[kani::proof_for_contract(contracts::new)] + pub fn check_new() { + let ptr = kani::any::() as *mut (); + let res = contracts::new(ptr); + cover!(res.is_none()); + cover!(res.is_some()); + } + + #[kani::proof_for_contract(contracts::new_unchecked)] + pub fn check_new_unchecked() { + let ptr = kani::any::() as *mut (); + let _ = unsafe { contracts::new_unchecked(ptr) }; + } + + #[kani::proof_for_contract(contracts::as_ref)] + pub fn check_as_ref() { + let ptr = kani::any::>(); + let non_null = NonNull::new(Box::into_raw(ptr)).unwrap(); + let _rf = unsafe { contracts::as_ref(&non_null) }; + } + + #[kani::proof_for_contract(contracts::as_ref)] + #[kani::should_panic] + pub fn check_as_ref_dangling() { + let ptr = kani::any::() as *mut u8; + kani::assume(!ptr.is_null()); + let non_null = NonNull::new(ptr).unwrap(); + let _rf = unsafe { contracts::as_ref(&non_null) }; + } + + /// FIX-ME: Modifies clause fail with pointer to ZST. + /// + #[kani::proof_for_contract(contracts::replace)] + pub fn check_replace_unit() { + check_replace_impl::<()>(); + } + + #[kani::proof_for_contract(contracts::replace)] + pub fn check_replace_char() { + check_replace_impl::(); + } + + fn check_replace_impl() { + let mut dst = T::any(); + let orig = dst.clone(); + let src = T::any(); + let ret = unsafe { contracts::replace(&mut dst, src.clone()) }; + assert_eq!(ret, orig); + assert_eq!(dst, src); + } +} From 63dd50435761c697b88a5d054c6be6f9824ebbc8 Mon Sep 17 00:00:00 2001 From: Kareem Khazem Date: Wed, 29 May 2024 18:46:58 +0100 Subject: [PATCH 109/225] Don't crash benchcomp when rounding non-numeric values (#3211) Previously, benchcomp would crash while rendering a scatterplot when some results had non-numeric values, because those values were being rounded using a function that doesn't handle non-numeric arguments. This commit fixes #3210. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- tools/benchcomp/benchcomp/visualizers/__init__.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tools/benchcomp/benchcomp/visualizers/__init__.py b/tools/benchcomp/benchcomp/visualizers/__init__.py index 1452488c76b1..865386900639 100644 --- a/tools/benchcomp/benchcomp/visualizers/__init__.py +++ b/tools/benchcomp/benchcomp/visualizers/__init__.py @@ -259,7 +259,7 @@ def _get_template(): {%- for bench_name, bench_variants in d["scaled_metrics"][metric]["benchmarks"].items () %} {% set v0 = bench_variants[d["scaled_variants"][metric][0]] -%} {% set v1 = bench_variants[d["scaled_variants"][metric][1]] -%} - "{{ bench_name }}": [{{ v0|round(3) }}, {{ v1|round(3) }}] + "{{ bench_name }}": [{{ v0|safe_round(3) }}, {{ v1|safe_round(3) }}] {%- endfor %} ``` Scatterplot axis ranges are {{ d["scaled_metrics"][metric]["min_value"] }} (bottom/left) to {{ d["scaled_metrics"][metric]["max_value"] }} (top/right). @@ -275,6 +275,14 @@ def _get_template(): """) + @staticmethod + def _safe_round(value, precision): + try: + return round(value, precision) + except TypeError: + return 0 + + @staticmethod def _get_variant_names(results): return results.values()[0]["variants"] @@ -410,6 +418,7 @@ def __call__(self, results): loader=jinja2.BaseLoader, autoescape=jinja2.select_autoescape( enabled_extensions=("html"), default_for_string=True)) + env.filters["safe_round"] = self._safe_round template = env.from_string(self._get_template()) include_scatterplot = self.scatterplot != Plot.OFF output = template.render(d=data, scatterplot=include_scatterplot)[:-1] From 4a889397bba73fb76f0a21aad66171daad0b2b31 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Thu, 30 May 2024 13:24:52 -0500 Subject: [PATCH 110/225] Update Rust toolchain nightly-2024-05-24 (#3212) > Please ensure your PR description includes the following: > 1. A description of how your changes improve Kani. > 2. Some context on the problem you are solving. > 3. A list of issues that are resolved by this PR. > 4. If you had to perform any manual test, please describe them. > > **Make sure you remove this list from the final PR description.** Fix usage of `tcx.crates()`: https://github.com/rust-lang/rust/pull/124976. 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 | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kani-compiler/src/kani_middle/resolve.rs b/kani-compiler/src/kani_middle/resolve.rs index 53670bfd482d..816f25343fe3 100644 --- a/kani-compiler/src/kani_middle/resolve.rs +++ b/kani-compiler/src/kani_middle/resolve.rs @@ -247,7 +247,7 @@ where /// Resolves an external crate name. fn resolve_external(tcx: TyCtxt, name: &str) -> Option { debug!(?name, "resolve_external"); - tcx.crates(()).iter().find_map(|crate_num| { + tcx.used_crates(()).iter().find_map(|crate_num| { let crate_name = tcx.crate_name(*crate_num); if crate_name.as_str() == name { Some(DefId { index: CRATE_DEF_INDEX, krate: *crate_num }) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 8cc564b125c4..afb9d0e2cd95 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-05-23" +channel = "nightly-2024-05-24" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From 6cf21a87555a163a8f86373c5f5242568d01d460 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Fri, 31 May 2024 14:34:10 -0500 Subject: [PATCH 111/225] Upgrade Rust toolchain nightly-2024-05-27 (#3215) `lazy_cell` became stable: https://github.com/rust-lang/rust/pull/121377. `vtable_ptr` is renamed to `_vtable_ptr`: https://github.com/rust-lang/rust/commit/d83f3ca8ca. 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/main.rs | 1 - rust-toolchain.toml | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index 8a9aa4821ab6..131e40944a0f 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -699,7 +699,7 @@ impl<'tcx> GotocCtx<'tcx> { data_cast } else { let vtable_expr = - meta.member("vtable_ptr", &self.symbol_table).cast_to( + meta.member("_vtable_ptr", &self.symbol_table).cast_to( typ.lookup_field_type("vtable", &self.symbol_table).unwrap(), ); dynamic_fat_ptr(typ, data_cast, vtable_expr, &self.symbol_table) diff --git a/kani-compiler/src/main.rs b/kani-compiler/src/main.rs index fef0fba66489..a5cfa347a85e 100644 --- a/kani-compiler/src/main.rs +++ b/kani-compiler/src/main.rs @@ -10,7 +10,6 @@ #![recursion_limit = "256"] #![feature(box_patterns)] #![feature(rustc_private)] -#![feature(lazy_cell)] #![feature(more_qualified_paths)] #![feature(iter_intersperse)] #![feature(let_chains)] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index afb9d0e2cd95..787015c6337e 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-05-24" +channel = "nightly-2024-05-27" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From c2cb6e4576eb5a68b982405efb1a4ea9d4a4c3d5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 21:57:21 +0000 Subject: [PATCH 112/225] Automatic toolchain upgrade to nightly-2024-05-28 (#3217) Update Rust toolchain from nightly-2024-05-27 to nightly-2024-05-28 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/bdbbb6c6a718d4d196131aa16bafb60e850311d9 up to https://github.com/rust-lang/rust/commit/84b40fc908c3adc7e0e470b3fbaa264df0e122b8. The log for this commit range is: https://github.com/rust-lang/rust/commit/84b40fc908 Auto merge of #125628 - matthiaskrgr:rollup-3zk9v3w, r=matthiaskrgr https://github.com/rust-lang/rust/commit/4966e1ae35 Rollup merge of #125625 - ChrisDenton:line-endings, r=Mark-Simulacrum https://github.com/rust-lang/rust/commit/61f9d35798 Rollup merge of #125616 - RalfJung:mir-validate-downcast-projection, r=compiler-errors https://github.com/rust-lang/rust/commit/e8dd585dd8 Rollup merge of #125542 - GuillaumeGomez:migrate-rustdoc-verify-output-files, r=jieyouxu https://github.com/rust-lang/rust/commit/8bd15878eb Rollup merge of #125339 - tbu-:pr_tidy_ui_tests_u32, r=clubby789 https://github.com/rust-lang/rust/commit/f00b02e6bb Auto merge of #125599 - camelid:clarify-stability, r=notriddle,GuillaumeGomez https://github.com/rust-lang/rust/commit/7a847fc4fb Use grep to implement verify-line-endings https://github.com/rust-lang/rust/commit/b0f8618938 Auto merge of #125413 - lcnr:ambig-drop-region-constraints, r=compiler-errors https://github.com/rust-lang/rust/commit/7d24f87068 MIR validation: ensure that downcast projection is followed by field projection https://github.com/rust-lang/rust/commit/f6e4703e91 Auto merge of #125611 - GuillaumeGomez:rollup-dfavpgg, r=GuillaumeGomez https://github.com/rust-lang/rust/commit/bdf3864d51 Migrate `run-make/rustdoc-verify-output-files` to `rmake.rs` https://github.com/rust-lang/rust/commit/f0ab814aec Add `Rustdoc::output_format` https://github.com/rust-lang/rust/commit/1551fd1202 Add file path in case it cannot be read in `Diff::actual_file` https://github.com/rust-lang/rust/commit/90fec5a087 Add `copy_dir_all` and `recursive_diff` functions to `run-make-support` https://github.com/rust-lang/rust/commit/7083131c92 Rollup merge of #125607 - GuillaumeGomez:migrate-compile-stdin, r=jieyouxu https://github.com/rust-lang/rust/commit/a9c125f864 Rollup merge of #125597 - compiler-errors:early-binder, r=jackh726 https://github.com/rust-lang/rust/commit/cfa7ab474f Rollup merge of #125535 - onur-ozkan:remove-deprecated-field, r=clubby789 https://github.com/rust-lang/rust/commit/f50b4f5034 Rollup merge of #125530 - SparrowLii:expand2, r=petrochenkov https://github.com/rust-lang/rust/commit/ad37f40355 Rollup merge of #125522 - spastorino:fix-lint-docs-edition-handling, r=Urgau,michaelwoerister https://github.com/rust-lang/rust/commit/86f2fa35a2 Rollup merge of #125148 - RalfJung:codegen-sh, r=scottmcm https://github.com/rust-lang/rust/commit/6dddc888fc Rollup merge of #124870 - Lokathor:update-result-docs, r=dtolnay https://github.com/rust-lang/rust/commit/a59072ec4f Auto merge of #125602 - RalfJung:interpret-mir-lifetime, r=oli-obk https://github.com/rust-lang/rust/commit/e4abfaeb62 Migrate `run-make/compile-stdin` to `rmake.rs` https://github.com/rust-lang/rust/commit/b582f807fa Auto merge of #125410 - fmease:adj-lint-diag-api, r=nnethercote https://github.com/rust-lang/rust/commit/fec98b3bbc Auto merge of #125468 - BoxyUwU:remove_defid_from_regionparam, r=compiler-errors https://github.com/rust-lang/rust/commit/e8379c9598 interpret: get rid of 'mir lifetime everywhere https://github.com/rust-lang/rust/commit/36d36a3e1f interpret: the MIR is actually at lifetime 'tcx https://github.com/rust-lang/rust/commit/699d28f968 rustdoc: Show "const" for const-unstable if also overall unstable https://github.com/rust-lang/rust/commit/cdc509f7c0 Auto merge of #125580 - RalfJung:miri-sync, r=RalfJung https://github.com/rust-lang/rust/commit/f92292978f Use EarlyBinder in rustc_type_ir, simplify imports https://github.com/rust-lang/rust/commit/993553ceb8 Uplift EarlyBinder https://github.com/rust-lang/rust/commit/529bb2573a Auto merge of #125593 - workingjubilee:rollup-67qk7di, r=workingjubilee https://github.com/rust-lang/rust/commit/bbcdb4fd3e Give EarlyBinder a tcx parameter https://github.com/rust-lang/rust/commit/4ff78692db Rollup merge of #125582 - scottmcm:less-from-usize, r=jieyouxu https://github.com/rust-lang/rust/commit/45507e4304 Rollup merge of #125566 - camelid:notify-accepted, r=GuillaumeGomez https://github.com/rust-lang/rust/commit/25b079a1cf Rollup merge of #125559 - scottmcm:simplify-shift-ubcheck, r=workingjubilee https://github.com/rust-lang/rust/commit/c51fc1d02b Rollup merge of #125544 - Urgau:check-cfg-mention-cargo-specific, r=jieyouxu https://github.com/rust-lang/rust/commit/b65b2b6ced Rollup merge of #125469 - compiler-errors:dont-skip-inner-const-body, r=cjgillot https://github.com/rust-lang/rust/commit/09e75921f3 Rollup merge of #125466 - compiler-errors:dont-probe-for-ambig-in-sugg, r=jieyouxu https://github.com/rust-lang/rust/commit/5860d43af3 Rollup merge of #125046 - bjorn3:no_mutable_static_linkage, r=cjgillot https://github.com/rust-lang/rust/commit/866630d004 Rollup merge of #124048 - veera-sivarajan:bugfix-123773-c23-variadics, r=compiler-errors https://github.com/rust-lang/rust/commit/0aad3f64e2 Auto merge of #125576 - lnicola:sync-from-ra, r=lnicola https://github.com/rust-lang/rust/commit/d37f456b2a Avoid a `FieldIdx::from_usize` in InstSimplify https://github.com/rust-lang/rust/commit/0963353634 Auto merge of #3631 - RalfJung:blocking-refactor, r=RalfJung https://github.com/rust-lang/rust/commit/2e89443b93 add a macro to declare thread unblock callbacks https://github.com/rust-lang/rust/commit/8e861c6c4c Auto merge of #3632 - RalfJung:readdir, r=RalfJung https://github.com/rust-lang/rust/commit/350f5c88db unix/fs: a bit of cleanup in macos_fbsd_readdir_r https://github.com/rust-lang/rust/commit/e09bf5694b Auto merge of #3633 - RalfJung:target, r=RalfJung https://github.com/rust-lang/rust/commit/cbec1288a2 fix './miri run --dep --target _' https://github.com/rust-lang/rust/commit/e6bb468b53 data_race: vector indices can be reused immediately when the thread is gone https://github.com/rust-lang/rust/commit/a131243557 completely refactor how we manage blocking and unblocking threads https://github.com/rust-lang/rust/commit/f7ca8a6d66 Auto merge of #17296 - mathew-horner:no-clone-target, r=Veykril https://github.com/rust-lang/rust/commit/bd9cc02d10 Auto merge of #17295 - 0xJonas:fix_passing_env_vars_to_cpptools, r=Veykril https://github.com/rust-lang/rust/commit/5fa30f7eaa make release_clock always work on the current thread https://github.com/rust-lang/rust/commit/fa7a3f9049 rustdoc: Elide const-unstable if also unstable overall https://github.com/rust-lang/rust/commit/91b3ef5b4a Notify T-rustdoc for beta-accepted and stable-accepted too https://github.com/rust-lang/rust/commit/9b480da367 It seems that anchor names are implicitly all lowercase https://github.com/rust-lang/rust/commit/0c84361342 Simplify the `unchecked_sh[lr]` ub-checks a bit https://github.com/rust-lang/rust/commit/f8279b10c3 Fix URL target, it's in the module not the type. https://github.com/rust-lang/rust/commit/2b2f83e5ff github showed that weird. https://github.com/rust-lang/rust/commit/2e8f14fb37 correct for copy paste errors when fixing wrapping. https://github.com/rust-lang/rust/commit/22668e83f6 Resolve https://github.com/rust-lang/rust/pull/124870#issuecomment-2128824959 https://github.com/rust-lang/rust/commit/939f2671a0 revert to the inconsistent paragraph wrapping. https://github.com/rust-lang/rust/commit/eb9894f3c9 Removed return https://github.com/rust-lang/rust/commit/afa8dfc51f Avoid clone when constructing runnable label. https://github.com/rust-lang/rust/commit/09677b03dd Formatting https://github.com/rust-lang/rust/commit/78fe45e273 Semicolon https://github.com/rust-lang/rust/commit/2315c6b764 Use correct format for setting environment variables when debugging with cpptools https://github.com/rust-lang/rust/commit/331bb3f10d Auto merge of #3630 - rust-lang:rustup-2024-05-25, r=saethlin https://github.com/rust-lang/rust/commit/bebcb4e4b8 Also mention my-self for check-cfg docs changes https://github.com/rust-lang/rust/commit/c76477d909 add change entry https://github.com/rust-lang/rust/commit/56dddd4c7e Remove deprecated field `dist.missing-tools` https://github.com/rust-lang/rust/commit/1d0ad04993 Merge from rustc https://github.com/rust-lang/rust/commit/3cfcfbf083 Preparing for merge from rustc https://github.com/rust-lang/rust/commit/41d4a95fca Add "better" edition handling on lint-docs tool https://github.com/rust-lang/rust/commit/278212342e cleanup dependence of `ExtCtxt` in transcribe when macro expansion https://github.com/rust-lang/rust/commit/24b5466892 drop region constraints for ambiguous goals https://github.com/rust-lang/rust/commit/ed8e436916 move generics_of call outside of iter https://github.com/rust-lang/rust/commit/56d77b9048 Auto merge of #17275 - roife:fix-issue-17012, r=Veykril https://github.com/rust-lang/rust/commit/796cb8031d Remove failing tests https://github.com/rust-lang/rust/commit/f856ee357c Remove `DefId` from `EarlyParamRegion` (clippy/smir) https://github.com/rust-lang/rust/commit/fe2d7794ca Remove `DefId` from `EarlyParamRegion` (tedium/diagnostics) https://github.com/rust-lang/rust/commit/bd6344d829 Remove `DefId` from `EarlyParamRegion` (type system) https://github.com/rust-lang/rust/commit/b7b350cff7 docs https://github.com/rust-lang/rust/commit/008f6b3a3f Auto merge of #3626 - devnexen:pthread_name_illumos, r=oli-obk https://github.com/rust-lang/rust/commit/7fc41d1bdf Auto merge of #3625 - Strophox:miri-allocation-fix, r=RalfJung https://github.com/rust-lang/rust/commit/b84620ff17 extend comments https://github.com/rust-lang/rust/commit/88d519f718 Auto merge of #3628 - RalfJung:tokio, r=RalfJung https://github.com/rust-lang/rust/commit/561bd9a5ec add back some tokio features https://github.com/rust-lang/rust/commit/10d414091b Auto merge of #3627 - rust-lang:rustup-2024-05-24, r=RalfJung https://github.com/rust-lang/rust/commit/4763eaf066 fmt https://github.com/rust-lang/rust/commit/debf88ae1a Merge from rustc https://github.com/rust-lang/rust/commit/9ce95c30b2 Preparing for merge from rustc https://github.com/rust-lang/rust/commit/c58b7c9c81 Don't skip inner const when looking for body for suggestion https://github.com/rust-lang/rust/commit/4bc41b91d7 Don't continue probing for method if in suggestion and autoderef hits ambiguity https://github.com/rust-lang/rust/commit/7f5e0aade8 solaris add suport for threadname. https://github.com/rust-lang/rust/commit/3c7a13d870 tests: update test for runnables https://github.com/rust-lang/rust/commit/c10bda5577 Update docs https://github.com/rust-lang/rust/commit/1a37cfb703 Use cwd from runnable.args for debugger https://github.com/rust-lang/rust/commit/7b54c8231e Revert "Debug use cargo workspace root as cwd. fixes #13022" https://github.com/rust-lang/rust/commit/d83b267bc1 Add cwd to CargoRunnable https://github.com/rust-lang/rust/commit/6259991f04 Auto merge of #17287 - Veykril:sysroot-encode-empty, r=Veykril https://github.com/rust-lang/rust/commit/f93256ca42 Allow sysroots to only consist of the source root dir https://github.com/rust-lang/rust/commit/1b374dfd9b differentiate between layout and alloc_layout https://github.com/rust-lang/rust/commit/56c363b43e fix alloc_bytes (always allocate at least 1B) https://github.com/rust-lang/rust/commit/ecadf37df4 Auto merge of #17284 - Veykril:doc-links, r=Veykril https://github.com/rust-lang/rust/commit/616fdd04bb Use correct toolchain channel when generating builtin type doc links https://github.com/rust-lang/rust/commit/6e8646df8b Auto merge of #17174 - Kohei316:fix-infer-async-block-with-tail-return-expr, r=Veykril https://github.com/rust-lang/rust/commit/425ed6a181 Update crates/hir-ty/src/infer/expr.rs https://github.com/rust-lang/rust/commit/68fe34a4c2 Auto merge of #17140 - harrysarson:harry-unused-self, r=Veykril https://github.com/rust-lang/rust/commit/6ea763b9e2 Auto merge of #3624 - rust-lang:rustup-2024-05-23, r=RalfJung https://github.com/rust-lang/rust/commit/400835fd11 fmt https://github.com/rust-lang/rust/commit/f1ffb8d859 Merge from rustc https://github.com/rust-lang/rust/commit/807a0f8c21 Preparing for merge from rustc https://github.com/rust-lang/rust/commit/37bf2d2dab Delay the construction of early lint diag structs https://github.com/rust-lang/rust/commit/9f67c50128 Remove `DelayDm` https://github.com/rust-lang/rust/commit/06bc4fc671 Remove `LintDiagnostic::msg` https://github.com/rust-lang/rust/commit/366ef95407 Slightly clean up some lint infra code https://github.com/rust-lang/rust/commit/ac2708a347 Auto merge of #17270 - davidbarsky:david/fix-completions-from-associated-types, r=Veykril https://github.com/rust-lang/rust/commit/f2c3ef77b1 fix: ensure implied bounds from associated types are considered in autocomplete https://github.com/rust-lang/rust/commit/04a9a1a531 Auto merge of #3614 - devnexen:illumos_time_support, r=oli-obk https://github.com/rust-lang/rust/commit/0916e72a34 Auto merge of #17251 - roife:fix-issue-17057, r=Veykril https://github.com/rust-lang/rust/commit/56ce7e0e06 Auto merge of #17252 - davidbarsky:david/refactor-standalone-bools-into-struct, r=Veykril https://github.com/rust-lang/rust/commit/f50f8fbcb9 Simplify https://github.com/rust-lang/rust/commit/7a21dff517 internal: refactor `prefer_no_std`/`prefer_prelude` bools into a struct https://github.com/rust-lang/rust/commit/4e9b12870c fix: check pseudo-block by local_id instead of ModuleOrigin https://github.com/rust-lang/rust/commit/ad810a51f0 Auto merge of #17277 - Veykril:find-path-fixes, r=Veykril https://github.com/rust-lang/rust/commit/3f638a9291 solaris/illumos localtime_r / clock_getime support enabled. https://github.com/rust-lang/rust/commit/d9dda8f84f Auto merge of #17279 - Veykril:format_args-escape, r=Veykril https://github.com/rust-lang/rust/commit/2ff9bab2eb fix: Fix format_args lowering passing incorrect parameters to rustc_parse_format https://github.com/rust-lang/rust/commit/39e6032445 Auto merge of #17248 - mladedav:dm/delay-clear, r=Veykril https://github.com/rust-lang/rust/commit/24bf53d993 Auto merge of #17268 - Veykril:signatures, r=Veykril https://github.com/rust-lang/rust/commit/b1830a5fe6 Update assists test fixtures https://github.com/rust-lang/rust/commit/b29c755572 expectify find_path tests https://github.com/rust-lang/rust/commit/5992af6506 fix: Fix general find-path inconsistencies https://github.com/rust-lang/rust/commit/7fd1429754 Auto merge of #3623 - RalfJung:rustup, r=RalfJung https://github.com/rust-lang/rust/commit/abbe244a81 clippy https://github.com/rust-lang/rust/commit/a1bc030b70 Merge from rustc https://github.com/rust-lang/rust/commit/24138f0034 Preparing for merge from rustc https://github.com/rust-lang/rust/commit/c8b0e5b1a4 The number of tests does not depend on the architecture's pointer width https://github.com/rust-lang/rust/commit/719eee2d82 test: add tests for extern preludes resolving in local mods https://github.com/rust-lang/rust/commit/41c006e21a Auto merge of #3610 - marc0246:missing-error-kinds, r=RalfJung https://github.com/rust-lang/rust/commit/37a37f6ab3 Use `throw_unsup_format` instead of returning `ENOTSUP` in the mmap shim https://github.com/rust-lang/rust/commit/6438554bce Show fn traits in signature info for trait implementors https://github.com/rust-lang/rust/commit/f42e55dfc8 Enable linked locations for closure param inlay hints https://github.com/rust-lang/rust/commit/4b3d7f6039 Render closure fn trait kind in siganture help https://github.com/rust-lang/rust/commit/7045044da3 Allow hir::Param to refer to other entity params aside from functions https://github.com/rust-lang/rust/commit/9ff4ffb817 Update builtin tool list https://github.com/rust-lang/rust/commit/ea2a16cadb fix: resolve extern prelude for local mods in block modules https://github.com/rust-lang/rust/commit/1287e868e9 Clear diagnostics only after new ones were received https://github.com/rust-lang/rust/commit/17bd43cb25 codegen: tweak/extend shift comments https://github.com/rust-lang/rust/commit/b468f21051 Don't use `T` with both Result and Option, improve explanation. https://github.com/rust-lang/rust/commit/531dae1cdf Only allow immutable statics with #[linkage] https://github.com/rust-lang/rust/commit/10f8d1ffef use teletype on the attribute name https://github.com/rust-lang/rust/commit/f94fa6bee3 Some Result combinations work like an Option. https://github.com/rust-lang/rust/commit/dd16cbcb4e braces around {self} in UseTree are not unnecessary https://github.com/rust-lang/rust/commit/39a653f632 Fix coercion of async block https://github.com/rust-lang/rust/commit/f005b451c2 Support C23's Variadics Without a Named Parameter https://github.com/rust-lang/rust/commit/62a104df98 Update Tests Co-authored-by: qinheping <16714939+qinheping@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 787015c6337e..7490dc74d65f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-05-27" +channel = "nightly-2024-05-28" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] From d46da395ee2e323edeae53774455bf09d2c44495 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 11:26:48 +0200 Subject: [PATCH 113/225] Automatic cargo update to 2024-06-03 (#3220) Dependency upgrade resulting from `cargo update`. --- Cargo.lock | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a1ef1f8d8c05..f1ee88cafeed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -166,7 +166,7 @@ version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn 2.0.66", @@ -357,12 +357,6 @@ dependencies = [ "ahash", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -732,9 +726,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -1021,11 +1015,11 @@ checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "f7993a8e3a9e88a00351486baae9522c91b123a088f76469e5bd5cc17198ea87" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", "rustversion", @@ -1383,9 +1377,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" dependencies = [ "memchr", ] From 0c40b6ff952b08ab4ff95175dda30114e74e06f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 21:26:19 +0200 Subject: [PATCH 114/225] Bump tests/perf/s2n-quic from `d03cc47` to `d90729d` (#3222) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `d03cc47` to `d90729d`. --- 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 d03cc470fa98..d90729de3f6d 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit d03cc470fa9812d06d204e312e4ada00079e96df +Subproject commit d90729de3f6d1fdc76ddff734591cfc2d8e61e80 From 7bf23a25dd3d7e9a4bea6c1b3b43d004d1a2c030 Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Mon, 3 Jun 2024 23:29:01 -0700 Subject: [PATCH 115/225] Add an experimental API for shadow memory (#3200) Introduces a data structure, `ShadowMem`, with two methods: ```rust pub fn set(&mut self, ptr: *const U, val: T) pub fn get(&self, ptr: *const U) -> T ``` for setting and getting values of type `T` associated with the memory location that `ptr` points to. Towards #3184 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/overrides/hooks.rs | 70 ++++++++++++++++ kani_metadata/src/unstable.rs | 2 + library/kani/src/lib.rs | 1 + library/kani/src/mem.rs | 14 ++++ library/kani/src/shadow.rs | 83 +++++++++++++++++++ tests/expected/shadow/uninit_array/expected | 3 + tests/expected/shadow/uninit_array/test.rs | 59 +++++++++++++ .../shadow/unsupported_num_objects/expected | 3 + .../shadow/unsupported_num_objects/test.rs | 41 +++++++++ .../shadow/unsupported_object_size/expected | 3 + .../shadow/unsupported_object_size/test.rs | 29 +++++++ 11 files changed, 308 insertions(+) create mode 100644 library/kani/src/shadow.rs create mode 100644 tests/expected/shadow/uninit_array/expected create mode 100644 tests/expected/shadow/uninit_array/test.rs create mode 100644 tests/expected/shadow/unsupported_num_objects/expected create mode 100644 tests/expected/shadow/unsupported_num_objects/test.rs create mode 100644 tests/expected/shadow/unsupported_object_size/expected create mode 100644 tests/expected/shadow/unsupported_object_size/test.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index 9f70c16430e5..9b2f9e770d32 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -242,6 +242,74 @@ impl GotocHook for IsAllocated { } } +/// Encodes __CPROVER_pointer_object(ptr) +struct PointerObject; +impl GotocHook for PointerObject { + fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { + matches_function(tcx, instance.def, "KaniPointerObject") + } + + 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 ptr = fargs.pop().unwrap().cast_to(Type::void_pointer()); + let target = target.unwrap(); + let loc = gcx.codegen_caller_span_stable(span); + let ret_place = + unwrap_or_return_codegen_unimplemented_stmt!(gcx, gcx.codegen_place_stable(assign_to)); + let ret_type = ret_place.goto_expr.typ().clone(); + + Stmt::block( + vec![ + ret_place.goto_expr.assign(Expr::pointer_object(ptr).cast_to(ret_type), loc), + Stmt::goto(bb_label(target), loc), + ], + loc, + ) + } +} + +/// Encodes __CPROVER_pointer_offset(ptr) +struct PointerOffset; +impl GotocHook for PointerOffset { + fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { + matches_function(tcx, instance.def, "KaniPointerOffset") + } + + 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 ptr = fargs.pop().unwrap().cast_to(Type::void_pointer()); + let target = target.unwrap(); + let loc = gcx.codegen_caller_span_stable(span); + let ret_place = + unwrap_or_return_codegen_unimplemented_stmt!(gcx, gcx.codegen_place_stable(assign_to)); + let ret_type = ret_place.goto_expr.typ().clone(); + + Stmt::block( + vec![ + ret_place.goto_expr.assign(Expr::pointer_offset(ptr).cast_to(ret_type), loc), + Stmt::goto(bb_label(target), loc), + ], + loc, + ) + } +} + struct RustAlloc; // Removing this hook causes regression failures. // https://github.com/model-checking/kani/issues/1170 @@ -399,6 +467,8 @@ pub fn fn_hooks() -> GotocHooks { Rc::new(Cover), Rc::new(Nondet), Rc::new(IsAllocated), + Rc::new(PointerObject), + Rc::new(PointerOffset), Rc::new(RustAlloc), Rc::new(MemCmp), Rc::new(UntrackedDeref), diff --git a/kani_metadata/src/unstable.rs b/kani_metadata/src/unstable.rs index 878b468dbdc3..d7b73678a836 100644 --- a/kani_metadata/src/unstable.rs +++ b/kani_metadata/src/unstable.rs @@ -87,6 +87,8 @@ pub enum UnstableFeature { /// Automatically check that no invalid value is produced which is considered UB in Rust. /// Note that this does not include checking uninitialized value. ValidValueChecks, + /// Ghost state and shadow memory APIs + GhostState, } impl UnstableFeature { diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index 25b1b389a2b1..341dd6752916 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -24,6 +24,7 @@ pub mod arbitrary; mod concrete_playback; pub mod futures; pub mod mem; +pub mod shadow; pub mod slice; pub mod tuple; pub mod vec; diff --git a/library/kani/src/mem.rs b/library/kani/src/mem.rs index c485ee16a562..c40a1aa696e3 100644 --- a/library/kani/src/mem.rs +++ b/library/kani/src/mem.rs @@ -290,6 +290,20 @@ unsafe fn has_valid_value(_ptr: *const T) -> bool { kani_intrinsic() } +/// Get the object ID of the given pointer. +#[rustc_diagnostic_item = "KaniPointerObject"] +#[inline(never)] +pub fn pointer_object(_ptr: *const T) -> usize { + kani_intrinsic() +} + +/// Get the object offset of the given pointer. +#[rustc_diagnostic_item = "KaniPointerOffset"] +#[inline(never)] +pub fn pointer_offset(_ptr: *const T) -> usize { + kani_intrinsic() +} + #[cfg(test)] mod tests { use super::{can_dereference, can_write, PtrProperties}; diff --git a/library/kani/src/shadow.rs b/library/kani/src/shadow.rs new file mode 100644 index 000000000000..a7ea57c6fd40 --- /dev/null +++ b/library/kani/src/shadow.rs @@ -0,0 +1,83 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This module contains an API for shadow memory. +//! Shadow memory is a mechanism by which we can store metadata on memory +//! locations, e.g. whether a memory location is initialized. +//! +//! The main data structure provided by this module is the `ShadowMem` struct, +//! which allows us to store metadata on a given memory location. +//! +//! # Example +//! +//! ``` +//! use kani::shadow::ShadowMem; +//! use std::alloc::{alloc, Layout}; +//! +//! let mut sm = ShadowMem::new(false); +//! +//! unsafe { +//! let ptr = alloc(Layout::new::()); +//! // assert the memory location is not initialized +//! assert!(!sm.get(ptr)); +//! // write to the memory location +//! *ptr = 42; +//! // update the shadow memory to indicate that this location is now initialized +//! sm.set(ptr, true); +//! } +//! ``` + +const MAX_NUM_OBJECTS: usize = 1024; +const MAX_OBJECT_SIZE: usize = 64; + +const MAX_NUM_OBJECTS_ASSERT_MSG: &str = "The number of objects exceeds the maximum number supported by Kani's shadow memory model (1024)"; +const MAX_OBJECT_SIZE_ASSERT_MSG: &str = + "The object size exceeds the maximum size supported by Kani's shadow memory model (64)"; + +/// A shadow memory data structure that contains a two-dimensional array of a +/// generic type `T`. +/// Each element of the outer array represents an object, and each element of +/// the inner array represents a byte in the object. +pub struct ShadowMem { + mem: [[T; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS], +} + +impl ShadowMem { + /// Create a new shadow memory instance initialized with the given value + #[crate::unstable( + feature = "ghost-state", + issue = 3184, + reason = "experimental ghost state/shadow memory API" + )] + pub const fn new(val: T) -> Self { + Self { mem: [[val; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS] } + } + + /// Get the shadow memory value of the given pointer + #[crate::unstable( + feature = "ghost-state", + issue = 3184, + reason = "experimental ghost state/shadow memory API" + )] + pub fn get(&self, ptr: *const U) -> T { + let obj = crate::mem::pointer_object(ptr); + let offset = crate::mem::pointer_offset(ptr); + crate::assert(obj < MAX_NUM_OBJECTS, MAX_NUM_OBJECTS_ASSERT_MSG); + crate::assert(offset < MAX_OBJECT_SIZE, MAX_OBJECT_SIZE_ASSERT_MSG); + self.mem[obj][offset] + } + + /// Set the shadow memory value of the given pointer + #[crate::unstable( + feature = "ghost-state", + issue = 3184, + reason = "experimental ghost state/shadow memory API" + )] + pub fn set(&mut self, ptr: *const U, val: T) { + let obj = crate::mem::pointer_object(ptr); + let offset = crate::mem::pointer_offset(ptr); + crate::assert(obj < MAX_NUM_OBJECTS, MAX_NUM_OBJECTS_ASSERT_MSG); + crate::assert(offset < MAX_OBJECT_SIZE, MAX_OBJECT_SIZE_ASSERT_MSG); + self.mem[obj][offset] = val; + } +} diff --git a/tests/expected/shadow/uninit_array/expected b/tests/expected/shadow/uninit_array/expected new file mode 100644 index 000000000000..1d5f70698010 --- /dev/null +++ b/tests/expected/shadow/uninit_array/expected @@ -0,0 +1,3 @@ +Failed Checks: assertion failed: SM.get(p) +Verification failed for - check_init_any +Complete - 1 successfully verified harnesses, 1 failures, 2 total. diff --git a/tests/expected/shadow/uninit_array/test.rs b/tests/expected/shadow/uninit_array/test.rs new file mode 100644 index 000000000000..8a9536e5a8e8 --- /dev/null +++ b/tests/expected/shadow/uninit_array/test.rs @@ -0,0 +1,59 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zghost-state + +// This is a basic test for the shadow memory implementation. +// It checks that shadow memory can be used to track whether a memory location +// is initialized. + +use std::alloc::{alloc, dealloc, Layout}; + +static mut SM: kani::shadow::ShadowMem = kani::shadow::ShadowMem::new(false); + +fn write(ptr: *mut i8, offset: usize, x: i8) { + unsafe { + let p = ptr.offset(offset as isize); + p.write(x); + SM.set(p as *const i8, true); + }; +} + +fn check_init(b: bool) { + // allocate an array of 5 i8's + let layout = Layout::array::(5).unwrap(); + let ptr = unsafe { alloc(layout) as *mut i8 }; + + // unconditionally write to all 5 locations except for the middle element + write(ptr, 0, 0); + write(ptr, 1, 1); + if b { + write(ptr, 2, 2) + }; + write(ptr, 3, 3); + write(ptr, 4, 4); + + // non-deterministically read from any of the elements and assert that: + // 1. The memory location is initialized + // 2. It has the expected value + // This would fail if `b` is false and `index == 2` + let index: usize = kani::any(); + if index < 5 { + unsafe { + let p = ptr.offset(index as isize); + let x = p.read(); + assert!(SM.get(p)); + assert_eq!(x, index as i8); + } + } + unsafe { dealloc(ptr as *mut u8, layout) }; +} + +#[kani::proof] +fn check_init_true() { + check_init(true); +} + +#[kani::proof] +fn check_init_any() { + check_init(kani::any()); +} diff --git a/tests/expected/shadow/unsupported_num_objects/expected b/tests/expected/shadow/unsupported_num_objects/expected new file mode 100644 index 000000000000..da3b5a671969 --- /dev/null +++ b/tests/expected/shadow/unsupported_num_objects/expected @@ -0,0 +1,3 @@ +Failed Checks: The number of objects exceeds the maximum number supported by Kani's shadow memory model (1024) +Verification failed for - check_max_objects_fail +Complete - 1 successfully verified harnesses, 1 failures, 2 total. diff --git a/tests/expected/shadow/unsupported_num_objects/test.rs b/tests/expected/shadow/unsupported_num_objects/test.rs new file mode 100644 index 000000000000..f60d0020e989 --- /dev/null +++ b/tests/expected/shadow/unsupported_num_objects/test.rs @@ -0,0 +1,41 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zghost-state + +// This test checks the maximum number of objects supported by Kani's shadow +// memory model (currently 1024) + +static mut SM: kani::shadow::ShadowMem = kani::shadow::ShadowMem::new(false); + +fn check_max_objects() { + let mut i = 0; + // A dummy loop that creates `N`` objects. + // After the loop, CBMC's object ID counter should be at `N` + 2: + // - `N` created in the loop + + // - the NULL pointer whose object ID is 0, and + // - the object ID for `i` + while i < N { + let x = i; + assert_eq!(kani::mem::pointer_object(&x as *const usize), i + 2); + i += 1; + } + + // create a new object whose ID is `N` + 2 + let x = 42; + assert_eq!(kani::mem::pointer_object(&x as *const i32), N + 2); + // the following call to `set` would fail if the object ID for `x` exceeds + // the maximum allowed by Kani's shadow memory model + unsafe { + SM.set(&x as *const i32, true); + } +} + +#[kani::proof] +fn check_max_objects_pass() { + check_max_objects::<1021>(); +} + +#[kani::proof] +fn check_max_objects_fail() { + check_max_objects::<1022>(); +} diff --git a/tests/expected/shadow/unsupported_object_size/expected b/tests/expected/shadow/unsupported_object_size/expected new file mode 100644 index 000000000000..a4598b5aac82 --- /dev/null +++ b/tests/expected/shadow/unsupported_object_size/expected @@ -0,0 +1,3 @@ +Failed Checks: The object size exceeds the maximum size supported by Kani's shadow memory model (64) +Verification failed for - check_max_object_size_fail +Complete - 1 successfully verified harnesses, 1 failures, 2 total. diff --git a/tests/expected/shadow/unsupported_object_size/test.rs b/tests/expected/shadow/unsupported_object_size/test.rs new file mode 100644 index 000000000000..d22ff1a6ca41 --- /dev/null +++ b/tests/expected/shadow/unsupported_object_size/test.rs @@ -0,0 +1,29 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zghost-state + +// This test checks the maximum object size supported by Kani's shadow +// memory model (currently 64) + +static mut SM: kani::shadow::ShadowMem = kani::shadow::ShadowMem::new(false); + +fn check_max_objects() { + let arr: [u8; N] = [0; N]; + let last = &arr[N - 1]; + assert_eq!(kani::mem::pointer_offset(last as *const u8), N - 1); + // the following call to `set_init` would fail if the object offset for + // `last` exceeds the maximum allowed by Kani's shadow memory model + unsafe { + SM.set(last as *const u8, true); + } +} + +#[kani::proof] +fn check_max_object_size_pass() { + check_max_objects::<64>(); +} + +#[kani::proof] +fn check_max_object_size_fail() { + check_max_objects::<65>(); +} From 358ade91d84869673fbaaaf6f848aa8f068f373f Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 4 Jun 2024 23:55:16 +0300 Subject: [PATCH 116/225] Bump Kani version to 0.52.0 (#3224) Updated version in all `Cargo.toml` files (via `find . -name Cargo.toml -exec sed -i 's/version = "0.51.0"/version = "0.52.0"/' {} \;`) and ran `cargo build-dev` to have `Cargo.lock` files updated. GitHub generated release notes: ## What's Changed * Bump tests/perf/s2n-quic from `6dd41e0` to `bd37960` by @dependabot in https://github.com/model-checking/kani/pull/3178 * Automatic cargo update to 2024-05-13 by @github-actions in https://github.com/model-checking/kani/pull/3177 * Upgrade toolchain to 2024-04-22 by @zhassan-aws in https://github.com/model-checking/kani/pull/3171 * Upgrade toolchain to 2024-05-14 by @zhassan-aws in https://github.com/model-checking/kani/pull/3183 * Automatic toolchain upgrade to nightly-2024-05-15 by @github-actions in https://github.com/model-checking/kani/pull/3185 * Include `--check-cfg=cfg(kani)` in the rust flags to avoid a warning about an unknown `cfg`. by @zhassan-aws in https://github.com/model-checking/kani/pull/3187 * Automatic toolchain upgrade to nightly-2024-05-16 by @github-actions in https://github.com/model-checking/kani/pull/3189 * Perform cargo update because of yanked libc version by @zhassan-aws in https://github.com/model-checking/kani/pull/3192 * Automatic toolchain upgrade to nightly-2024-05-17 by @github-actions in https://github.com/model-checking/kani/pull/3191 * Automatic cargo update to 2024-05-20 by @github-actions in https://github.com/model-checking/kani/pull/3195 * Bump tests/perf/s2n-quic from `bd37960` to `f5d9d74` by @dependabot in https://github.com/model-checking/kani/pull/3196 * New section about linter configuraton checking in the doc. by @remi-delmas-3000 in https://github.com/model-checking/kani/pull/3198 * Automatic cargo update to 2024-05-27 by @github-actions in https://github.com/model-checking/kani/pull/3201 * Bump tests/perf/s2n-quic from `f5d9d74` to `d03cc47` by @dependabot in https://github.com/model-checking/kani/pull/3202 * Update Rust toolchain from nightly-2024-05-17 to nightly-2024-05-23 by @remi-delmas-3000 in https://github.com/model-checking/kani/pull/3199 * Fix `{,e}println!()` by @GrigorenkoPV in https://github.com/model-checking/kani/pull/3209 * Contracts for a few core functions by @celinval in https://github.com/model-checking/kani/pull/3107 * Don't crash benchcomp when rounding non-numeric values by @karkhaz in https://github.com/model-checking/kani/pull/3211 * Update Rust toolchain nightly-2024-05-24 by @qinheping in https://github.com/model-checking/kani/pull/3212 * Upgrade Rust toolchain nightly-2024-05-27 by @qinheping in https://github.com/model-checking/kani/pull/3215 * Automatic toolchain upgrade to nightly-2024-05-28 by @github-actions in https://github.com/model-checking/kani/pull/3217 * Automatic cargo update to 2024-06-03 by @github-actions in https://github.com/model-checking/kani/pull/3220 * Bump tests/perf/s2n-quic from `d03cc47` to `d90729d` by @dependabot in https://github.com/model-checking/kani/pull/3222 * Add simple API for shadow memory by @zhassan-aws in https://github.com/model-checking/kani/pull/3200 ## New Contributors * @GrigorenkoPV made their first contribution in https://github.com/model-checking/kani/pull/3209 **Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.51.0...kani-0.52.0 --- CHANGELOG.md | 13 ++++++++++++- Cargo.lock | 18 +++++++++--------- 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_macros/Cargo.toml | 2 +- library/std/Cargo.toml | 2 +- tools/build-kani/Cargo.toml | 2 +- 11 files changed, 30 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fc4cac54063..9b4acbb284aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,20 @@ 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.51.0] +## [0.52.0] ## What's Changed +* New section about linter configuraton checking in the doc by @remi-delmas-3000 in https://github.com/model-checking/kani/pull/3198 +* Fix `{,e}println!()` by @GrigorenkoPV in https://github.com/model-checking/kani/pull/3209 +* Contracts for a few core functions by @celinval in https://github.com/model-checking/kani/pull/3107 +* Add simple API for shadow memory by @zhassan-aws in https://github.com/model-checking/kani/pull/3200 +* Upgrade Rust toolchain to 2024-05-28 by @zhassan-aws @remi-delmas-3000 @qinheping + +**Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.51.0...kani-0.52.0 + +## [0.51.0] + +### What's Changed * Do not assume that ZST-typed symbols refer to unique objects by @tautschnig in https://github.com/model-checking/kani/pull/3134 * Remove `kani::Arbitrary` from the `modifies` contract instrumentation by @feliperodri in https://github.com/model-checking/kani/pull/3169 diff --git a/Cargo.lock b/Cargo.lock index f1ee88cafeed..dc15890341c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,7 +92,7 @@ checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "build-kani" -version = "0.51.0" +version = "0.52.0" dependencies = [ "anyhow", "cargo_metadata", @@ -228,7 +228,7 @@ dependencies = [ [[package]] name = "cprover_bindings" -version = "0.51.0" +version = "0.52.0" dependencies = [ "lazy_static", "linear-map", @@ -405,14 +405,14 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "kani" -version = "0.51.0" +version = "0.52.0" dependencies = [ "kani_macros", ] [[package]] name = "kani-compiler" -version = "0.51.0" +version = "0.52.0" dependencies = [ "clap", "cprover_bindings", @@ -433,7 +433,7 @@ dependencies = [ [[package]] name = "kani-driver" -version = "0.51.0" +version = "0.52.0" dependencies = [ "anyhow", "cargo_metadata", @@ -461,7 +461,7 @@ dependencies = [ [[package]] name = "kani-verifier" -version = "0.51.0" +version = "0.52.0" dependencies = [ "anyhow", "home", @@ -470,7 +470,7 @@ dependencies = [ [[package]] name = "kani_macros" -version = "0.51.0" +version = "0.52.0" dependencies = [ "proc-macro-error", "proc-macro2", @@ -480,7 +480,7 @@ dependencies = [ [[package]] name = "kani_metadata" -version = "0.51.0" +version = "0.52.0" dependencies = [ "clap", "cprover_bindings", @@ -985,7 +985,7 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "std" -version = "0.51.0" +version = "0.52.0" dependencies = [ "kani", ] diff --git a/Cargo.toml b/Cargo.toml index 24812a157ae5..bd3c7f3336c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-verifier" -version = "0.51.0" +version = "0.52.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 844accd3914b..b98b09c68a58 100644 --- a/cprover_bindings/Cargo.toml +++ b/cprover_bindings/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "cprover_bindings" -version = "0.51.0" +version = "0.52.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index 7315ff224bd8..2ef273802028 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-compiler" -version = "0.51.0" +version = "0.52.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index 6b25fe02a263..2263e39792e2 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-driver" -version = "0.51.0" +version = "0.52.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 645ed4da5fd2..f9d701018367 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_metadata" -version = "0.51.0" +version = "0.52.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index 4b158e41daa0..c0f783e65315 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.51.0" +version = "0.52.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 0f9dda7136ad..e915fdc64a75 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_macros" -version = "0.51.0" +version = "0.52.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index b83c9d3dcba0..fcc2739dc606 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.51.0" +version = "0.52.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 21ad6be2a4b0..30c2807aa323 100644 --- a/tools/build-kani/Cargo.toml +++ b/tools/build-kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "build-kani" -version = "0.51.0" +version = "0.52.0" edition = "2021" description = "Builds Kani, Sysroot and release bundle." license = "MIT OR Apache-2.0" From ab56b9d96308be5acba4865577abb9450518ed9a Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Tue, 4 Jun 2024 15:17:12 -0700 Subject: [PATCH 117/225] Add `kani_core` library placeholder and build logic (#3227) While at it, I also added a `--skip-libs` to skip rebuilding the Kani libraries and standard library at every `cargo build-dev` execution. We usually only need to rebuild the libraries when we make changes to them or when we update the Rust toolchain. Rebuilding them can be quite time consuming when you are making changes to Kani. Towards #3226 #3153 Co-authored-by: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> --- Cargo.lock | 7 ++++ Cargo.toml | 1 + library/kani_core/Cargo.toml | 11 +++++++ library/kani_core/src/lib.rs | 5 +++ library/kani_macros/Cargo.toml | 5 ++- tools/build-kani/src/main.rs | 8 ++++- tools/build-kani/src/parser.rs | 4 +++ tools/build-kani/src/sysroot.rs | 58 +++++++++++++++++++++++---------- 8 files changed, 79 insertions(+), 20 deletions(-) create mode 100644 library/kani_core/Cargo.toml create mode 100644 library/kani_core/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index dc15890341c3..7b2857e1c7ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -468,6 +468,13 @@ dependencies = [ "os_info", ] +[[package]] +name = "kani_core" +version = "0.51.0" +dependencies = [ + "kani_macros", +] + [[package]] name = "kani_macros" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index bd3c7f3336c2..e4ff44f42ddc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ members = [ "kani-driver", "kani-compiler", "kani_metadata", + "library/kani_core", ] # This indicates what package to e.g. build with 'cargo build' without --workspace diff --git a/library/kani_core/Cargo.toml b/library/kani_core/Cargo.toml new file mode 100644 index 000000000000..ad96017a2a28 --- /dev/null +++ b/library/kani_core/Cargo.toml @@ -0,0 +1,11 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +[package] +name = "kani_core" +version = "0.51.0" +edition = "2021" +license = "MIT OR Apache-2.0" +publish = false + +[dependencies] +kani_macros = { path = "../kani_macros", features = ["no_core"] } \ No newline at end of file diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs new file mode 100644 index 000000000000..00b5efbe719c --- /dev/null +++ b/library/kani_core/src/lib.rs @@ -0,0 +1,5 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This is placeholder for the new `kani_core` library. +#![feature(no_core)] diff --git a/library/kani_macros/Cargo.toml b/library/kani_macros/Cargo.toml index e915fdc64a75..dbe2126d0458 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -19,4 +19,7 @@ syn = { version = "2.0.18", features = ["full", "visit-mut", "visit"] } [package.metadata.rust-analyzer] # This package uses rustc crates. -rustc_private=true +rustc_private = true + +[features] +no_core = [] \ No newline at end of file diff --git a/tools/build-kani/src/main.rs b/tools/build-kani/src/main.rs index f65d52e03fd1..3f1bc26cfbbb 100644 --- a/tools/build-kani/src/main.rs +++ b/tools/build-kani/src/main.rs @@ -19,7 +19,13 @@ fn main() -> Result<()> { let args = parser::ArgParser::parse(); match args.subcommand { - parser::Commands::BuildDev(build_parser) => build_lib(&build_bin(&build_parser.args)?), + parser::Commands::BuildDev(build_parser) => { + let bin_folder = &build_bin(&build_parser.args)?; + if !build_parser.skip_libs { + build_lib(&bin_folder)?; + } + Ok(()) + } parser::Commands::Bundle(bundle_parser) => { let version_string = bundle_parser.version; let kani_string = format!("kani-{version_string}"); diff --git a/tools/build-kani/src/parser.rs b/tools/build-kani/src/parser.rs index 3f9dfa0a1d42..718cbb00bcb4 100644 --- a/tools/build-kani/src/parser.rs +++ b/tools/build-kani/src/parser.rs @@ -17,6 +17,10 @@ pub struct BuildDevParser { /// Arguments to be passed down to cargo when building cargo binaries. #[clap(value_name = "ARG", allow_hyphen_values = true)] pub args: Vec, + /// Do not re-build Kani libraries. Only use this if you know there has been no changes to Kani + /// libraries or the underlying Rust compiler. + #[clap(long)] + pub skip_libs: bool, } #[derive(Args, Debug, Eq, PartialEq)] diff --git a/tools/build-kani/src/sysroot.rs b/tools/build-kani/src/sysroot.rs index b831ed0d63a8..f3ece1ee006a 100644 --- a/tools/build-kani/src/sysroot.rs +++ b/tools/build-kani/src/sysroot.rs @@ -55,18 +55,29 @@ pub fn kani_playback_lib() -> PathBuf { path_buf!(kani_sysroot(), "playback/lib") } +/// Returns the path to where Kani libraries for no_core is kept. +pub fn kani_no_core_lib() -> PathBuf { + path_buf!(kani_sysroot(), "no_core/lib") +} + /// Returns the path to where Kani's pre-compiled binaries are stored. fn kani_sysroot_bin() -> PathBuf { path_buf!(kani_sysroot(), "bin") } +/// Returns the build target +fn build_target() -> &'static str { + env!("TARGET") +} + /// Build the `lib/` folder and `lib-playback/` for the new sysroot. /// - The `lib/` folder contains the sysroot for verification. /// - The `lib-playback/` folder contains the sysroot used for playback. pub fn build_lib(bin_folder: &Path) -> Result<()> { let compiler_path = bin_folder.join("kani-compiler"); build_verification_lib(&compiler_path)?; - build_playback_lib(&compiler_path) + build_playback_lib(&compiler_path)?; + build_no_core_lib(&compiler_path) } /// Build the `lib/` folder for the new sysroot used during verification. @@ -75,7 +86,9 @@ fn build_verification_lib(compiler_path: &Path) -> Result<()> { let extra_args = ["-Z", "build-std=panic_abort,std,test", "--config", "profile.dev.panic=\"abort\""]; let compiler_args = ["--kani-compiler", "-Cllvm-args=--ignore-global-asm --build-std"]; - build_kani_lib(compiler_path, &kani_sysroot_lib(), &extra_args, &compiler_args) + let packages = ["std", "kani", "kani_macros"]; + let artifacts = build_kani_lib(compiler_path, &packages, &extra_args, &compiler_args)?; + copy_artifacts(&artifacts, &kani_sysroot_lib(), true) } /// Build the `lib-playback/` folder that will be used during counter example playback. @@ -83,26 +96,30 @@ fn build_verification_lib(compiler_path: &Path) -> Result<()> { fn build_playback_lib(compiler_path: &Path) -> Result<()> { let extra_args = ["--features=std/concrete_playback,kani/concrete_playback", "-Z", "build-std=std,test"]; - build_kani_lib(compiler_path, &kani_playback_lib(), &extra_args, &[]) + let packages = ["std", "kani", "kani_macros"]; + let artifacts = build_kani_lib(compiler_path, &packages, &extra_args, &[])?; + copy_artifacts(&artifacts, &kani_playback_lib(), true) +} + +/// Build the no core library folder that will be used during std verification. +fn build_no_core_lib(compiler_path: &Path) -> Result<()> { + let extra_args = ["--features=kani_macros/no_core"]; + let packages = ["kani_core", "kani_macros"]; + let artifacts = build_kani_lib(compiler_path, &packages, &extra_args, &[])?; + copy_artifacts(&artifacts, &kani_no_core_lib(), false) } fn build_kani_lib( compiler_path: &Path, - output_path: &Path, + packages: &[&str], extra_cargo_args: &[&str], extra_rustc_args: &[&str], -) -> Result<()> { +) -> Result> { // Run cargo build with -Z build-std - let target = env!("TARGET"); + let target = build_target(); let target_dir = env!("KANI_BUILD_LIBS"); let args = [ "build", - "-p", - "std", - "-p", - "kani", - "-p", - "kani_macros", "-Z", "unstable-options", "--target-dir", @@ -137,6 +154,7 @@ fn build_kani_lib( .env("CARGO_ENCODED_RUSTFLAGS", rustc_args.join("\x1f")) .env("RUSTC", compiler_path) .args(args) + .args(packages.iter().copied().flat_map(|pkg| ["-p", pkg])) .args(extra_cargo_args) .stdout(Stdio::piped()) .spawn() @@ -152,20 +170,24 @@ fn build_kani_lib( } // Create sysroot folder hierarchy. - copy_artifacts(&artifacts, output_path, target) + Ok(artifacts) } /// Copy all the artifacts to their correct place to generate a valid sysroot. -fn copy_artifacts(artifacts: &[Artifact], sysroot_lib: &Path, target: &str) -> Result<()> { - // Create sysroot folder hierarchy. +fn copy_artifacts(artifacts: &[Artifact], sysroot_lib: &Path, copy_std: bool) -> Result<()> { + // Create sysroot folder. sysroot_lib.exists().then(|| fs::remove_dir_all(sysroot_lib)); - let std_path = path_buf!(&sysroot_lib, "rustlib", target, "lib"); - fs::create_dir_all(&std_path).expect(&format!("Failed to create {std_path:?}")); + fs::create_dir_all(sysroot_lib)?; // Copy Kani libraries into sysroot top folder. copy_libs(&artifacts, &sysroot_lib, &is_kani_lib); + // Copy standard libraries into rustlib//lib/ folder. - copy_libs(&artifacts, &std_path, &is_std_lib); + 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); + } Ok(()) } From 9f4fc302e229d4c96cb4c670ba06179edc1e232e Mon Sep 17 00:00:00 2001 From: Matias Scharager Date: Wed, 5 Jun 2024 14:17:53 -0400 Subject: [PATCH 118/225] Change ensures into closures (#3207) This change now separates the front facing "result" name and the internal facing "result_kani_internal" ident where the user can specify with the keyword "result" but then the system replaces this with the internal representation. If the user chooses to use a different variable name than result, this now supports the syntax of `#[kani::ensures(|result_var| expr)]` where result_var can be any arbitrary name. For example, the following test now works: ``` #[kani::ensures(|banana : &u32| *banana == a.wrapping_add(b))] fn simple_addition(a: u32, b: u32) -> u32 { return a.wrapping_add(b); } ``` In addition, the string "result_kani_internal" is now a global constant and can be updated in a single place if needed. Resolves #2597 since the user can specify the variable name they want within the ensures binding An important change is that the result is now a pass by reference instead, so as in the example an `&u32` instead of `u32` 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: Matias Scharager --- library/kani/src/contracts.rs | 10 ++-- library/kani_macros/src/lib.rs | 10 ++-- .../src/sysroot/contracts/bootstrap.rs | 12 ++-- .../src/sysroot/contracts/check.rs | 12 ++-- .../src/sysroot/contracts/initialize.rs | 28 +++++---- .../kani_macros/src/sysroot/contracts/mod.rs | 58 ++++++++++--------- .../src/sysroot/contracts/replace.rs | 18 +++--- .../src/sysroot/contracts/shared.rs | 4 +- rfc/src/rfcs/0009-function-contracts.md | 23 ++++---- .../arbitrary_ensures_fail.expected | 2 +- .../arbitrary_ensures_fail.rs | 2 +- .../arbitrary_ensures_pass.expected | 2 +- .../arbitrary_ensures_pass.rs | 2 +- .../arbitrary_requires_fail.rs | 2 +- .../function-contract/attribute_complain.rs | 2 +- .../attribute_no_complain.rs | 2 +- .../checking_from_external_mod.expected | 2 +- .../checking_from_external_mod.rs | 2 +- .../checking_in_impl.expected | 2 +- .../function-contract/checking_in_impl.rs | 2 +- .../function-contract/fail_on_two.expected | 4 +- .../expected/function-contract/fail_on_two.rs | 4 +- .../gcd_failure_code.expected | 4 +- .../function-contract/gcd_failure_code.rs | 2 +- .../gcd_failure_contract.expected | 4 +- .../function-contract/gcd_failure_contract.rs | 4 +- .../gcd_rec_code_fail.expected | 2 +- .../function-contract/gcd_rec_code_fail.rs | 2 +- .../gcd_rec_comparison_pass.expected | 2 +- .../gcd_rec_comparison_pass.rs | 2 +- .../gcd_rec_contract_fail.expected | 2 +- .../gcd_rec_contract_fail.rs | 2 +- .../gcd_rec_replacement_pass.rs | 2 +- .../gcd_rec_simple_pass.expected | 2 +- .../function-contract/gcd_rec_simple_pass.rs | 2 +- .../function-contract/gcd_replacement_fail.rs | 2 +- .../function-contract/gcd_replacement_pass.rs | 2 +- .../function-contract/gcd_success.expected | 2 +- .../expected/function-contract/gcd_success.rs | 2 +- .../modifies/check_only_verification.rs | 2 +- .../function-contract/modifies/expr_pass.rs | 2 +- .../modifies/expr_replace_pass.rs | 2 +- .../modifies/fail_missing_recursion_attr.rs | 2 +- .../modifies/field_replace_pass.rs | 2 +- .../modifies/global_replace_pass.rs | 2 +- .../function-contract/modifies/havoc_pass.rs | 2 +- .../modifies/havoc_pass_reordered.rs | 2 +- .../modifies/mistake_condition_return.rs | 4 +- .../modifies/unique_arguments.rs | 4 +- .../modifies/vec_pass.expected | 2 +- .../function-contract/modifies/vec_pass.rs | 2 +- .../expected/function-contract/pattern_use.rs | 2 +- .../prohibit-pointers/allowed_const_ptr.rs | 2 +- .../prohibit-pointers/allowed_mut_ref.rs | 2 +- .../allowed_mut_return_ref.rs | 2 +- .../prohibit-pointers/allowed_ref.rs | 2 +- .../prohibit-pointers/hidden.rs | 2 +- .../prohibit-pointers/plain_pointer.rs | 2 +- .../prohibit-pointers/return_pointer.rs | 2 +- .../simple_ensures_fail.expected | 4 +- .../function-contract/simple_ensures_fail.rs | 2 +- .../simple_ensures_pass.expected | 2 +- .../function-contract/simple_ensures_pass.rs | 2 +- .../function-contract/simple_replace_fail.rs | 2 +- .../function-contract/simple_replace_pass.rs | 2 +- .../type_annotation_needed.rs | 2 +- tests/std-checks/core/src/ptr.rs | 4 +- 67 files changed, 161 insertions(+), 148 deletions(-) diff --git a/library/kani/src/contracts.rs b/library/kani/src/contracts.rs index 4f963038cab9..65b9ec7c01cc 100644 --- a/library/kani/src/contracts.rs +++ b/library/kani/src/contracts.rs @@ -51,14 +51,14 @@ //! approximation of the result of division for instance could be this: //! //! ``` -//! #[ensures(result <= dividend)] +//! #[ensures(|result : &u32| *result <= dividend)] //! ``` //! //! This is called a postcondition and it also has access to the arguments and //! is expressed in regular Rust code. The same restrictions apply as did for -//! [`requires`][macro@requires]. In addition to the arguments the postcondition -//! also has access to the value returned from the function in a variable called -//! `result`. +//! [`requires`][macro@requires]. In addition to the postcondition is expressed +//! as a closure where the value returned from the function is passed to this +//! closure by reference. //! //! You may combine as many [`requires`][macro@requires] and //! [`ensures`][macro@ensures] attributes on a single function as you please. @@ -67,7 +67,7 @@ //! //! ``` //! #[kani::requires(divisor != 0)] -//! #[kani::ensures(result <= dividend)] +//! #[kani::ensures(|result : &u32| *result <= dividend)] //! fn my_div(dividend: u32, divisor: u32) -> u32 { //! dividend / divisor //! } diff --git a/library/kani_macros/src/lib.rs b/library/kani_macros/src/lib.rs index 0991730f8802..44a0ca78ea41 100644 --- a/library/kani_macros/src/lib.rs +++ b/library/kani_macros/src/lib.rs @@ -131,11 +131,11 @@ pub fn requires(attr: TokenStream, item: TokenStream) -> TokenStream { /// This is part of the function contract API, for more general information see /// the [module-level documentation](../kani/contracts/index.html). /// -/// The contents of the attribute is a condition over the input values to the -/// annotated function *and* its return value, accessible as a variable called -/// `result`. All Rust syntax is supported, even calling other functions, but -/// the computations must be side effect free, e.g. it cannot perform I/O or use -/// mutable memory. +/// The contents of the attribute is a closure that captures the input values to +/// the annotated function and the input to the function is the return value of +/// the function passed by reference. All Rust syntax is supported, even calling +/// other functions, but the computations must be side effect free, e.g. it +/// cannot perform I/O or use mutable memory. /// /// Kani requires each function that uses a contract (this attribute or /// [`requires`][macro@requires]) to have at least one designated diff --git a/library/kani_macros/src/sysroot/contracts/bootstrap.rs b/library/kani_macros/src/sysroot/contracts/bootstrap.rs index 8d930fdebda9..7c35cc469254 100644 --- a/library/kani_macros/src/sysroot/contracts/bootstrap.rs +++ b/library/kani_macros/src/sysroot/contracts/bootstrap.rs @@ -4,11 +4,14 @@ //! Special way we handle the first time we encounter a contract attribute on a //! function. -use proc_macro2::Span; +use proc_macro2::{Ident, Span}; use quote::quote; use syn::ItemFn; -use super::{helpers::*, shared::identifier_for_generated_function, ContractConditionsHandler}; +use super::{ + helpers::*, shared::identifier_for_generated_function, ContractConditionsHandler, + INTERNAL_RESULT_IDENT, +}; impl<'a> ContractConditionsHandler<'a> { /// The complex case. We are the first time a contract is handled on this function, so @@ -80,6 +83,7 @@ impl<'a> ContractConditionsHandler<'a> { (quote!(#check_fn_name), quote!(#replace_fn_name)) }; + let result = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site()); self.output.extend(quote!( #[allow(dead_code, unused_variables)] #[kanitool::is_contract_generated(recursion_wrapper)] @@ -89,9 +93,9 @@ impl<'a> ContractConditionsHandler<'a> { #call_replace(#(#args),*) } else { unsafe { REENTRY = true }; - let result = #call_check(#(#also_args),*); + let #result = #call_check(#(#also_args),*); unsafe { REENTRY = false }; - result + #result } } )); diff --git a/library/kani_macros/src/sysroot/contracts/check.rs b/library/kani_macros/src/sysroot/contracts/check.rs index 516bd187ba7f..473535e8d8fd 100644 --- a/library/kani_macros/src/sysroot/contracts/check.rs +++ b/library/kani_macros/src/sysroot/contracts/check.rs @@ -10,7 +10,7 @@ use syn::{Expr, FnArg, ItemFn, Token}; use super::{ helpers::*, shared::{make_unsafe_argument_copies, try_as_result_assign_mut}, - ContractConditionsData, ContractConditionsHandler, + ContractConditionsData, ContractConditionsHandler, INTERNAL_RESULT_IDENT, }; const WRAPPER_ARG_PREFIX: &str = "_wrapper_arg_"; @@ -46,14 +46,15 @@ impl<'a> ContractConditionsHandler<'a> { assert!(matches!( inner.pop(), Some(syn::Stmt::Expr(syn::Expr::Path(pexpr), None)) - if pexpr.path.get_ident().map_or(false, |id| id == "result") + if pexpr.path.get_ident().map_or(false, |id| id == INTERNAL_RESULT_IDENT) )); + let result = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site()); quote!( #arg_copies #(#inner)* #exec_postconditions - result + #result ) } ContractConditionsData::Modifies { attr } => { @@ -95,9 +96,10 @@ impl<'a> ContractConditionsHandler<'a> { } else { quote!(#wrapper_name) }; + let result = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site()); syn::parse_quote!( - let result : #return_type = #wrapper_call(#(#args),*); - result + let #result : #return_type = #wrapper_call(#(#args),*); + #result ) } else { self.annotated_fn.block.stmts.clone() diff --git a/library/kani_macros/src/sysroot/contracts/initialize.rs b/library/kani_macros/src/sysroot/contracts/initialize.rs index 6452ab9ace62..aee0c21bc82d 100644 --- a/library/kani_macros/src/sysroot/contracts/initialize.rs +++ b/library/kani_macros/src/sysroot/contracts/initialize.rs @@ -7,12 +7,13 @@ use std::collections::{HashMap, HashSet}; use proc_macro::{Diagnostic, TokenStream}; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; -use syn::{spanned::Spanned, visit::Visit, visit_mut::VisitMut, Expr, ItemFn, Signature}; +use quote::quote; +use syn::{spanned::Spanned, visit::Visit, visit_mut::VisitMut, Expr, ExprClosure, ItemFn}; use super::{ helpers::{chunks_by, is_token_stream_2_comma, matches_path}, ContractConditionsData, ContractConditionsHandler, ContractConditionsType, - ContractFunctionState, + ContractFunctionState, INTERNAL_RESULT_IDENT, }; impl<'a> TryFrom<&'a syn::Attribute> for ContractFunctionState { @@ -81,7 +82,11 @@ impl<'a> ContractConditionsHandler<'a> { ContractConditionsData::Requires { attr: syn::parse(attr)? } } ContractConditionsType::Ensures => { - ContractConditionsData::new_ensures(&annotated_fn.sig, syn::parse(attr)?) + let mut data: ExprClosure = syn::parse(attr)?; + let argument_names = rename_argument_occurrences(&annotated_fn.sig, &mut data); + let result: Ident = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site()); + let app: Expr = Expr::Verbatim(quote!((#data)(&#result))); + ContractConditionsData::Ensures { argument_names, attr: app } } ContractConditionsType::Modifies => { ContractConditionsData::new_modifies(attr, &mut output) @@ -92,16 +97,6 @@ impl<'a> ContractConditionsHandler<'a> { } } impl ContractConditionsData { - /// Constructs a [`Self::Ensures`] from the signature of the decorated - /// function and the contents of the decorating attribute. - /// - /// Renames the [`Ident`]s used in `attr` and stores the translation map in - /// `argument_names`. - fn new_ensures(sig: &Signature, mut attr: Expr) -> Self { - let argument_names = rename_argument_occurrences(sig, &mut attr); - ContractConditionsData::Ensures { argument_names, attr } - } - /// Constructs a [`Self::Modifies`] from the contents of the decorating attribute. /// /// Responsible for parsing the attribute. @@ -129,7 +124,10 @@ impl ContractConditionsData { /// - Creates new names for them; /// - Replaces all occurrences of those idents in `attrs` with the new names and; /// - Returns the mapping of old names to new names. -fn rename_argument_occurrences(sig: &syn::Signature, attr: &mut Expr) -> HashMap { +fn rename_argument_occurrences( + sig: &syn::Signature, + attr: &mut ExprClosure, +) -> HashMap { let mut arg_ident_collector = ArgumentIdentCollector::new(); arg_ident_collector.visit_signature(&sig); @@ -144,7 +142,7 @@ fn rename_argument_occurrences(sig: &syn::Signature, attr: &mut Expr) -> HashMap .collect::>(); let mut ident_rewriter = Renamer(&arg_idents); - ident_rewriter.visit_expr_mut(attr); + ident_rewriter.visit_expr_closure_mut(attr); arg_idents } diff --git a/library/kani_macros/src/sysroot/contracts/mod.rs b/library/kani_macros/src/sysroot/contracts/mod.rs index 02d2b98eb8db..bef6f8d921bc 100644 --- a/library/kani_macros/src/sysroot/contracts/mod.rs +++ b/library/kani_macros/src/sysroot/contracts/mod.rs @@ -159,9 +159,9 @@ //! call_replace(fn args...) //! } else { //! unsafe { reentry = true }; -//! let result = call_check(fn args...); +//! let result_kani_internal = call_check(fn args...); //! unsafe { reentry = false }; -//! result +//! result_kani_internal //! } //! } //! ``` @@ -173,7 +173,7 @@ //! //! ``` //! #[kani::requires(divisor != 0)] -//! #[kani::ensures(result <= dividend)] +//! #[kani::ensures(|result : &u32| *result <= dividend)] //! fn div(dividend: u32, divisor: u32) -> u32 { //! dividend / divisor //! } @@ -186,31 +186,35 @@ //! #[kanitool::replaced_with = "div_replace_965916"] //! fn div(dividend: u32, divisor: u32) -> u32 { dividend / divisor } //! -//! #[allow(dead_code)] -//! #[allow(unused_variables)] -//! #[kanitool::is_contract_generated(check)] -//! fn div_check_965916(dividend: u32, divisor: u32) -> u32 { -//! let dividend_renamed = kani::internal::untracked_deref(÷nd); -//! let divisor_renamed = kani::internal::untracked_deref(&divisor); -//! let result = { kani::assume(divisor != 0); { dividend / divisor } }; -//! kani::assert(result <= dividend_renamed, "result <= dividend"); +//! #[allow(dead_code, unused_variables)] +//! #[kanitool :: is_contract_generated(check)] fn +//! div_check_b97df2(dividend : u32, divisor : u32) -> u32 +//! { +//! let dividend_renamed = kani::internal::untracked_deref(& dividend); +//! let divisor_renamed = kani::internal::untracked_deref(& divisor); +//! kani::assume(divisor != 0); +//! let result_kani_internal : u32 = div_wrapper_b97df2(dividend, divisor); +//! kani::assert( +//! (| result : & u32 | *result <= dividend_renamed)(& result_kani_internal), +//! stringify!(|result : &u32| *result <= dividend)); //! std::mem::forget(dividend_renamed); //! std::mem::forget(divisor_renamed); -//! result +//! result_kani_internal //! } //! -//! #[allow(dead_code)] -//! #[allow(unused_variables)] -//! #[kanitool::is_contract_generated(replace)] -//! fn div_replace_965916(dividend: u32, divisor: u32) -> u32 { -//! kani::assert(divisor != 0, "divisor != 0"); -//! let dividend_renamed = kani::internal::untracked_deref(÷nd); -//! let divisor_renamed = kani::internal::untracked_deref(&divisor); -//! let result = kani::any(); -//! kani::assume(result <= dividend_renamed, "result <= dividend"); -//! std::mem::forget(dividend_renamed); +//! #[allow(dead_code, unused_variables)] +//! #[kanitool :: is_contract_generated(replace)] fn +//! div_replace_b97df2(dividend : u32, divisor : u32) -> u32 +//! { +//! let divisor_renamed = kani::internal::untracked_deref(& divisor); +//! let dividend_renamed = kani::internal::untracked_deref(& dividend); +//! kani::assert(divisor != 0, stringify! (divisor != 0)); +//! let result_kani_internal : u32 = kani::any_modifies(); +//! kani::assume( +//! (|result : & u32| *result <= dividend_renamed)(&result_kani_internal)); //! std::mem::forget(divisor_renamed); -//! result +//! std::mem::forget(dividend_renamed); +//! result_kani_internal //! } //! //! #[allow(dead_code)] @@ -220,12 +224,12 @@ //! static mut REENTRY: bool = false; //! //! if unsafe { REENTRY } { -//! div_replace_965916(dividend, divisor) +//! div_replace_b97df2(dividend, divisor) //! } else { //! unsafe { reentry = true }; -//! let result = div_check_965916(dividend, divisor); +//! let result_kani_internal = div_check_b97df2(dividend, divisor); //! unsafe { reentry = false }; -//! result +//! result_kani_internal //! } //! } //! ``` @@ -243,6 +247,8 @@ mod initialize; mod replace; mod shared; +const INTERNAL_RESULT_IDENT: &str = "result_kani_internal"; + pub fn requires(attr: TokenStream, item: TokenStream) -> TokenStream { contract_main(attr, item, ContractConditionsType::Requires) } diff --git a/library/kani_macros/src/sysroot/contracts/replace.rs b/library/kani_macros/src/sysroot/contracts/replace.rs index 28e401c22d48..546568dc8a1d 100644 --- a/library/kani_macros/src/sysroot/contracts/replace.rs +++ b/library/kani_macros/src/sysroot/contracts/replace.rs @@ -3,13 +3,13 @@ //! Logic used for generating the code that replaces a function with its contract. -use proc_macro2::{Ident, TokenStream as TokenStream2}; +use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::quote; use super::{ helpers::*, shared::{make_unsafe_argument_copies, try_as_result_assign}, - ContractConditionsData, ContractConditionsHandler, + ContractConditionsData, ContractConditionsHandler, INTERNAL_RESULT_IDENT, }; impl<'a> ContractConditionsHandler<'a> { @@ -39,7 +39,8 @@ impl<'a> ContractConditionsHandler<'a> { fn ensure_bootstrapped_replace_body(&self) -> (Vec, Vec) { if self.is_first_emit() { let return_type = return_type_to_type(&self.annotated_fn.sig.output); - (vec![syn::parse_quote!(let result : #return_type = kani::any_modifies();)], vec![]) + let result = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site()); + (vec![syn::parse_quote!(let #result : #return_type = kani::any_modifies();)], vec![]) } else { let stmts = &self.annotated_fn.block.stmts; let idx = stmts @@ -70,30 +71,33 @@ impl<'a> ContractConditionsHandler<'a> { match &self.condition_type { ContractConditionsData::Requires { attr } => { let Self { attr_copy, .. } = self; + let result = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site()); quote!( kani::assert(#attr, stringify!(#attr_copy)); #(#before)* #(#after)* - result + #result ) } ContractConditionsData::Ensures { attr, argument_names } => { let (arg_copies, copy_clean) = make_unsafe_argument_copies(&argument_names); + let result = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site()); quote!( #arg_copies #(#before)* #(#after)* kani::assume(#attr); #copy_clean - result + #result ) } ContractConditionsData::Modifies { attr } => { + let result = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site()); quote!( #(#before)* #(*unsafe { kani::internal::Pointer::assignable(#attr) } = kani::any_modifies();)* #(#after)* - result + #result ) } } @@ -126,7 +130,7 @@ impl<'a> ContractConditionsHandler<'a> { } } -/// Is this statement `let result : <...> = kani::any_modifies();`. +/// Is this statement `let result_kani_internal : <...> = kani::any_modifies();`. fn is_replace_return_havoc(stmt: &syn::Stmt) -> bool { let Some(syn::LocalInit { diverge: None, expr: e, .. }) = try_as_result_assign(stmt) else { return false; diff --git a/library/kani_macros/src/sysroot/contracts/shared.rs b/library/kani_macros/src/sysroot/contracts/shared.rs index bf5a8000da50..b0f5804824ad 100644 --- a/library/kani_macros/src/sysroot/contracts/shared.rs +++ b/library/kani_macros/src/sysroot/contracts/shared.rs @@ -13,7 +13,7 @@ use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; use syn::Attribute; -use super::{ContractConditionsHandler, ContractFunctionState}; +use super::{ContractConditionsHandler, ContractFunctionState, INTERNAL_RESULT_IDENT}; impl ContractFunctionState { /// Do we need to emit the `is_contract_generated` tag attribute on the @@ -114,7 +114,7 @@ macro_rules! try_as_result_assign_pat { ident: result_ident, subpat: None, .. - }) if result_ident == "result" + }) if result_ident == INTERNAL_RESULT_IDENT ) => init.$convert(), _ => None, } diff --git a/rfc/src/rfcs/0009-function-contracts.md b/rfc/src/rfcs/0009-function-contracts.md index dae805a7db72..deeeca66ab7a 100644 --- a/rfc/src/rfcs/0009-function-contracts.md +++ b/rfc/src/rfcs/0009-function-contracts.md @@ -65,7 +65,7 @@ fn my_div(dividend: u32, divisor: u32) -> u32 { ```rs #[kani::requires(divisor != 0)] - #[kani::ensures(result <= dividend)] + #[kani::ensures(|result : &u32| *result <= dividend)] fn my_div(dividend: u32, divisor: u32) -> u32 { dividend / divisor } @@ -79,8 +79,7 @@ fn my_div(dividend: u32, divisor: u32) -> u32 { Conditions in contracts are Rust expressions which reference the function arguments and, in case of `ensures`, the return value of the - function. The return value is a special variable called `result` (see [open - questions](#open-questions) on a discussion about (re)naming). Syntactically + function. The return value is passed into the ensures closure statement by reference. Syntactically Kani supports any Rust expression, including function calls, defining types etc. However they must be side-effect free (see also side effects [here](#changes-to-other-components)) or Kani will throw a compile error. @@ -132,8 +131,8 @@ fn my_div(dividend: u32, divisor: u32) -> u32 { let dividend = kani::any(); let divisor = kani::any(); kani::assume(divisor != 0); // requires - let result = my_div(dividend, divisor); - kani::assert(result <= dividend); // ensures + let result_kani_internal = my_div(dividend, divisor); + kani::assert((|result : &u32| *result <= dividend)(result_kani_internal)); // ensures } ``` @@ -306,7 +305,7 @@ available to `ensures`. It is used like so: ```rs impl Vec { - #[kani::ensures(old(self.is_empty()) || result.is_some())] + #[kani::ensures(|result : &Option| old(self.is_empty()) || result.is_some())] fn pop(&mut self) -> Option { ... } @@ -324,8 +323,8 @@ Note also that `old` is syntax, not a function and implemented as an extraction and lifting during code generation. It can reference e.g. `pop`'s arguments but not local variables. Compare the following -**Invalid ❌:** `#[kani::ensures({ let x = self.is_empty(); old(x) } || result.is_some())]`
-**Valid ✅:** `#[kani::ensures(old({ let x = self.is_empty(); x }) || result.is_some())]` +**Invalid ❌:** `#[kani::ensures(|result : &Option| { let x = self.is_empty(); old(x) } || result.is_some())]`
+**Valid ✅:** `#[kani::ensures(|result : &Option| old({ let x = self.is_empty(); x }) || result.is_some())]` And it will only be recognized as `old(...)`, not as `let old1 = old; old1(...)` etc. @@ -410,7 +409,7 @@ the below example: ```rs impl Vec { - #[kani::ensures(self.is_empty() || self.len() == old(self.len()) - 1)] + #[kani::ensures(|result : &Option| self.is_empty() || self.len() == old(self.len()) - 1)] fn pop(&mut self) -> Option { ... } @@ -425,8 +424,8 @@ following: impl Vec { fn check_pop(&mut self) -> Option { let old_1 = self.len(); - let result = Self::pop(self); - kani::assert(self.is_empty() || self.len() == old_1 - 1) + let result_kani_internal = Self::pop(self); + kani::assert((|result : &Option| self.is_empty() || self.len() == old_1 - 1)(result_kani_internal)) } } ``` @@ -450,7 +449,7 @@ sensible contract for it might look as follows: ```rs impl Vec { - #[ensures(self.len() == result.0.len() + result.1.len())] + #[ensures(|result : &(&mut [T], &mut [T])| self.len() == result.0.len() + result.1.len())] fn split_at_mut(&mut self, i: usize) -> (&mut [T], &mut [T]) { ... } diff --git a/tests/expected/function-contract/arbitrary_ensures_fail.expected b/tests/expected/function-contract/arbitrary_ensures_fail.expected index 0a59d2cea5eb..4b70f8364e05 100644 --- a/tests/expected/function-contract/arbitrary_ensures_fail.expected +++ b/tests/expected/function-contract/arbitrary_ensures_fail.expected @@ -1,6 +1,6 @@ assertion\ - Status: FAILURE\ -- Description: "result == x"\ +- Description: "|result : &u32| *result == x"\ in function max VERIFICATION:- FAILED diff --git a/tests/expected/function-contract/arbitrary_ensures_fail.rs b/tests/expected/function-contract/arbitrary_ensures_fail.rs index 91638b1cc037..8d66402180d7 100644 --- a/tests/expected/function-contract/arbitrary_ensures_fail.rs +++ b/tests/expected/function-contract/arbitrary_ensures_fail.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zfunction-contracts -#[kani::ensures(result == x)] +#[kani::ensures(|result : &u32| *result == x)] fn max(x: u32, y: u32) -> u32 { if x > y { x } else { y } } diff --git a/tests/expected/function-contract/arbitrary_ensures_pass.expected b/tests/expected/function-contract/arbitrary_ensures_pass.expected index 85619fa84c22..9eee213789b9 100644 --- a/tests/expected/function-contract/arbitrary_ensures_pass.expected +++ b/tests/expected/function-contract/arbitrary_ensures_pass.expected @@ -1,6 +1,6 @@ assertion\ - Status: SUCCESS\ -- Description: "result == x || result == y"\ +- Description: "|result : &u32| *result == x || *result == y"\ in function max VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/arbitrary_ensures_pass.rs b/tests/expected/function-contract/arbitrary_ensures_pass.rs index df8d3a2361fb..86302f705925 100644 --- a/tests/expected/function-contract/arbitrary_ensures_pass.rs +++ b/tests/expected/function-contract/arbitrary_ensures_pass.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zfunction-contracts -#[kani::ensures(result == x || result == y)] +#[kani::ensures(|result : &u32| *result == x || *result == y)] fn max(x: u32, y: u32) -> u32 { if x > y { x } else { y } } diff --git a/tests/expected/function-contract/arbitrary_requires_fail.rs b/tests/expected/function-contract/arbitrary_requires_fail.rs index d052e19b0335..78ab1b77bf10 100644 --- a/tests/expected/function-contract/arbitrary_requires_fail.rs +++ b/tests/expected/function-contract/arbitrary_requires_fail.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zfunction-contracts -#[kani::ensures(result <= dividend)] +#[kani::ensures(|result : &u32| *result <= dividend)] fn div(dividend: u32, divisor: u32) -> u32 { dividend / divisor } diff --git a/tests/expected/function-contract/attribute_complain.rs b/tests/expected/function-contract/attribute_complain.rs index f16e975c2001..8532889e1eca 100644 --- a/tests/expected/function-contract/attribute_complain.rs +++ b/tests/expected/function-contract/attribute_complain.rs @@ -1,7 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -#[kani::ensures(true)] +#[kani::ensures(|result| true)] fn always() {} #[kani::proof_for_contract(always)] diff --git a/tests/expected/function-contract/attribute_no_complain.rs b/tests/expected/function-contract/attribute_no_complain.rs index bcf1f0cadafd..b3004b24f4d4 100644 --- a/tests/expected/function-contract/attribute_no_complain.rs +++ b/tests/expected/function-contract/attribute_no_complain.rs @@ -1,7 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -#[kani::ensures(true)] +#[kani::ensures(|result| true)] fn always() {} #[kani::proof] diff --git a/tests/expected/function-contract/checking_from_external_mod.expected b/tests/expected/function-contract/checking_from_external_mod.expected index c31b5c389fc8..e181e6b2ad17 100644 --- a/tests/expected/function-contract/checking_from_external_mod.expected +++ b/tests/expected/function-contract/checking_from_external_mod.expected @@ -1,5 +1,5 @@ - Status: SUCCESS\ -- Description: "(result == x) | (result == y)"\ +- Description: "|result : &u32| (*result == x) | (*result == y)"\ in function max VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/checking_from_external_mod.rs b/tests/expected/function-contract/checking_from_external_mod.rs index 43d1551f9aef..ea01cfadd511 100644 --- a/tests/expected/function-contract/checking_from_external_mod.rs +++ b/tests/expected/function-contract/checking_from_external_mod.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zfunction-contracts -#[kani::ensures((result == x) | (result == y))] +#[kani::ensures(|result : &u32| (*result == x) | (*result == y))] fn max(x: u32, y: u32) -> u32 { if x > y { x } else { y } } diff --git a/tests/expected/function-contract/checking_in_impl.expected b/tests/expected/function-contract/checking_in_impl.expected index d5a390be8425..cfe84c06fc85 100644 --- a/tests/expected/function-contract/checking_in_impl.expected +++ b/tests/expected/function-contract/checking_in_impl.expected @@ -1,5 +1,5 @@ - Status: SUCCESS\ -- Description: "(result == self) | (result == y)"\ +- Description: "|result : &WrappedInt| (*result == self) | (*result == y)"\ in function WrappedInt::max VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/checking_in_impl.rs b/tests/expected/function-contract/checking_in_impl.rs index 7d5c0506d9df..8721e7fffb7f 100644 --- a/tests/expected/function-contract/checking_in_impl.rs +++ b/tests/expected/function-contract/checking_in_impl.rs @@ -8,7 +8,7 @@ extern crate kani; struct WrappedInt(u32); impl WrappedInt { - #[kani::ensures((result == self) | (result == y))] + #[kani::ensures(|result : &WrappedInt| (*result == self) | (*result == y))] fn max(self, y: WrappedInt) -> WrappedInt { Self(if self.0 > y.0 { self.0 } else { y.0 }) } diff --git a/tests/expected/function-contract/fail_on_two.expected b/tests/expected/function-contract/fail_on_two.expected index 32fc28012d3a..79cc3d572af0 100644 --- a/tests/expected/function-contract/fail_on_two.expected +++ b/tests/expected/function-contract/fail_on_two.expected @@ -7,6 +7,6 @@ Failed Checks: internal error: entered unreachable code: fail on two assertion\ - Status: FAILURE\ -- Description: "result < 3" +- Description: "|result : &i32| *result < 3" -Failed Checks: result < 3 +Failed Checks: |result : &i32| *result < 3 diff --git a/tests/expected/function-contract/fail_on_two.rs b/tests/expected/function-contract/fail_on_two.rs index 605065449194..32ad74c31cd9 100644 --- a/tests/expected/function-contract/fail_on_two.rs +++ b/tests/expected/function-contract/fail_on_two.rs @@ -16,7 +16,7 @@ //! once because the postcondition is violated). If instead the hypothesis (e.g. //! contract replacement) is used we'd expect the verification to succeed. -#[kani::ensures(result < 3)] +#[kani::ensures(|result : &i32| *result < 3)] #[kani::recursion] fn fail_on_two(i: i32) -> i32 { match i { @@ -32,7 +32,7 @@ fn harness() { let _ = fail_on_two(first); } -#[kani::ensures(result < 3)] +#[kani::ensures(|result : &i32| *result < 3)] #[kani::recursion] fn fail_on_two_in_postcondition(i: i32) -> i32 { let j = i + 1; diff --git a/tests/expected/function-contract/gcd_failure_code.expected b/tests/expected/function-contract/gcd_failure_code.expected index c7fe5a721abb..ca21817c8329 100644 --- a/tests/expected/function-contract/gcd_failure_code.expected +++ b/tests/expected/function-contract/gcd_failure_code.expected @@ -1,8 +1,8 @@ assertion\ - Status: FAILURE\ -- Description: "result != 0 && x % result == 0 && y % result == 0"\ +- Description: "|result : &T| *result != 0 && x % *result == 0 && y % *result == 0"\ in function gcd -Failed Checks: result != 0 && x % result == 0 && y % result == 0 +Failed Checks: |result : &T| *result != 0 && x % *result == 0 && y % *result == 0 VERIFICATION:- FAILED diff --git a/tests/expected/function-contract/gcd_failure_code.rs b/tests/expected/function-contract/gcd_failure_code.rs index f76e04b75fee..118cc3c930e5 100644 --- a/tests/expected/function-contract/gcd_failure_code.rs +++ b/tests/expected/function-contract/gcd_failure_code.rs @@ -5,7 +5,7 @@ type T = u8; /// Euclid's algorithm for calculating the GCD of two numbers #[kani::requires(x != 0 && y != 0)] -#[kani::ensures(result != 0 && x % result == 0 && y % result == 0)] +#[kani::ensures(|result : &T| *result != 0 && x % *result == 0 && y % *result == 0)] fn gcd(x: T, y: T) -> T { let mut max = x; let mut min = y; diff --git a/tests/expected/function-contract/gcd_failure_contract.expected b/tests/expected/function-contract/gcd_failure_contract.expected index aeadfb563ab9..4bb02ab36f49 100644 --- a/tests/expected/function-contract/gcd_failure_contract.expected +++ b/tests/expected/function-contract/gcd_failure_contract.expected @@ -1,8 +1,8 @@ assertion\ - Status: FAILURE\ -- Description: "result != 0 && x % result == 1 && y % result == 0"\ +- Description: "|result : &T| *result != 0 && x % *result == 1 && y % *result == 0"\ in function gcd\ -Failed Checks: result != 0 && x % result == 1 && y % result == 0 +Failed Checks: |result : &T| *result != 0 && x % *result == 1 && y % *result == 0 VERIFICATION:- FAILED diff --git a/tests/expected/function-contract/gcd_failure_contract.rs b/tests/expected/function-contract/gcd_failure_contract.rs index 6b835466c5a0..d4eea8ceb98c 100644 --- a/tests/expected/function-contract/gcd_failure_contract.rs +++ b/tests/expected/function-contract/gcd_failure_contract.rs @@ -5,8 +5,8 @@ type T = u8; /// Euclid's algorithm for calculating the GCD of two numbers #[kani::requires(x != 0 && y != 0)] -// Changed `0` to `1` in `x % result == 0` to mess with this contract -#[kani::ensures(result != 0 && x % result == 1 && y % result == 0)] +// Changed `0` to `1` in `x % *result == 0` to mess with this contract +#[kani::ensures(|result : &T| *result != 0 && x % *result == 1 && y % *result == 0)] fn gcd(x: T, y: T) -> T { let mut max = x; let mut min = y; diff --git a/tests/expected/function-contract/gcd_rec_code_fail.expected b/tests/expected/function-contract/gcd_rec_code_fail.expected index 80dbaadbf4c7..863392098be4 100644 --- a/tests/expected/function-contract/gcd_rec_code_fail.expected +++ b/tests/expected/function-contract/gcd_rec_code_fail.expected @@ -1,3 +1,3 @@ -Failed Checks: result != 0 && x % result == 0 && y % result == 0 +Failed Checks: |result : &T| *result != 0 && x % *result == 0 && y % *result == 0 VERIFICATION:- FAILED diff --git a/tests/expected/function-contract/gcd_rec_code_fail.rs b/tests/expected/function-contract/gcd_rec_code_fail.rs index da38318e2a66..90260f56d4dc 100644 --- a/tests/expected/function-contract/gcd_rec_code_fail.rs +++ b/tests/expected/function-contract/gcd_rec_code_fail.rs @@ -5,7 +5,7 @@ type T = u8; /// Euclid's algorithm for calculating the GCD of two numbers #[kani::requires(x != 0 && y != 0)] -#[kani::ensures(result != 0 && x % result == 0 && y % result == 0)] +#[kani::ensures(|result : &T| *result != 0 && x % *result == 0 && y % *result == 0)] #[kani::recursion] fn gcd(x: T, y: T) -> T { let mut max = x; diff --git a/tests/expected/function-contract/gcd_rec_comparison_pass.expected b/tests/expected/function-contract/gcd_rec_comparison_pass.expected index da647dfd40aa..0556bfc50204 100644 --- a/tests/expected/function-contract/gcd_rec_comparison_pass.expected +++ b/tests/expected/function-contract/gcd_rec_comparison_pass.expected @@ -4,6 +4,6 @@ assertion\ assertion\ - Status: SUCCESS\ -- Description: "result != 0 && x % result == 0 && y % result == 0" +- Description: "|result : &T| *result != 0 && x % *result == 0 && y % *result == 0" VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/gcd_rec_comparison_pass.rs b/tests/expected/function-contract/gcd_rec_comparison_pass.rs index 1db9691ae8e4..ae5fbbe9b60f 100644 --- a/tests/expected/function-contract/gcd_rec_comparison_pass.rs +++ b/tests/expected/function-contract/gcd_rec_comparison_pass.rs @@ -5,7 +5,7 @@ type T = u8; /// Euclid's algorithm for calculating the GCD of two numbers #[kani::requires(x != 0 && y != 0)] -#[kani::ensures(result != 0 && x % result == 0 && y % result == 0)] +#[kani::ensures(|result : &T| *result != 0 && x % *result == 0 && y % *result == 0)] #[kani::recursion] fn gcd(x: T, y: T) -> T { let mut max = x; diff --git a/tests/expected/function-contract/gcd_rec_contract_fail.expected b/tests/expected/function-contract/gcd_rec_contract_fail.expected index bfb470192a39..6cc0354ca89a 100644 --- a/tests/expected/function-contract/gcd_rec_contract_fail.expected +++ b/tests/expected/function-contract/gcd_rec_contract_fail.expected @@ -1,3 +1,3 @@ -Failed Checks: result != 0 && x % result == 1 && y % result == 0 +Failed Checks: |result : &T| *result != 0 && x % *result == 1 && y % *result == 0 VERIFICATION:- FAILED diff --git a/tests/expected/function-contract/gcd_rec_contract_fail.rs b/tests/expected/function-contract/gcd_rec_contract_fail.rs index 3fe34cb2effc..2306db0e9353 100644 --- a/tests/expected/function-contract/gcd_rec_contract_fail.rs +++ b/tests/expected/function-contract/gcd_rec_contract_fail.rs @@ -6,7 +6,7 @@ type T = u8; /// Euclid's algorithm for calculating the GCD of two numbers #[kani::requires(x != 0 && y != 0)] // Changed `0` to `1` in `x % result == 0` to mess with this contract -#[kani::ensures(result != 0 && x % result == 1 && y % result == 0)] +#[kani::ensures(|result : &T| *result != 0 && x % *result == 1 && y % *result == 0)] fn gcd(x: T, y: T) -> T { let mut max = x; let mut min = y; diff --git a/tests/expected/function-contract/gcd_rec_replacement_pass.rs b/tests/expected/function-contract/gcd_rec_replacement_pass.rs index d8a5bbd234ed..0fd04b0076ba 100644 --- a/tests/expected/function-contract/gcd_rec_replacement_pass.rs +++ b/tests/expected/function-contract/gcd_rec_replacement_pass.rs @@ -5,7 +5,7 @@ type T = u8; /// Euclid's algorithm for calculating the GCD of two numbers #[kani::requires(x != 0 && y != 0)] -#[kani::ensures(result != 0 && x % result == 0 && y % result == 0)] +#[kani::ensures(|result : &T| *result != 0 && x % *result == 0 && y % *result == 0)] fn gcd(x: T, y: T) -> T { let mut max = x; let mut min = y; diff --git a/tests/expected/function-contract/gcd_rec_simple_pass.expected b/tests/expected/function-contract/gcd_rec_simple_pass.expected index da647dfd40aa..0556bfc50204 100644 --- a/tests/expected/function-contract/gcd_rec_simple_pass.expected +++ b/tests/expected/function-contract/gcd_rec_simple_pass.expected @@ -4,6 +4,6 @@ assertion\ assertion\ - Status: SUCCESS\ -- Description: "result != 0 && x % result == 0 && y % result == 0" +- Description: "|result : &T| *result != 0 && x % *result == 0 && y % *result == 0" VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/gcd_rec_simple_pass.rs b/tests/expected/function-contract/gcd_rec_simple_pass.rs index 464e3d8bbea1..626a48aa5ec2 100644 --- a/tests/expected/function-contract/gcd_rec_simple_pass.rs +++ b/tests/expected/function-contract/gcd_rec_simple_pass.rs @@ -5,7 +5,7 @@ type T = u8; /// Euclid's algorithm for calculating the GCD of two numbers #[kani::requires(x != 0 && y != 0)] -#[kani::ensures(result != 0 && x % result == 0 && y % result == 0)] +#[kani::ensures(|result : &T| *result != 0 && x % *result == 0 && y % *result == 0)] #[kani::recursion] fn gcd(x: T, y: T) -> T { let mut max = x; diff --git a/tests/expected/function-contract/gcd_replacement_fail.rs b/tests/expected/function-contract/gcd_replacement_fail.rs index 8bd59c5c14fe..0186a369c3b2 100644 --- a/tests/expected/function-contract/gcd_replacement_fail.rs +++ b/tests/expected/function-contract/gcd_replacement_fail.rs @@ -5,7 +5,7 @@ type T = u8; /// Euclid's algorithm for calculating the GCD of two numbers #[kani::requires(x != 0 && y != 0)] -#[kani::ensures(result != 0 && x % result == 0)] +#[kani::ensures(|result : &T| *result != 0 && x % *result == 0)] fn gcd(x: T, y: T) -> T { let mut max = x; let mut min = y; diff --git a/tests/expected/function-contract/gcd_replacement_pass.rs b/tests/expected/function-contract/gcd_replacement_pass.rs index 9827dd3a1512..b45241198737 100644 --- a/tests/expected/function-contract/gcd_replacement_pass.rs +++ b/tests/expected/function-contract/gcd_replacement_pass.rs @@ -5,7 +5,7 @@ type T = u8; /// Euclid's algorithm for calculating the GCD of two numbers #[kani::requires(x != 0 && y != 0)] -#[kani::ensures(result != 0 && x % result == 0 && y % result == 0)] +#[kani::ensures(|result : &T| *result != 0 && x % *result == 0 && y % *result == 0)] fn gcd(x: T, y: T) -> T { let mut max = x; let mut min = y; diff --git a/tests/expected/function-contract/gcd_success.expected b/tests/expected/function-contract/gcd_success.expected index 73b531d424b9..e5885dd11179 100644 --- a/tests/expected/function-contract/gcd_success.expected +++ b/tests/expected/function-contract/gcd_success.expected @@ -1,6 +1,6 @@ assertion\ - Status: SUCCESS\ -- Description: "result != 0 && x % result == 0 && y % result == 0"\ +- Description: "|result : &T| *result != 0 && x % *result == 0 && y % *result == 0"\ in function gcd VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/gcd_success.rs b/tests/expected/function-contract/gcd_success.rs index d3a2c75b7d20..3b983a230b60 100644 --- a/tests/expected/function-contract/gcd_success.rs +++ b/tests/expected/function-contract/gcd_success.rs @@ -5,7 +5,7 @@ type T = u8; /// Euclid's algorithm for calculating the GCD of two numbers #[kani::requires(x != 0 && y != 0)] -#[kani::ensures(result != 0 && x % result == 0 && y % result == 0)] +#[kani::ensures(|result : &T| *result != 0 && x % *result == 0 && y % *result == 0)] fn gcd(x: T, y: T) -> T { let mut max = x; let mut min = y; diff --git a/tests/expected/function-contract/modifies/check_only_verification.rs b/tests/expected/function-contract/modifies/check_only_verification.rs index 2f247a718ae9..9f3fb3614733 100644 --- a/tests/expected/function-contract/modifies/check_only_verification.rs +++ b/tests/expected/function-contract/modifies/check_only_verification.rs @@ -7,7 +7,7 @@ #[kani::requires(*ptr < 100)] #[kani::modifies(ptr)] -#[kani::ensures(result == 100)] +#[kani::ensures(|result : &u32| *result == 100)] fn modify(ptr: &mut u32) -> u32 { *ptr += 1; *ptr diff --git a/tests/expected/function-contract/modifies/expr_pass.rs b/tests/expected/function-contract/modifies/expr_pass.rs index 65e561df48a2..9a6f46a6aaaa 100644 --- a/tests/expected/function-contract/modifies/expr_pass.rs +++ b/tests/expected/function-contract/modifies/expr_pass.rs @@ -7,7 +7,7 @@ #[kani::requires(**ptr < 100)] #[kani::modifies(ptr.as_ref())] -#[kani::ensures(**ptr < 101)] +#[kani::ensures(|result| **ptr < 101)] fn modify(ptr: &mut Box) { *ptr.as_mut() += 1; } diff --git a/tests/expected/function-contract/modifies/expr_replace_pass.rs b/tests/expected/function-contract/modifies/expr_replace_pass.rs index 8be1ef2cbaee..779280dd46b4 100644 --- a/tests/expected/function-contract/modifies/expr_replace_pass.rs +++ b/tests/expected/function-contract/modifies/expr_replace_pass.rs @@ -4,7 +4,7 @@ #[kani::requires(**ptr < 100)] #[kani::modifies(ptr.as_ref())] -#[kani::ensures(**ptr == prior + 1)] +#[kani::ensures(|result| **ptr == prior + 1)] fn modify(ptr: &mut Box, prior: u32) { *ptr.as_mut() += 1; } diff --git a/tests/expected/function-contract/modifies/fail_missing_recursion_attr.rs b/tests/expected/function-contract/modifies/fail_missing_recursion_attr.rs index 644b641e731e..5e9b96e021f7 100644 --- a/tests/expected/function-contract/modifies/fail_missing_recursion_attr.rs +++ b/tests/expected/function-contract/modifies/fail_missing_recursion_attr.rs @@ -5,7 +5,7 @@ //! Check whether Kani fails if users forgot to annotate recursive //! functions with `#[kani::recursion]` when using function contracts. -#[kani::ensures(result < 3)] +#[kani::ensures(|result : &i32| *result < 3)] fn fail_on_two(i: i32) -> i32 { match i { 0 => fail_on_two(i + 1), diff --git a/tests/expected/function-contract/modifies/field_replace_pass.rs b/tests/expected/function-contract/modifies/field_replace_pass.rs index a6ae4ea4a7e0..1bbbaf31121a 100644 --- a/tests/expected/function-contract/modifies/field_replace_pass.rs +++ b/tests/expected/function-contract/modifies/field_replace_pass.rs @@ -8,7 +8,7 @@ struct S<'a> { } #[kani::requires(*s.target < 100)] #[kani::modifies(s.target)] -#[kani::ensures(*s.target == prior + 1)] +#[kani::ensures(|result| *s.target == prior + 1)] fn modify(s: S, prior: u32) { *s.target += 1; } diff --git a/tests/expected/function-contract/modifies/global_replace_pass.rs b/tests/expected/function-contract/modifies/global_replace_pass.rs index 333348f25ce4..69d36bd96033 100644 --- a/tests/expected/function-contract/modifies/global_replace_pass.rs +++ b/tests/expected/function-contract/modifies/global_replace_pass.rs @@ -5,7 +5,7 @@ static mut PTR: u32 = 0; #[kani::modifies(&mut PTR)] -#[kani::ensures(PTR == src)] +#[kani::ensures(|result| PTR == src)] unsafe fn modify(src: u32) { PTR = src; } diff --git a/tests/expected/function-contract/modifies/havoc_pass.rs b/tests/expected/function-contract/modifies/havoc_pass.rs index aa5bcada1a26..ebdd139727d3 100644 --- a/tests/expected/function-contract/modifies/havoc_pass.rs +++ b/tests/expected/function-contract/modifies/havoc_pass.rs @@ -3,7 +3,7 @@ // kani-flags: -Zfunction-contracts #[kani::modifies(dst)] -#[kani::ensures(*dst == src)] +#[kani::ensures(|result| *dst == src)] fn copy(src: u32, dst: &mut u32) { *dst = src; } diff --git a/tests/expected/function-contract/modifies/havoc_pass_reordered.rs b/tests/expected/function-contract/modifies/havoc_pass_reordered.rs index dc5f370179e5..43581ee677a6 100644 --- a/tests/expected/function-contract/modifies/havoc_pass_reordered.rs +++ b/tests/expected/function-contract/modifies/havoc_pass_reordered.rs @@ -3,7 +3,7 @@ // kani-flags: -Zfunction-contracts // These two are reordered in comparison to `havoc_pass` and we expect the test case to pass still -#[kani::ensures(*dst == src)] +#[kani::ensures(|result| *dst == src)] #[kani::modifies(dst)] fn copy(src: u32, dst: &mut u32) { *dst = src; diff --git a/tests/expected/function-contract/modifies/mistake_condition_return.rs b/tests/expected/function-contract/modifies/mistake_condition_return.rs index 484819af4248..b2d6ad6a8a55 100644 --- a/tests/expected/function-contract/modifies/mistake_condition_return.rs +++ b/tests/expected/function-contract/modifies/mistake_condition_return.rs @@ -16,8 +16,8 @@ // However, contract instrumentation will create a separate non-deterministic // value to return in this function that can only be constrained by using the // `result` keyword. Thus the correct condition would be -// `#[kani::ensures(result == 100)]`. -#[kani::ensures(*ptr == 100)] +// `#[kani::ensures(|result| result == 100)]`. +#[kani::ensures(|result| *ptr == 100)] fn modify(ptr: &mut u32) -> u32 { *ptr += 1; *ptr diff --git a/tests/expected/function-contract/modifies/unique_arguments.rs b/tests/expected/function-contract/modifies/unique_arguments.rs index 396ba4c5b036..ea4502bde2ad 100644 --- a/tests/expected/function-contract/modifies/unique_arguments.rs +++ b/tests/expected/function-contract/modifies/unique_arguments.rs @@ -4,8 +4,8 @@ #[kani::modifies(a)] #[kani::modifies(b)] -#[kani::ensures(*a == 1)] -#[kani::ensures(*b == 2)] +#[kani::ensures(|result| *a == 1)] +#[kani::ensures(|result| *b == 2)] fn two_pointers(a: &mut u32, b: &mut u32) { *a = 1; *b = 2; diff --git a/tests/expected/function-contract/modifies/vec_pass.expected b/tests/expected/function-contract/modifies/vec_pass.expected index d31486f2dcc6..4d5407fdd850 100644 --- a/tests/expected/function-contract/modifies/vec_pass.expected +++ b/tests/expected/function-contract/modifies/vec_pass.expected @@ -15,6 +15,6 @@ in function modify_replace assertion\ - Status: SUCCESS\ -- Description: "v[0] == src" +- Description: "|result| v[0] == src" VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/modifies/vec_pass.rs b/tests/expected/function-contract/modifies/vec_pass.rs index 1e40a2a08eb7..e3ac28ac3b3d 100644 --- a/tests/expected/function-contract/modifies/vec_pass.rs +++ b/tests/expected/function-contract/modifies/vec_pass.rs @@ -4,7 +4,7 @@ #[kani::requires(v.len() > 0)] #[kani::modifies(&v[0])] -#[kani::ensures(v[0] == src)] +#[kani::ensures(|result| v[0] == src)] fn modify(v: &mut Vec, src: u32) { v[0] = src } diff --git a/tests/expected/function-contract/pattern_use.rs b/tests/expected/function-contract/pattern_use.rs index a51312acd2f0..ead1c1538b4d 100644 --- a/tests/expected/function-contract/pattern_use.rs +++ b/tests/expected/function-contract/pattern_use.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zfunction-contracts -#[kani::ensures(result <= dividend)] +#[kani::ensures(|result : &u32| *result <= dividend)] fn div((dividend, divisor): (u32, u32)) -> u32 { dividend / divisor } diff --git a/tests/expected/function-contract/prohibit-pointers/allowed_const_ptr.rs b/tests/expected/function-contract/prohibit-pointers/allowed_const_ptr.rs index 3d88fc0926ed..1b3653951ad1 100644 --- a/tests/expected/function-contract/prohibit-pointers/allowed_const_ptr.rs +++ b/tests/expected/function-contract/prohibit-pointers/allowed_const_ptr.rs @@ -4,7 +4,7 @@ #![allow(unreachable_code, unused_variables)] -#[kani::ensures(true)] +#[kani::ensures(|result| true)] fn allowed_pointer(t: *const bool) {} #[kani::proof_for_contract(allowed_pointer)] diff --git a/tests/expected/function-contract/prohibit-pointers/allowed_mut_ref.rs b/tests/expected/function-contract/prohibit-pointers/allowed_mut_ref.rs index 22771f76632d..2c41bf28d741 100644 --- a/tests/expected/function-contract/prohibit-pointers/allowed_mut_ref.rs +++ b/tests/expected/function-contract/prohibit-pointers/allowed_mut_ref.rs @@ -4,7 +4,7 @@ #![allow(unreachable_code, unused_variables)] -#[kani::ensures(true)] +#[kani::ensures(|result| true)] fn allowed_mut_ref(t: &mut bool) {} #[kani::proof_for_contract(allowed_mut_ref)] diff --git a/tests/expected/function-contract/prohibit-pointers/allowed_mut_return_ref.rs b/tests/expected/function-contract/prohibit-pointers/allowed_mut_return_ref.rs index e5151396898d..fdab681e0c05 100644 --- a/tests/expected/function-contract/prohibit-pointers/allowed_mut_return_ref.rs +++ b/tests/expected/function-contract/prohibit-pointers/allowed_mut_return_ref.rs @@ -17,7 +17,7 @@ impl<'a> kani::Arbitrary for ArbitraryPointer<&'a mut bool> { } } -#[kani::ensures(true)] +#[kani::ensures(|result| true)] fn allowed_mut_return_ref<'a>() -> ArbitraryPointer<&'a mut bool> { ArbitraryPointer(unsafe { &mut B as &'a mut bool }) } diff --git a/tests/expected/function-contract/prohibit-pointers/allowed_ref.rs b/tests/expected/function-contract/prohibit-pointers/allowed_ref.rs index 3dd4145eff9c..cef3e87ee0f2 100644 --- a/tests/expected/function-contract/prohibit-pointers/allowed_ref.rs +++ b/tests/expected/function-contract/prohibit-pointers/allowed_ref.rs @@ -4,7 +4,7 @@ #![allow(unreachable_code, unused_variables)] -#[kani::ensures(true)] +#[kani::ensures(|result| true)] fn allowed_ref(t: &bool) {} #[kani::proof_for_contract(allowed_ref)] diff --git a/tests/expected/function-contract/prohibit-pointers/hidden.rs b/tests/expected/function-contract/prohibit-pointers/hidden.rs index 9ca23fe6b2e1..1a9b7a475479 100644 --- a/tests/expected/function-contract/prohibit-pointers/hidden.rs +++ b/tests/expected/function-contract/prohibit-pointers/hidden.rs @@ -6,7 +6,7 @@ struct HidesAPointer(*mut u32); -#[kani::ensures(true)] +#[kani::ensures(|result| true)] fn hidden_pointer(h: HidesAPointer) {} #[kani::proof_for_contract(hidden_pointer)] diff --git a/tests/expected/function-contract/prohibit-pointers/plain_pointer.rs b/tests/expected/function-contract/prohibit-pointers/plain_pointer.rs index 24e9d006a9c0..868327e15046 100644 --- a/tests/expected/function-contract/prohibit-pointers/plain_pointer.rs +++ b/tests/expected/function-contract/prohibit-pointers/plain_pointer.rs @@ -4,7 +4,7 @@ #![allow(unreachable_code, unused_variables)] -#[kani::ensures(true)] +#[kani::ensures(|result| true)] fn plain_pointer(t: *mut i32) {} #[kani::proof_for_contract(plain_pointer)] diff --git a/tests/expected/function-contract/prohibit-pointers/return_pointer.rs b/tests/expected/function-contract/prohibit-pointers/return_pointer.rs index 2bacdca9bbb6..6314129ddf89 100644 --- a/tests/expected/function-contract/prohibit-pointers/return_pointer.rs +++ b/tests/expected/function-contract/prohibit-pointers/return_pointer.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zfunction-contracts -#[kani::ensures(unsafe{ *result == *input })] +#[kani::ensures(|result : &*const usize| unsafe{ **result == *input })] fn return_pointer(input: *const usize) -> *const usize { input } diff --git a/tests/expected/function-contract/simple_ensures_fail.expected b/tests/expected/function-contract/simple_ensures_fail.expected index 8e9b42d42640..360242d07daf 100644 --- a/tests/expected/function-contract/simple_ensures_fail.expected +++ b/tests/expected/function-contract/simple_ensures_fail.expected @@ -1,8 +1,8 @@ assertion\ - Status: FAILURE\ -- Description: "result == x"\ +- Description: "|result : &u32| *result == x"\ in function max -Failed Checks: result == x +Failed Checks: |result : &u32| *result == x VERIFICATION:- FAILED diff --git a/tests/expected/function-contract/simple_ensures_fail.rs b/tests/expected/function-contract/simple_ensures_fail.rs index 687853612dcc..43521b171ea7 100644 --- a/tests/expected/function-contract/simple_ensures_fail.rs +++ b/tests/expected/function-contract/simple_ensures_fail.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zfunction-contracts -#[kani::ensures(result == x)] +#[kani::ensures(|result : &u32| *result == x)] fn max(x: u32, y: u32) -> u32 { if x > y { x } else { y } } diff --git a/tests/expected/function-contract/simple_ensures_pass.expected b/tests/expected/function-contract/simple_ensures_pass.expected index 5a7874964413..bdcde74c3bfe 100644 --- a/tests/expected/function-contract/simple_ensures_pass.expected +++ b/tests/expected/function-contract/simple_ensures_pass.expected @@ -1,6 +1,6 @@ assertion\ - Status: SUCCESS\ -- Description: "(result == x) | (result == y)"\ +- Description: "|result : &u32| (*result == x) | (*result == y)"\ in function max VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/simple_ensures_pass.rs b/tests/expected/function-contract/simple_ensures_pass.rs index 2d36f5c96e88..7be58fef3b04 100644 --- a/tests/expected/function-contract/simple_ensures_pass.rs +++ b/tests/expected/function-contract/simple_ensures_pass.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zfunction-contracts -#[kani::ensures((result == x) | (result == y))] +#[kani::ensures(|result : &u32| (*result == x) | (*result == y))] fn max(x: u32, y: u32) -> u32 { if x > y { x } else { y } } diff --git a/tests/expected/function-contract/simple_replace_fail.rs b/tests/expected/function-contract/simple_replace_fail.rs index 33a531a3aef7..dd448d2cdee6 100644 --- a/tests/expected/function-contract/simple_replace_fail.rs +++ b/tests/expected/function-contract/simple_replace_fail.rs @@ -3,7 +3,7 @@ // kani-flags: -Zfunction-contracts #[kani::requires(divisor != 0)] -#[kani::ensures(result <= dividend)] +#[kani::ensures(|result : &u32| *result <= dividend)] fn div(dividend: u32, divisor: u32) -> u32 { dividend / divisor } diff --git a/tests/expected/function-contract/simple_replace_pass.rs b/tests/expected/function-contract/simple_replace_pass.rs index 0dcc6cd59fe3..7c57cc6a0b7f 100644 --- a/tests/expected/function-contract/simple_replace_pass.rs +++ b/tests/expected/function-contract/simple_replace_pass.rs @@ -3,7 +3,7 @@ // kani-flags: -Zfunction-contracts #[kani::requires(divisor != 0)] -#[kani::ensures(result <= dividend)] +#[kani::ensures(|result : &u32| *result <= dividend)] fn div(dividend: u32, divisor: u32) -> u32 { dividend / divisor } diff --git a/tests/expected/function-contract/type_annotation_needed.rs b/tests/expected/function-contract/type_annotation_needed.rs index 09b20443d47b..5a3b9fbae5b0 100644 --- a/tests/expected/function-contract/type_annotation_needed.rs +++ b/tests/expected/function-contract/type_annotation_needed.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zfunction-contracts -#[kani::ensures(result.is_some())] +#[kani::ensures(|result : &Option| result.is_some())] fn or_default(opt: Option) -> Option { opt.or(Some(T::default())) } diff --git a/tests/std-checks/core/src/ptr.rs b/tests/std-checks/core/src/ptr.rs index 847b8fd1c138..49cf9e168214 100644 --- a/tests/std-checks/core/src/ptr.rs +++ b/tests/std-checks/core/src/ptr.rs @@ -9,8 +9,8 @@ pub mod contracts { use super::*; use kani::{ensures, implies, mem::*, modifies, requires}; - #[ensures(implies!(ptr.is_null() => result.is_none()))] - #[ensures(implies!(!ptr.is_null() => result.is_some()))] + #[ensures(|result : &Option>| implies!(ptr.is_null() => result.is_none()))] + #[ensures(|result : &Option>| implies!(!ptr.is_null() => result.is_some()))] pub fn new(ptr: *mut T) -> Option> { NonNull::new(ptr) } From a6332fc2ed04c5a72e4671835ef584c0b31c4f3b Mon Sep 17 00:00:00 2001 From: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Date: Thu, 6 Jun 2024 16:07:04 -0400 Subject: [PATCH 119/225] (Re)introduce `Invariant` trait (#3190) This PR reintroduces the `Invariant` trait as a mechanism for the specification of type safety invariants. The trait is defined in the Kani library where we also provide `Invariant` implementations for primitive types. In contrast to the previous `Invariant` trait, this version doesn't require the `Arbitrary` bound (i.e., it has the same requirements as `Arbitrary`). This way, the user isn't required to provide an `Arbitrary` implementation in addition to the `Invariant` one. Related #3095 --- library/kani/src/arbitrary.rs | 6 +- library/kani/src/invariant.rs | 102 ++++++++++++++++++++++++ library/kani/src/lib.rs | 2 + tests/kani/Invariant/invariant_impls.rs | 38 +++++++++ tests/kani/Invariant/percentage.rs | 62 ++++++++++++++ 5 files changed, 207 insertions(+), 3 deletions(-) create mode 100644 library/kani/src/invariant.rs create mode 100644 tests/kani/Invariant/invariant_impls.rs create mode 100644 tests/kani/Invariant/percentage.rs diff --git a/library/kani/src/arbitrary.rs b/library/kani/src/arbitrary.rs index 938f3c30968f..3f1adc787b79 100644 --- a/library/kani/src/arbitrary.rs +++ b/library/kani/src/arbitrary.rs @@ -1,8 +1,8 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -//! This module introduces the Arbitrary trait as well as implementation for primitive types and -//! other std containers. +//! This module introduces the `Arbitrary` trait as well as implementation for +//! primitive types and other std containers. use std::{ marker::{PhantomData, PhantomPinned}, @@ -66,7 +66,7 @@ trivial_arbitrary!(i64); trivial_arbitrary!(i128); trivial_arbitrary!(isize); -// We do not constraint floating points values per type spec. Users must add assumptions to their +// We do not constrain floating points values per type spec. Users must add assumptions to their // verification code if they want to eliminate NaN, infinite, or subnormal. trivial_arbitrary!(f32); trivial_arbitrary!(f64); diff --git a/library/kani/src/invariant.rs b/library/kani/src/invariant.rs new file mode 100644 index 000000000000..f118f94e995c --- /dev/null +++ b/library/kani/src/invariant.rs @@ -0,0 +1,102 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This module introduces the `Invariant` trait as well as its implementation +//! for primitive types. + +/// This trait should be used to specify and check type safety invariants for a +/// type. For type invariants, we refer to the definitions in the Rust's Unsafe +/// Code Guidelines Reference: +/// +/// +/// In summary, the reference distinguishes two kinds of type invariants: +/// - *Validity invariant*: An invariant that all data must uphold any time +/// it's accessed or copied in a typed manner. This invariant is exploited by +/// the compiler to perform optimizations. +/// - *Safety invariant*: An invariant that safe code may assume all data to +/// uphold. This invariant can be temporarily violated by unsafe code, but +/// must always be upheld when interfacing with unknown safe code. +/// +/// Therefore, validity invariants must be upheld at all times, while safety +/// invariants only need to be upheld at the boundaries to safe code. +/// +/// Safety invariants are particularly interesting for user-defined types, and +/// the `Invariant` trait allows you to check them with Kani. +/// +/// It can also be used in tests. It's a programmatic way to specify (in Rust) +/// properties over your data types. Since it's written in Rust, it can be used +/// for static and dynamic checking. +/// +/// For example, let's say you're creating a type that represents a date: +/// +/// ```rust +/// #[derive(kani::Arbitrary)] +/// pub struct MyDate { +/// day: u8, +/// month: u8, +/// year: i64, +/// } +/// ``` +/// You can specify its safety invariant as: +/// ```rust +/// impl kani::Invariant for MyDate { +/// fn is_safe(&self) -> bool { +/// self.month > 0 +/// && self.month <= 12 +/// && self.day > 0 +/// && self.day <= days_in_month(self.year, self.month) +/// } +/// } +/// ``` +/// And use it to check that your APIs are safe: +/// ```rust +/// #[kani::proof] +/// fn check_increase_date() { +/// let mut date: MyDate = kani::any(); +/// // Increase date by one day +/// increase_date(date, 1); +/// assert!(date.is_safe()); +/// } +/// ``` +pub trait Invariant +where + Self: Sized, +{ + fn is_safe(&self) -> bool; +} + +/// Any value is considered safe for the type +macro_rules! trivial_invariant { + ( $type: ty ) => { + impl Invariant for $type { + #[inline(always)] + fn is_safe(&self) -> bool { + true + } + } + }; +} + +trivial_invariant!(u8); +trivial_invariant!(u16); +trivial_invariant!(u32); +trivial_invariant!(u64); +trivial_invariant!(u128); +trivial_invariant!(usize); + +trivial_invariant!(i8); +trivial_invariant!(i16); +trivial_invariant!(i32); +trivial_invariant!(i64); +trivial_invariant!(i128); +trivial_invariant!(isize); + +// We do not constrain the safety invariant for floating points types. +// Users can create a new type wrapping the floating point type and define an +// invariant that checks for NaN, infinite, or subnormal values. +trivial_invariant!(f32); +trivial_invariant!(f64); + +trivial_invariant!(()); +trivial_invariant!(bool); +trivial_invariant!(char); diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index 341dd6752916..e81ddbe7904b 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -23,6 +23,7 @@ pub mod arbitrary; #[cfg(feature = "concrete_playback")] mod concrete_playback; pub mod futures; +pub mod invariant; pub mod mem; pub mod shadow; pub mod slice; @@ -37,6 +38,7 @@ mod models; pub use arbitrary::Arbitrary; #[cfg(feature = "concrete_playback")] pub use concrete_playback::concrete_playback_run; +pub use invariant::Invariant; #[cfg(not(feature = "concrete_playback"))] /// NOP `concrete_playback` for type checking during verification mode. diff --git a/tests/kani/Invariant/invariant_impls.rs b/tests/kani/Invariant/invariant_impls.rs new file mode 100644 index 000000000000..4f00f4134956 --- /dev/null +++ b/tests/kani/Invariant/invariant_impls.rs @@ -0,0 +1,38 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check the `Invariant` implementations that we include in the Kani library +//! with respect to the underlying type invariants. +extern crate kani; +use kani::Invariant; + +macro_rules! check_safe_type { + ( $type: ty ) => { + let value: $type = kani::any(); + assert!(value.is_safe()); + }; +} + +#[kani::proof] +fn check_safe_impls() { + check_safe_type!(u8); + check_safe_type!(u16); + check_safe_type!(u32); + check_safe_type!(u64); + check_safe_type!(u128); + check_safe_type!(usize); + + check_safe_type!(i8); + check_safe_type!(i16); + check_safe_type!(i32); + check_safe_type!(i64); + check_safe_type!(i128); + check_safe_type!(isize); + + check_safe_type!(f32); + check_safe_type!(f64); + + check_safe_type!(()); + check_safe_type!(bool); + check_safe_type!(char); +} diff --git a/tests/kani/Invariant/percentage.rs b/tests/kani/Invariant/percentage.rs new file mode 100644 index 000000000000..f3976f48e249 --- /dev/null +++ b/tests/kani/Invariant/percentage.rs @@ -0,0 +1,62 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that the `Invariant` implementation behaves as expected when used on a +//! custom type. + +extern crate kani; +use kani::Invariant; + +// We use the default `Arbitrary` implementation, which allows values that +// shouldn't be considered safe for the `Percentage` type. +#[derive(kani::Arbitrary)] +struct Percentage(u8); + +impl Percentage { + pub fn try_new(val: u8) -> Result { + if val <= 100 { + Ok(Self(val)) + } else { + Err(String::from("error: invalid percentage value")) + } + } + + pub fn value(&self) -> u8 { + self.0 + } + + pub fn increase(&self, other: u8) -> Percentage { + let amount = self.0 + other; + Percentage::try_new(amount.min(100)).unwrap() + } +} + +impl kani::Invariant for Percentage { + fn is_safe(&self) -> bool { + self.0 <= 100 + } +} + +#[kani::proof] +fn check_assume_safe() { + let percentage: Percentage = kani::any(); + kani::assume(percentage.is_safe()); + assert!(percentage.value() <= 100); +} + +#[kani::proof] +#[kani::should_panic] +fn check_assert_safe() { + let percentage: Percentage = kani::any(); + assert!(percentage.is_safe()); +} + +#[kani::proof] +fn check_increase_safe() { + let percentage: Percentage = kani::any(); + kani::assume(percentage.is_safe()); + let amount = kani::any(); + kani::assume(amount <= 100); + let new_percentage = percentage.increase(amount); + assert!(new_percentage.is_safe()); +} From e9eeef72d0d6617019b0f46d67e471f13e2120e8 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 6 Jun 2024 13:53:34 -0700 Subject: [PATCH 120/225] Remove empty box creation from contracts impl (#3233) There seems to be an issue in CBMC contracts implementation that it assumes that `free` must have a body. However, slicing can remove `free` body if the harness does not allocate anything. https://github.com/diffblue/cbmc/issues/8317 We used to create an empty Box before to force `free` to be in scope. Instead, just invoke `free(NULL)` which is a no-op. 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: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> --- .../codegen_cprover_gotoc/overrides/hooks.rs | 40 +++++++++++++++++++ library/kani/src/internal.rs | 14 +++++++ .../kani_macros/src/sysroot/contracts/mod.rs | 2 +- 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index 9b2f9e770d32..d66e16b6ce59 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -458,6 +458,45 @@ impl GotocHook for UntrackedDeref { } } +struct InitContracts; + +/// CBMC contracts currently has a limitation where `free` has to be in scope. +/// However, if there is no dynamic allocation in the harness, slicing removes `free` from the +/// scope. +/// +/// Thus, this function will basically translate into: +/// ```c +/// // This is a no-op. +/// free(NULL); +/// ``` +impl GotocHook for InitContracts { + fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { + matches_function(tcx, instance.def, "KaniInitContracts") + } + + fn handle( + &self, + gcx: &mut GotocCtx, + _instance: Instance, + fargs: Vec, + _assign_to: &Place, + target: Option, + span: Span, + ) -> Stmt { + assert_eq!(fargs.len(), 0,); + let loc = gcx.codegen_span_stable(span); + Stmt::block( + vec![ + BuiltinFn::Free + .call(vec![Expr::pointer_constant(0, Type::void_pointer())], loc) + .as_stmt(loc), + Stmt::goto(bb_label(target.unwrap()), loc), + ], + loc, + ) + } +} + pub fn fn_hooks() -> GotocHooks { GotocHooks { hooks: vec![ @@ -472,6 +511,7 @@ pub fn fn_hooks() -> GotocHooks { Rc::new(RustAlloc), Rc::new(MemCmp), Rc::new(UntrackedDeref), + Rc::new(InitContracts), ], } } diff --git a/library/kani/src/internal.rs b/library/kani/src/internal.rs index d2f2970d1c05..a910c333b112 100644 --- a/library/kani/src/internal.rs +++ b/library/kani/src/internal.rs @@ -76,3 +76,17 @@ impl<'a, T> Pointer<'a> for *mut T { pub fn untracked_deref(_: &T) -> T { todo!() } + +/// CBMC contracts currently has a limitation where `free` has to be in scope. +/// However, if there is no dynamic allocation in the harness, slicing removes `free` from the +/// scope. +/// +/// Thus, this function will basically translate into: +/// ```c +/// // This is a no-op. +/// free(NULL); +/// ``` +#[inline(never)] +#[doc(hidden)] +#[rustc_diagnostic_item = "KaniInitContracts"] +pub fn init_contracts() {} diff --git a/library/kani_macros/src/sysroot/contracts/mod.rs b/library/kani_macros/src/sysroot/contracts/mod.rs index bef6f8d921bc..de3b5da123ed 100644 --- a/library/kani_macros/src/sysroot/contracts/mod.rs +++ b/library/kani_macros/src/sysroot/contracts/mod.rs @@ -294,7 +294,7 @@ pub fn proof_for_contract(attr: TokenStream, item: TokenStream) -> TokenStream { #[kanitool::proof_for_contract = stringify!(#args)] #(#attrs)* #vis #sig { - let _ = std::boxed::Box::new(0_usize); + kani::internal::init_contracts(); #block } ) From dcbf3aafa92e5c40e1eb7da7c5475b152c4222d5 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 6 Jun 2024 15:13:12 -0700 Subject: [PATCH 121/225] Add a new verify-std subcommand to Kani (#3231) This subcommand will take the path to the standard library. It will then use `cargo build -Z build-std` to build the custom standard library and verify any harness found during the build. ## Call out - So far I only performed manual tests. I'm going to add a few unit tests and a script in the next revision. Resolves #3226 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: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> --- kani-compiler/src/kani_middle/intrinsics.rs | 7 +- kani-driver/src/args/mod.rs | 10 ++ kani-driver/src/args/std_args.rs | 77 +++++++++++ kani-driver/src/call_cargo.rs | 125 ++++++++++++------ kani-driver/src/call_single_file.rs | 56 +++++--- kani-driver/src/concrete_playback/playback.rs | 6 +- kani-driver/src/main.rs | 33 +++-- kani-driver/src/project.rs | 32 +++++ kani-driver/src/session.rs | 5 + kani_metadata/src/unstable.rs | 4 +- library/kani_core/src/lib.rs | 3 + tools/build-kani/src/main.rs | 3 +- 12 files changed, 290 insertions(+), 71 deletions(-) create mode 100644 kani-driver/src/args/std_args.rs diff --git a/kani-compiler/src/kani_middle/intrinsics.rs b/kani-compiler/src/kani_middle/intrinsics.rs index 51e6140b7e1a..82a75a91f1ad 100644 --- a/kani-compiler/src/kani_middle/intrinsics.rs +++ b/kani-compiler/src/kani_middle/intrinsics.rs @@ -57,7 +57,12 @@ impl<'tcx> ModelIntrinsics<'tcx> { let arg_ty = args[0].node.ty(&self.local_decls, tcx); if arg_ty.is_simd() { // Get the stub definition. - let stub_id = tcx.get_diagnostic_item(Symbol::intern("KaniModelSimdBitmask")).unwrap(); + let Some(stub_id) = tcx.get_diagnostic_item(Symbol::intern("KaniModelSimdBitmask")) + else { + // This should only happen when verifying the standard library. + // We don't need to warn here, since the backend will print unsupported constructs. + return; + }; debug!(?func, ?stub_id, "replace_simd_bitmask"); // Get SIMD information from the type. diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index 36d9e0af2d0e..f0c9cb0c4e8d 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -6,6 +6,7 @@ pub mod assess_args; pub mod cargo; pub mod common; pub mod playback_args; +pub mod std_args; pub use assess_args::*; @@ -90,6 +91,8 @@ pub struct StandaloneArgs { pub enum StandaloneSubcommand { /// Execute concrete playback testcases of a local crate. Playback(Box), + /// Verify the rust standard library. + VerifyStd(Box), } #[derive(Debug, clap::Parser)] @@ -448,6 +451,13 @@ fn check_no_cargo_opt(is_set: bool, name: &str) -> Result<(), Error> { impl ValidateArgs for StandaloneArgs { fn validate(&self) -> Result<(), Error> { self.verify_opts.validate()?; + + match &self.command { + Some(StandaloneSubcommand::VerifyStd(args)) => args.validate()?, + // TODO: Invoke PlaybackArgs::validate() + None | Some(StandaloneSubcommand::Playback(..)) => {} + }; + // Cargo target arguments. check_no_cargo_opt(self.verify_opts.target.bins, "--bins")?; check_no_cargo_opt(self.verify_opts.target.lib, "--lib")?; diff --git a/kani-driver/src/args/std_args.rs b/kani-driver/src/args/std_args.rs new file mode 100644 index 000000000000..3818b6261010 --- /dev/null +++ b/kani-driver/src/args/std_args.rs @@ -0,0 +1,77 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Implements the `verify-std` subcommand handling. + +use crate::args::{ValidateArgs, VerificationArgs}; +use clap::error::ErrorKind; +use clap::{Error, Parser}; +use kani_metadata::UnstableFeature; +use std::path::PathBuf; + +/// Verify a local version of the Rust standard library. +/// +/// This is an **unstable option** and it the standard library version must be compatible with +/// Kani's toolchain version. +#[derive(Debug, Parser)] +pub struct VerifyStdArgs { + /// The path to the folder containing the crates for the Rust standard library. + /// Note that this directory must be named `library` as used in the Rust toolchain and + /// repository. + pub std_path: PathBuf, + + #[command(flatten)] + pub verify_opts: VerificationArgs, +} + +impl ValidateArgs for VerifyStdArgs { + 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, + "The `verify-std` subcommand is unstable and requires -Z unstable-options", + )); + } + + 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(()) + } + } + } +} diff --git a/kani-driver/src/call_cargo.rs b/kani-driver/src/call_cargo.rs index 2d5d36306e21..ebf61e153c46 100644 --- a/kani-driver/src/call_cargo.rs +++ b/kani-driver/src/call_cargo.rs @@ -2,20 +2,22 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use crate::args::VerificationArgs; -use crate::call_single_file::to_rustc_arg; +use crate::call_single_file::{to_rustc_arg, LibConfig}; use crate::project::Artifact; -use crate::session::{setup_cargo_command, KaniSession}; +use crate::session::{lib_folder, lib_no_core_folder, setup_cargo_command, KaniSession}; use crate::util; use anyhow::{bail, Context, Result}; use cargo_metadata::diagnostic::{Diagnostic, DiagnosticLevel}; -use cargo_metadata::{Message, Metadata, MetadataCommand, Package, Target}; +use cargo_metadata::{ + Artifact as RustcArtifact, Message, Metadata, MetadataCommand, Package, Target, +}; use kani_metadata::{ArtifactType, CompilerArtifactStub}; use std::ffi::{OsStr, OsString}; use std::fmt::{self, Display}; use std::fs::{self, File}; use std::io::BufReader; use std::io::IsTerminal; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; use tracing::{debug, trace}; @@ -43,6 +45,47 @@ pub struct CargoOutputs { } impl KaniSession { + /// Create a new cargo library in the given path. + pub fn cargo_init_lib(&self, path: &Path) -> Result<()> { + let mut cmd = setup_cargo_command()?; + cmd.args(["init", "--lib", path.to_string_lossy().as_ref()]); + self.run_terminal(cmd) + } + + pub fn cargo_build_std(&self, std_path: &Path, krate_path: &Path) -> Result> { + let lib_path = lib_no_core_folder().unwrap(); + let mut rustc_args = self.kani_rustc_flags(LibConfig::new_no_core(lib_path)); + rustc_args.push(to_rustc_arg(self.kani_compiler_flags()).into()); + rustc_args.push(self.reachability_arg().into()); + + let mut cargo_args: Vec = vec!["build".into()]; + cargo_args.append(&mut cargo_config_args()); + + // Configuration needed to parse cargo compilation status. + cargo_args.push("--message-format".into()); + cargo_args.push("json-diagnostic-rendered-ansi".into()); + cargo_args.push("-Z".into()); + cargo_args.push("build-std=panic_abort,core,std".into()); + + if self.args.common_args.verbose { + cargo_args.push("-v".into()); + } + + // Since we are verifying the standard library, we set the reachability to all crates. + let mut cmd = setup_cargo_command()?; + cmd.args(&cargo_args) + .current_dir(krate_path) + .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 + .env("CARGO_ENCODED_RUSTFLAGS", rustc_args.join(OsStr::new("\x1f"))) + .env("CARGO_TERM_PROGRESS_WHEN", "never") + .env("__CARGO_TESTS_ONLY_SRC_ROOT", std_path.as_os_str()); + + let build_artifacts = self.run_build(cmd)?; + Ok(build_artifacts.into_iter().filter_map(map_kani_artifact).collect()) + } + /// Calls `cargo_build` to generate `*.symtab.json` files in `target_dir` pub fn cargo_build(&self, keep_going: bool) -> Result { let build_target = env!("TARGET"); // see build.rs @@ -60,7 +103,8 @@ impl KaniSession { fs::remove_dir_all(&target_dir)?; } - let mut rustc_args = self.kani_rustc_flags(); + let lib_path = lib_folder().unwrap(); + let mut rustc_args = self.kani_rustc_flags(LibConfig::new(lib_path)); rustc_args.push(to_rustc_arg(self.kani_compiler_flags()).into()); let mut cargo_args: Vec = vec!["rustc".into()]; @@ -120,7 +164,7 @@ impl KaniSession { .env("CARGO_ENCODED_RUSTFLAGS", rustc_args.join(OsStr::new("\x1f"))) .env("CARGO_TERM_PROGRESS_WHEN", "never"); - match self.run_cargo(cmd, verification_target.target()) { + match self.run_build_target(cmd, verification_target.target()) { Err(err) => { if keep_going { let target_str = format!("{verification_target}"); @@ -179,9 +223,9 @@ impl KaniSession { /// Run cargo and collect any error found. /// We also collect the metadata file generated during compilation if any. - fn run_cargo(&self, cargo_cmd: Command, target: &Target) -> Result> { + fn run_build(&self, cargo_cmd: Command) -> Result> { let support_color = std::io::stdout().is_terminal(); - let mut artifact = None; + let mut artifacts = vec![]; if let Some(mut cargo_process) = self.run_piped(cargo_cmd)? { let reader = BufReader::new(cargo_process.stdout.take().unwrap()); let mut error_count = 0; @@ -211,33 +255,9 @@ impl KaniSession { } }, Message::CompilerArtifact(rustc_artifact) => { - /// Compares two targets, and falls back to a weaker - /// comparison where we avoid dashes in their names. - fn same_target(t1: &Target, t2: &Target) -> bool { - (t1 == t2) - || (t1.name.replace('-', "_") == t2.name.replace('-', "_") - && t1.kind == t2.kind - && t1.src_path == t2.src_path - && t1.edition == t2.edition - && t1.doctest == t2.doctest - && t1.test == t2.test - && t1.doc == t2.doc) - } - // This used to be `rustc_artifact == *target`, but it - // started to fail after the `cargo` change in - // - // - // We should revisit this check after a while to see if - // it's not needed anymore or it can be restricted to - // certain cases. - // TODO: - if same_target(&rustc_artifact.target, target) { - debug_assert!( - artifact.is_none(), - "expected only one artifact for `{target:?}`", - ); - artifact = Some(rustc_artifact); - } + // Compares two targets, and falls back to a weaker + // comparison where we avoid dashes in their names. + artifacts.push(rustc_artifact) } Message::BuildScriptExecuted(_) | Message::BuildFinished(_) => { // do nothing @@ -263,11 +283,40 @@ impl KaniSession { ); } } + Ok(artifacts) + } + + /// Run cargo and collect any error found. + /// We also collect the metadata file generated during compilation if any for the given target. + fn run_build_target(&self, cargo_cmd: Command, target: &Target) -> Result> { + /// This used to be `rustc_artifact == *target`, but it + /// started to fail after the `cargo` change in + /// + /// + /// We should revisit this check after a while to see if + /// it's not needed anymore or it can be restricted to + /// certain cases. + /// TODO: + fn same_target(t1: &Target, t2: &Target) -> bool { + (t1 == t2) + || (t1.name.replace('-', "_") == t2.name.replace('-', "_") + && t1.kind == t2.kind + && t1.src_path == t2.src_path + && t1.edition == t2.edition + && t1.doctest == t2.doctest + && t1.test == t2.test + && t1.doc == t2.doc) + } + + let artifacts = self.run_build(cargo_cmd)?; + debug!(?artifacts, "run_build_target"); + // We generate kani specific artifacts only for the build target. The build target is // always the last artifact generated in a build, and all the other artifacts are related - // to dependencies or build scripts. Hence, we need to invoke `map_kani_artifact` only - // for the last compiler artifact. - Ok(artifact.and_then(map_kani_artifact)) + // to dependencies or build scripts. + Ok(artifacts.into_iter().rev().find_map(|artifact| { + if same_target(&artifact.target, target) { map_kani_artifact(artifact) } else { None } + })) } } diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index 1c8d63d6d87a..335d9840e0e7 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -9,6 +9,39 @@ use std::process::Command; use crate::session::{lib_folder, KaniSession}; +pub struct LibConfig { + args: Vec, +} + +impl LibConfig { + pub fn new(path: PathBuf) -> LibConfig { + let sysroot = &path.parent().unwrap(); + let kani_std_rlib = path.join("libstd.rlib"); + let kani_std_wrapper = format!("noprelude:std={}", kani_std_rlib.to_str().unwrap()); + let args = [ + "--sysroot", + sysroot.to_str().unwrap(), + "-L", + path.to_str().unwrap(), + "--extern", + "kani", + "--extern", + kani_std_wrapper.as_str(), + ] + .map(OsString::from) + .to_vec(); + LibConfig { args } + } + + pub fn new_no_core(path: PathBuf) -> LibConfig { + LibConfig { + args: ["-L", path.to_str().unwrap(), "--extern", "kani_core"] + .map(OsString::from) + .to_vec(), + } + } +} + impl KaniSession { /// Used by `kani` and not `cargo-kani` to process a single Rust file into a `.symtab.json` // TODO: Move these functions to be part of the builder. @@ -21,7 +54,8 @@ impl KaniSession { let mut kani_args = self.kani_compiler_flags(); kani_args.push(format!("--reachability={}", self.reachability_mode())); - let mut rustc_args = self.kani_rustc_flags(); + let lib_path = lib_folder().unwrap(); + let mut rustc_args = self.kani_rustc_flags(LibConfig::new(lib_path)); rustc_args.push(file.into()); rustc_args.push("--out-dir".into()); rustc_args.push(OsString::from(outdir.as_os_str())); @@ -119,9 +153,8 @@ impl KaniSession { } /// This function generates all rustc configurations required by our goto-c codegen. - pub fn kani_rustc_flags(&self) -> Vec { - let lib_path = lib_folder().unwrap(); - let mut flags: Vec<_> = base_rustc_flags(lib_path); + pub fn kani_rustc_flags(&self, lib_config: LibConfig) -> Vec { + let mut flags: Vec<_> = base_rustc_flags(lib_config); // We only use panic abort strategy for verification since we cannot handle unwind logic. flags.extend_from_slice( &[ @@ -156,10 +189,7 @@ impl KaniSession { } /// Common flags used for compiling user code for verification and playback flow. -pub fn base_rustc_flags(lib_path: PathBuf) -> Vec { - let kani_std_rlib = lib_path.join("libstd.rlib"); - let kani_std_wrapper = format!("noprelude:std={}", kani_std_rlib.to_str().unwrap()); - let sysroot = lib_path.parent().unwrap(); +pub fn base_rustc_flags(lib_config: LibConfig) -> Vec { let mut flags = [ "-C", "overflow-checks=on", @@ -176,18 +206,12 @@ pub fn base_rustc_flags(lib_path: PathBuf) -> Vec { "crate-attr=feature(register_tool)", "-Z", "crate-attr=register_tool(kanitool)", - "--sysroot", - sysroot.to_str().unwrap(), - "-L", - lib_path.to_str().unwrap(), - "--extern", - "kani", - "--extern", - kani_std_wrapper.as_str(), ] .map(OsString::from) .to_vec(); + flags.extend(lib_config.args); + // e.g. compiletest will set 'compile-flags' here and we should pass those down to rustc // and we fail in `tests/kani/Match/match_bool.rs` if let Ok(str) = std::env::var("RUSTFLAGS") { diff --git a/kani-driver/src/concrete_playback/playback.rs b/kani-driver/src/concrete_playback/playback.rs index d8c1762da92b..c56694ad32fd 100644 --- a/kani-driver/src/concrete_playback/playback.rs +++ b/kani-driver/src/concrete_playback/playback.rs @@ -6,7 +6,7 @@ use crate::args::common::Verbosity; use crate::args::playback_args::{CargoPlaybackArgs, KaniPlaybackArgs, MessageFormat}; use crate::call_cargo::cargo_config_args; -use crate::call_single_file::base_rustc_flags; +use crate::call_single_file::{base_rustc_flags, LibConfig}; use crate::session::{lib_playback_folder, setup_cargo_command, InstallType}; use crate::{session, util}; use anyhow::Result; @@ -70,7 +70,7 @@ fn build_test(install: &InstallType, args: &KaniPlaybackArgs) -> Result util::info_operation("Building", args.input.to_string_lossy().deref()); } - let mut rustc_args = base_rustc_flags(lib_playback_folder()?); + let mut rustc_args = base_rustc_flags(LibConfig::new(lib_playback_folder()?)); rustc_args.push("--test".into()); rustc_args.push(OsString::from(&args.input)); rustc_args.push(format!("--crate-name={TEST_BIN_NAME}").into()); @@ -96,7 +96,7 @@ fn cargo_test(args: CargoPlaybackArgs) -> Result<()> { let install = InstallType::new()?; let mut cmd = setup_cargo_command()?; - let rustc_args = base_rustc_flags(lib_playback_folder()?); + let rustc_args = base_rustc_flags(LibConfig::new(lib_playback_folder()?)); let mut cargo_args: Vec = vec!["test".into()]; if args.playback.common_opts.verbose() { diff --git a/kani-driver/src/main.rs b/kani-driver/src/main.rs index 798625560970..3bb38ed1294c 100644 --- a/kani-driver/src/main.rs +++ b/kani-driver/src/main.rs @@ -94,17 +94,28 @@ fn standalone_main() -> Result<()> { let args = args::StandaloneArgs::parse(); check_is_valid(&args); - if let Some(StandaloneSubcommand::Playback(args)) = args.command { - return playback_standalone(*args); - } - - let session = session::KaniSession::new(args.verify_opts)?; - - if !session.args.common_args.quiet { - print_kani_version(InvocationType::Standalone); - } - - let project = project::standalone_project(&args.input.unwrap(), args.crate_name, &session)?; + let (session, project) = match args.command { + Some(StandaloneSubcommand::Playback(args)) => return playback_standalone(*args), + Some(StandaloneSubcommand::VerifyStd(args)) => { + let session = KaniSession::new(args.verify_opts)?; + if !session.args.common_args.quiet { + print_kani_version(InvocationType::Standalone); + } + + let project = project::std_project(&args.std_path, &session)?; + (session, project) + } + None => { + let session = KaniSession::new(args.verify_opts)?; + if !session.args.common_args.quiet { + print_kani_version(InvocationType::Standalone); + } + + let project = + project::standalone_project(&args.input.unwrap(), args.crate_name, &session)?; + (session, project) + } + }; if session.args.only_codegen { Ok(()) } else { verify_project(project, session) } } diff --git a/kani-driver/src/project.rs b/kani-driver/src/project.rs index fcf3805ab3df..dfcc9eb3ac46 100644 --- a/kani-driver/src/project.rs +++ b/kani-driver/src/project.rs @@ -23,6 +23,8 @@ use anyhow::{Context, Result}; use kani_metadata::{ artifact::convert_type, ArtifactType, ArtifactType::*, HarnessMetadata, KaniMetadata, }; +use std::env::current_dir; +use std::fs; use std::fs::File; use std::io::BufWriter; use std::ops::Deref; @@ -383,3 +385,33 @@ fn standalone_artifact(out_dir: &Path, crate_name: &String, typ: ArtifactType) - let _ = path.set_extension(typ); Artifact { path, typ } } + +/// Verify the custom version of the standard library in the given path. +/// +/// Note that we assume that `std_path` points to a directory named "library". +/// This should be checked as part of the argument validation. +pub(crate) fn std_project(std_path: &Path, session: &KaniSession) -> Result { + // Create output directory + let outdir = if let Some(target_dir) = &session.args.target_dir { + target_dir.clone() + } else { + current_dir()?.join("target") + }; + fs::create_dir_all(&outdir)?; // This is a no-op if directory exists. + let outdir = outdir.canonicalize()?; + + // Create dummy crate needed to build using `cargo -Z build-std` + let dummy_crate = outdir.join("kani_verify_std"); + if dummy_crate.exists() { + fs::remove_dir_all(&dummy_crate)?; + } + session.cargo_init_lib(&dummy_crate)?; + + // Build cargo project for dummy crate. + let std_path = std_path.canonicalize()?; + let outputs = session.cargo_build_std(std_path.parent().unwrap(), &dummy_crate)?; + + // Get the metadata and return a Kani project. + let metadata = outputs.iter().map(|md_file| from_json(md_file)).collect::>>()?; + Project::try_new(session, outdir, metadata, None, None) +} diff --git a/kani-driver/src/session.rs b/kani-driver/src/session.rs index 8cf4a20450f5..bf02aaef7426 100644 --- a/kani-driver/src/session.rs +++ b/kani-driver/src/session.rs @@ -280,6 +280,11 @@ pub fn lib_playback_folder() -> Result { Ok(base_folder()?.join("playback/lib")) } +/// Return the path for the folder where the pre-compiled rust libraries with no_core. +pub fn lib_no_core_folder() -> Result { + Ok(base_folder()?.join("no_core/lib")) +} + /// Return the base folder for the entire kani installation. pub fn base_folder() -> Result { Ok(bin_folder()? diff --git a/kani_metadata/src/unstable.rs b/kani_metadata/src/unstable.rs index d7b73678a836..929322858d2a 100644 --- a/kani_metadata/src/unstable.rs +++ b/kani_metadata/src/unstable.rs @@ -87,8 +87,10 @@ pub enum UnstableFeature { /// Automatically check that no invalid value is produced which is considered UB in Rust. /// Note that this does not include checking uninitialized value. ValidValueChecks, - /// Ghost state and shadow memory APIs + /// Ghost state and shadow memory APIs. GhostState, + /// Enable an unstable option or subcommand. + UnstableOptions, } impl UnstableFeature { diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index 00b5efbe719c..a5cd40d71e92 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -3,3 +3,6 @@ //! This is placeholder for the new `kani_core` library. #![feature(no_core)] +#![no_core] + +pub use kani_macros::*; diff --git a/tools/build-kani/src/main.rs b/tools/build-kani/src/main.rs index 3f1bc26cfbbb..2c181d69d6e5 100644 --- a/tools/build-kani/src/main.rs +++ b/tools/build-kani/src/main.rs @@ -10,7 +10,7 @@ mod parser; mod sysroot; -use crate::sysroot::{build_bin, build_lib, kani_playback_lib, kani_sysroot_lib}; +use crate::sysroot::{build_bin, build_lib, kani_no_core_lib, kani_playback_lib, kani_sysroot_lib}; use anyhow::{bail, Result}; use clap::Parser; use std::{ffi::OsString, path::Path, process::Command}; @@ -102,6 +102,7 @@ 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)?; // 5. Record the exact toolchain and rustc version we use std::fs::write(dir.join("rust-toolchain-version"), env!("RUSTUP_TOOLCHAIN"))?; From 0bb1325e5f5e20dfdc89f292fd4bf71e6362066a Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 6 Jun 2024 20:02:22 -0700 Subject: [PATCH 122/225] Add a regression test for running `verify-std` command. (#3236) In #3226, we added a new command that allow Kani to verify properties in a custom standard library. In this PR, we create a script test that create a modified version of the standard library and runs Kani against it. I also moved the script based tests to run after the more generic regression jobs. I think the script jobs are a bit harder to debug, they may take longer, and they are usually very specific to a few features. So probably best if we run the more generic tests first. --- Cargo.toml | 1 + kani-driver/src/call_cargo.rs | 2 + scripts/kani-regression.sh | 2 +- .../verify_std_cmd/config.yml | 4 + .../verify_std_cmd/verify_std.expected | 11 +++ .../verify_std_cmd/verify_std.sh | 91 +++++++++++++++++++ 6 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 tests/script-based-pre/verify_std_cmd/config.yml create mode 100644 tests/script-based-pre/verify_std_cmd/verify_std.expected create mode 100755 tests/script-based-pre/verify_std_cmd/verify_std.sh diff --git a/Cargo.toml b/Cargo.toml index e4ff44f42ddc..641cbaf961be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,4 +68,5 @@ exclude = [ "tests/script-based-pre", "tests/script-based-pre/build-cache-bin/target/new_dep", "tests/script-based-pre/build-cache-dirty/target/new_dep", + "tests/script-based-pre/verify_std_cmd/tmp_dir/target/kani_verify_std", ] diff --git a/kani-driver/src/call_cargo.rs b/kani-driver/src/call_cargo.rs index ebf61e153c46..a1d976d73150 100644 --- a/kani-driver/src/call_cargo.rs +++ b/kani-driver/src/call_cargo.rs @@ -57,6 +57,8 @@ impl KaniSession { let mut rustc_args = self.kani_rustc_flags(LibConfig::new_no_core(lib_path)); rustc_args.push(to_rustc_arg(self.kani_compiler_flags()).into()); rustc_args.push(self.reachability_arg().into()); + // Ignore global assembly, since `compiler_builtins` has some. + rustc_args.push(to_rustc_arg(vec!["--ignore-global-asm".to_string()]).into()); let mut cargo_args: Vec = vec!["build".into()]; cargo_args.append(&mut cargo_config_args()); diff --git a/scripts/kani-regression.sh b/scripts/kani-regression.sh index dcced313ce0b..d3bb0f7efb45 100755 --- a/scripts/kani-regression.sh +++ b/scripts/kani-regression.sh @@ -49,7 +49,6 @@ RUSTFLAGS=--cfg=kani_sysroot cargo test -p kani_macros --features syn/extra-trai # Declare testing suite information (suite and mode) TESTS=( - "script-based-pre exec" "kani kani" "expected expected" "ui expected" @@ -59,6 +58,7 @@ TESTS=( "smack kani" "cargo-kani cargo-kani" "cargo-ui cargo-kani" + "script-based-pre exec" "coverage coverage-based" "kani-docs cargo-kani" "kani-fixme kani-fixme" diff --git a/tests/script-based-pre/verify_std_cmd/config.yml b/tests/script-based-pre/verify_std_cmd/config.yml new file mode 100644 index 000000000000..f6e398303b79 --- /dev/null +++ b/tests/script-based-pre/verify_std_cmd/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: verify_std.sh +expected: verify_std.expected diff --git a/tests/script-based-pre/verify_std_cmd/verify_std.expected b/tests/script-based-pre/verify_std_cmd/verify_std.expected new file mode 100644 index 000000000000..aa965f50cdf2 --- /dev/null +++ b/tests/script-based-pre/verify_std_cmd/verify_std.expected @@ -0,0 +1,11 @@ +[TEST] Run kani verify-std +Checking harness kani::check_success... +VERIFICATION:- SUCCESSFUL + +Checking harness kani::check_panic... +VERIFICATION:- SUCCESSFUL (encountered one or more panics as expected) + +Checking harness num::verify::check_non_zero... +VERIFICATION:- SUCCESSFUL + +Complete - 3 successfully verified harnesses, 0 failures, 3 total. diff --git a/tests/script-based-pre/verify_std_cmd/verify_std.sh b/tests/script-based-pre/verify_std_cmd/verify_std.sh new file mode 100755 index 000000000000..9e18ed72ca2a --- /dev/null +++ b/tests/script-based-pre/verify_std_cmd/verify_std.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +# Test `kani verify-std` subcommand. +# 1. Make a copy of the rust standard library. +# 2. Add a few Kani definitions to it + a few harnesses +# 3. Execute Kani + +set +e + +TMP_DIR="tmp_dir" + +rm -rf ${TMP_DIR} +mkdir ${TMP_DIR} + +# Create a custom standard library. +echo "[TEST] Copy standard library from the current toolchain" +SYSROOT=$(rustc --print sysroot) +STD_PATH="${SYSROOT}/lib/rustlib/src/rust/library" +cp -r "${STD_PATH}" "${TMP_DIR}" + +# Insert a small harness in one of the standard library modules. +CORE_CODE=' +#[cfg(kani)] +#[unstable(feature = "kani", issue = "none")] +pub mod kani { + pub use kani_core::proof; + + #[rustc_diagnostic_item = "KaniAnyRaw"] + #[inline(never)] + pub fn any_raw_inner() -> T { + loop {} + } + + #[inline(never)] + #[rustc_diagnostic_item = "KaniAssert"] + pub const fn assert(cond: bool, msg: &'\''static str) { + let _ = cond; + let _ = msg; + } + + #[kani_core::proof] + #[kani_core::should_panic] + fn check_panic() { + let num: u8 = any_raw_inner(); + assert!(num != 0, "Found zero"); + } + + #[kani_core::proof] + fn check_success() { + let orig: u8 = any_raw_inner(); + let mid = orig as i8; + let new = mid as u8; + assert!(orig == new, "Conversion round trip works"); + } +} +' + +STD_CODE=' +#[cfg(kani)] +mod verify { + use core::kani; + #[kani::proof] + fn check_non_zero() { + let orig: u32 = kani::any_raw_inner(); + if let Some(val) = core::num::NonZeroU32::new(orig) { + assert!(orig == val.into()); + } else { + assert!(orig == 0); + }; + } +} +' + +echo "[TEST] Modify library" +echo "${CORE_CODE}" >> ${TMP_DIR}/library/core/src/lib.rs +echo "${STD_CODE}" >> ${TMP_DIR}/library/std/src/num.rs + +# Note: Prepending with sed doesn't work on MacOs the same way it does in linux. +# sed -i '1s/^/#![cfg_attr(kani, feature(kani))]\n/' ${TMP_DIR}/library/std/src/lib.rs +cp ${TMP_DIR}/library/std/src/lib.rs ${TMP_DIR}/std_lib.rs +echo '#![cfg_attr(kani, feature(kani))]' > ${TMP_DIR}/library/std/src/lib.rs +cat ${TMP_DIR}/std_lib.rs >> ${TMP_DIR}/library/std/src/lib.rs + +echo "[TEST] Run kani verify-std" +export RUST_BACKTRACE=1 +kani verify-std -Z unstable-options "${TMP_DIR}/library" --target-dir "${TMP_DIR}/target" + +# Cleanup +rm -r ${TMP_DIR} From ffcb5adcf26f9b27214df09e2348b7e6b25ccc88 Mon Sep 17 00:00:00 2001 From: Artem Agvanian Date: Fri, 7 Jun 2024 15:11:17 -0700 Subject: [PATCH 123/225] Inject pointer validity check when casting raw pointers to references (#3221) Resolves #2975 This PR makes Kani able to check if a raw pointer is valid before casting it to a reference. To check for the pointer validity when casting it to a reference, inject asserting `__CPROVER_r_ok(ptr, sz)` into places where a raw pointer is dereferenced and a reference is immediately taken. Since this check seems to cause some of the CIs to run out of memory, it is only enabled under `-Z ptr-to-ref-cast-checks` flag. 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/args.rs | 3 + .../codegen_cprover_gotoc/codegen/assert.rs | 62 +++++++- .../codegen_cprover_gotoc/codegen/rvalue.rs | 17 ++- .../codegen/statement.rs | 1 + kani-driver/src/call_single_file.rs | 4 + kani_metadata/src/unstable.rs | 2 + tests/expected/ptr_to_ref_cast/expected | 75 ++++++++++ .../ptr_to_ref_cast/ptr_to_ref_cast.rs | 140 ++++++++++++++++++ tests/std-checks/core/Cargo.toml | 2 +- tests/std-checks/core/ptr.expected | 3 +- 10 files changed, 305 insertions(+), 4 deletions(-) create mode 100644 tests/expected/ptr_to_ref_cast/expected create mode 100644 tests/expected/ptr_to_ref_cast/ptr_to_ref_cast.rs diff --git a/kani-compiler/src/args.rs b/kani-compiler/src/args.rs index 225cb94f4264..b4d4eb3718d8 100644 --- a/kani-compiler/src/args.rs +++ b/kani-compiler/src/args.rs @@ -85,4 +85,7 @@ pub enum ExtraChecks { /// Check that produced values are valid except for uninitialized values. /// See https://github.com/model-checking/kani/issues/920. Validity, + /// Check pointer validity when casting pointers to references. + /// See https://github.com/model-checking/kani/issues/2975. + PtrToRefCast, } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs index cad3595bca50..70a65ad6ebca 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs @@ -21,7 +21,8 @@ use crate::codegen_cprover_gotoc::GotocCtx; use cbmc::goto_program::{Expr, Location, Stmt, Type}; use cbmc::InternedString; -use stable_mir::ty::Span as SpanStable; +use stable_mir::mir::{Place, ProjectionElem}; +use stable_mir::ty::{Span as SpanStable, TypeAndMut}; use strum_macros::{AsRefStr, EnumString}; use tracing::debug; @@ -323,4 +324,63 @@ impl<'tcx> GotocCtx<'tcx> { self.codegen_assert_assume(cond, PropertyClass::SanityCheck, &assert_msg, loc) } + + /// If converting a raw pointer to a reference, &(*ptr), need to inject + /// a check to make sure that the pointer points to a valid memory location, + /// since dereferencing an invalid pointer is UB in Rust. + pub fn codegen_raw_ptr_deref_validity_check( + &mut self, + place: &Place, + loc: &Location, + ) -> Option { + if let Some(ProjectionElem::Deref) = place.projection.last() { + // Create a place without the topmost dereference projection.ß + let ptr_place = { + let mut ptr_place = place.clone(); + ptr_place.projection.pop(); + ptr_place + }; + // Only inject the check if dereferencing a raw pointer. + let ptr_place_ty = self.place_ty_stable(&ptr_place); + if ptr_place_ty.kind().is_raw_ptr() { + // Extract the size of the pointee. + let pointee_size = { + let TypeAndMut { ty: pointee_ty, .. } = + ptr_place_ty.kind().builtin_deref(true).unwrap(); + let pointee_ty_layout = pointee_ty.layout().unwrap(); + pointee_ty_layout.shape().size.bytes() + }; + + // __CPROVER_r_ok fails if size == 0, so need to explicitly avoid the check. + if pointee_size != 0 { + // Encode __CPROVER_r_ok(ptr, size). + // First, generate a CBMC expression representing the pointer. + let ptr = { + let ptr_projection = self.codegen_place_stable(&ptr_place).unwrap(); + let place_ty = self.place_ty_stable(place); + if self.use_thin_pointer_stable(place_ty) { + ptr_projection.goto_expr().clone() + } else { + ptr_projection.goto_expr().clone().member("data", &self.symbol_table) + } + }; + // Then, generate a __CPROVER_r_ok check. + let raw_ptr_read_ok_expr = Expr::read_ok( + ptr.cast_to(Type::void_pointer()), + Expr::int_constant(pointee_size, Type::size_t()), + ) + .cast_to(Type::Bool); + // Finally, assert that the pointer points to a valid memory location. + let raw_ptr_read_ok = self.codegen_assert( + raw_ptr_read_ok_expr, + PropertyClass::SafetyCheck, + "dereference failure: pointer invalid", + *loc, + ); + return Some(raw_ptr_read_ok); + } + } + } + None + } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index 131e40944a0f..89a50971243b 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -1,6 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT +use crate::args::ExtraChecks; use crate::codegen_cprover_gotoc::codegen::place::ProjectedPlace; use crate::codegen_cprover_gotoc::codegen::ty_stable::pointee_type_stable; use crate::codegen_cprover_gotoc::codegen::PropertyClass; @@ -721,7 +722,21 @@ impl<'tcx> GotocCtx<'tcx> { match rv { 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) => self.codegen_place_ref_stable(&p), + Rvalue::Ref(_, _, p) | Rvalue::AddressOf(_, p) => { + let place_ref = self.codegen_place_ref_stable(&p); + if self.queries.args().ub_check.contains(&ExtraChecks::PtrToRefCast) { + let place_ref_type = place_ref.typ().clone(); + match self.codegen_raw_ptr_deref_validity_check(&p, &loc) { + Some(ptr_validity_check_expr) => Expr::statement_expression( + vec![ptr_validity_check_expr, place_ref.as_stmt(loc)], + place_ref_type, + ), + None => place_ref, + } + } else { + place_ref + } + } Rvalue::Len(p) => self.codegen_rvalue_len(p), // Rust has begun distinguishing "ptr -> num" and "num -> ptr" (providence-relevant casts) but we do not yet: // Should we? Tracking ticket: https://github.com/model-checking/kani/issues/1274 diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index ba00ff0a3dbf..0c43f566e5ac 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -337,6 +337,7 @@ impl<'tcx> GotocCtx<'tcx> { Stmt::skip(loc) } InstanceKind::Shim => { + // Since the reference is used right away here, no need to inject a check for pointer validity. let place_ref = self.codegen_place_ref_stable(place); match place_ty.kind() { TyKind::RigidTy(RigidTy::Dynamic(..)) => { diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index 335d9840e0e7..29128a02dc2a 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -139,6 +139,10 @@ impl KaniSession { flags.push("--ub-check=validity".into()) } + if self.args.common_args.unstable_features.contains(UnstableFeature::PtrToRefCastChecks) { + flags.push("--ub-check=ptr_to_ref_cast".into()) + } + if self.args.ignore_locals_lifetime { flags.push("--ignore-storage-markers".into()) } diff --git a/kani_metadata/src/unstable.rs b/kani_metadata/src/unstable.rs index 929322858d2a..9f82e4b02a64 100644 --- a/kani_metadata/src/unstable.rs +++ b/kani_metadata/src/unstable.rs @@ -89,6 +89,8 @@ pub enum UnstableFeature { ValidValueChecks, /// Ghost state and shadow memory APIs. GhostState, + /// Automatically check that pointers are valid when casting them to references. + PtrToRefCastChecks, /// Enable an unstable option or subcommand. UnstableOptions, } diff --git a/tests/expected/ptr_to_ref_cast/expected b/tests/expected/ptr_to_ref_cast/expected new file mode 100644 index 000000000000..1b449911af11 --- /dev/null +++ b/tests/expected/ptr_to_ref_cast/expected @@ -0,0 +1,75 @@ +Checking harness check_zst_deref... + +Status: FAILURE\ +Description: "dereference failure: pointer invalid"\ +in function zst_deref + +VERIFICATION:- FAILED + +Checking harness check_equal_size_deref... + +Status: SUCCESS\ +Description: "dereference failure: pointer invalid"\ +in function equal_size_deref + +Status: SUCCESS\ +Description: "dereference failure: pointer invalid"\ +in function equal_size_deref + +VERIFICATION:- SUCCESSFUL + +Checking harness check_smaller_deref... + +Status: SUCCESS\ +Description: "dereference failure: pointer invalid"\ +in function smaller_deref + +Status: SUCCESS\ +Description: "dereference failure: pointer invalid"\ +in function smaller_deref + +VERIFICATION:- SUCCESSFUL + +Checking harness check_larger_deref_struct... + +Status: FAILURE\ +Description: "dereference failure: pointer invalid"\ +in function larger_deref_struct + +VERIFICATION:- FAILED + +Checking harness check_larger_deref_into_ptr... + +Status: FAILURE\ +Description: "dereference failure: pointer invalid"\ +in function larger_deref_into_ptr + +VERIFICATION:- FAILED + +Checking harness check_larger_deref... + +Status: FAILURE\ +Description: "dereference failure: pointer invalid"\ +in function larger_deref + +VERIFICATION:- FAILED + +Checking harness check_store... + +Status: FAILURE\ +Description: "dereference failure: pointer invalid"\ +in function Store::<'_, 3>::from + +Status: SUCCESS\ +Description: "assertion failed: broken.data.len() == 3"\ +in function check_store + +VERIFICATION:- FAILED + +Summary: +Verification failed for - check_zst_deref +Verification failed for - check_larger_deref_struct +Verification failed for - check_larger_deref_into_ptr +Verification failed for - check_larger_deref +Verification failed for - check_store +Complete - 2 successfully verified harnesses, 5 failures, 7 total. \ No newline at end of file diff --git a/tests/expected/ptr_to_ref_cast/ptr_to_ref_cast.rs b/tests/expected/ptr_to_ref_cast/ptr_to_ref_cast.rs new file mode 100644 index 000000000000..3b713a34d967 --- /dev/null +++ b/tests/expected/ptr_to_ref_cast/ptr_to_ref_cast.rs @@ -0,0 +1,140 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z ptr-to-ref-cast-checks + +//! This test case checks that raw pointer validity is checked before converting it to a reference, e.g., &(*ptr). + +// 1. Original example. + +struct Store<'a, const LEN: usize> { + data: [&'a i128; LEN], +} + +impl<'a, const LEN: usize> Store<'a, LEN> { + pub fn from(var: &i64) -> Self { + let ref1: *const i64 = var; + let ref2: *const i128 = ref1 as *const i128; + unsafe { + Store { data: [&*ref2; LEN] } // ---- THIS LINE SHOULD FAIL + } + } +} + +#[kani::proof] +pub fn check_store() { + let val = 1; + let broken = Store::<3>::from(&val); + assert_eq!(broken.data.len(), 3) +} + +// 2. Make sure the error is raised when casting to a simple type of a larger size. + +pub fn larger_deref(var: &i64) { + let ref1: *const i64 = var; + let ref2: *const i128 = ref1 as *const i128; + let ref3: &i128 = unsafe { &*ref2 }; // ---- THIS LINE SHOULD FAIL +} + +#[kani::proof] +pub fn check_larger_deref() { + let var: i64 = kani::any(); + larger_deref(&var); +} + +// 3. Make sure the error is raised when casting to a simple type of a larger size and storing the result in a pointer. + +pub fn larger_deref_into_ptr(var: &i64) { + let ref1: *const i64 = var; + let ref2: *const i128 = ref1 as *const i128; + let ref3: *const i128 = unsafe { &*ref2 }; // ---- THIS LINE SHOULD FAIL +} + +#[kani::proof] +pub fn check_larger_deref_into_ptr() { + let var: i64 = kani::any(); + larger_deref_into_ptr(&var); +} + +// 4. Make sure the error is raised when casting to a struct of a larger size. + +#[derive(kani::Arbitrary)] +struct Foo { + a: u8, +} + +#[derive(kani::Arbitrary)] +struct Bar { + a: u8, + b: u64, + c: u64, +} + +pub fn larger_deref_struct(var: &Foo) { + let ref1: *const Foo = var; + let ref2: *const Bar = ref1 as *const Bar; + let ref3: &Bar = unsafe { &*ref2 }; // ---- THIS LINE SHOULD FAIL +} + +#[kani::proof] +pub fn check_larger_deref_struct() { + let var: Foo = kani::any(); + larger_deref_struct(&var); +} + +// 5. Make sure the error is not raised if the target size is smaller. + +pub fn smaller_deref(var: &i64, var_struct: &Bar) { + let ref1: *const i64 = var; + let ref2: *const i32 = ref1 as *const i32; + let ref3: &i32 = unsafe { &*ref2 }; + + let ref1_struct: *const Bar = var_struct; + let ref2_struct: *const Foo = ref1_struct as *const Foo; + let ref3_struct: &Foo = unsafe { &*ref2_struct }; +} + +#[kani::proof] +pub fn check_smaller_deref() { + let var: i64 = kani::any(); + let var_struct: Bar = kani::any(); + smaller_deref(&var, &var_struct); +} + +// 6. Make sure the error is not raised if the target size is the same. + +pub fn equal_size_deref(var: &i64, var_struct: &Foo) { + let ref1: *const i64 = var; + let ref2: &i64 = unsafe { &*ref1 }; + + let ref1_struct: *const Foo = var_struct; + let ref2_struct: &Foo = unsafe { &*ref1_struct }; +} + +#[kani::proof] +pub fn check_equal_size_deref() { + let var: i64 = kani::any(); + let var_struct: Foo = kani::any(); + equal_size_deref(&var, &var_struct); +} + +// 7. Make sure the check works with ZSTs. + +#[derive(kani::Arbitrary)] +struct Zero; + +pub fn zst_deref(var_struct: &Foo, var_zst: &Zero) { + let ref1_struct: *const Foo = var_struct; + let ref2_struct: *const Zero = ref1_struct as *const Zero; + let ref3_struct: &Zero = unsafe { &*ref2_struct }; + + let ref1_zst: *const Zero = var_zst; + let ref2_zst: *const Foo = ref1_zst as *const Foo; + let ref3_zst: &Foo = unsafe { &*ref2_zst }; // ---- THIS LINE SHOULD FAIL +} + +#[kani::proof] +pub fn check_zst_deref() { + let var_struct: Foo = kani::any(); + let var_zst: Zero = kani::any(); + zst_deref(&var_struct, &var_zst); +} diff --git a/tests/std-checks/core/Cargo.toml b/tests/std-checks/core/Cargo.toml index 9cacaa1368e3..f6e1645c3a39 100644 --- a/tests/std-checks/core/Cargo.toml +++ b/tests/std-checks/core/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" description = "This crate contains contracts and harnesses for core library" [package.metadata.kani] -unstable = { function-contracts = true, mem-predicates = true } +unstable = { function-contracts = true, mem-predicates = true, ptr-to-ref-cast-checks = true } [package.metadata.kani.flags] output-format = "terse" diff --git a/tests/std-checks/core/ptr.expected b/tests/std-checks/core/ptr.expected index 337174141ff0..43d3bd6baf60 100644 --- a/tests/std-checks/core/ptr.expected +++ b/tests/std-checks/core/ptr.expected @@ -1,3 +1,4 @@ Summary: Verification failed for - ptr::verify::check_replace_unit -Complete - 5 successfully verified harnesses, 1 failures, 6 total. +Verification failed for - ptr::verify::check_as_ref_dangling +Complete - 4 successfully verified harnesses, 2 failures, 6 total. From adc1f0df9728e39149996fad7f35068275619e88 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Sat, 8 Jun 2024 17:02:41 +0200 Subject: [PATCH 124/225] Do not turn trivially diverging loops into assume(false) (#3223) CBMC's symbolic execution by default turns `while(true) {}` loops into `assume(false)` to counter trivial non-termination of symbolic execution. When unwinding assertions are enabled, however, we should report the non-termination of such loops. Resolves: #2909 --- kani-driver/src/call_cbmc.rs | 2 ++ .../function-contract/diverging_loop.expected | 3 +++ .../expected/function-contract/diverging_loop.rs | 15 +++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 tests/expected/function-contract/diverging_loop.expected create mode 100644 tests/expected/function-contract/diverging_loop.rs diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index 7d0edf6f50e5..7a623253f3a3 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -182,7 +182,9 @@ impl KaniSession { } if self.args.checks.unwinding_on() { + // TODO: With CBMC v6 the below can be removed as those are defaults. args.push("--unwinding-assertions".into()); + args.push("--no-self-loops-to-assumptions".into()); } if self.args.extra_pointer_checks { diff --git a/tests/expected/function-contract/diverging_loop.expected b/tests/expected/function-contract/diverging_loop.expected new file mode 100644 index 000000000000..05c2856a7c31 --- /dev/null +++ b/tests/expected/function-contract/diverging_loop.expected @@ -0,0 +1,3 @@ +Failed Checks: unwinding assertion loop 0 + +VERIFICATION:- FAILED diff --git a/tests/expected/function-contract/diverging_loop.rs b/tests/expected/function-contract/diverging_loop.rs new file mode 100644 index 000000000000..219f723494f7 --- /dev/null +++ b/tests/expected/function-contract/diverging_loop.rs @@ -0,0 +1,15 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +#[kani::ensures(|result : &i32| *result == 1)] +fn foo() -> i32 { + loop {} + 2 +} + +#[kani::proof_for_contract(foo)] +#[kani::unwind(1)] +fn check_foo() { + let _ = foo(); +} From 02ac2680af0c9aa13a96f9c756748fad8bf441ea Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 11:19:04 +0200 Subject: [PATCH 125/225] Automatic cargo update to 2024-06-10 (#3246) Dependency upgrade resulting from `cargo update`. --- Cargo.lock | 62 +++++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b2857e1c7ba..31eac9d1663e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,9 +55,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys", ] @@ -140,9 +140,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.4" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" dependencies = [ "clap_builder", "clap_derive", @@ -150,9 +150,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" dependencies = [ "anstream", "anstyle", @@ -162,9 +162,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck", "proc-macro2", @@ -174,9 +174,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" @@ -810,14 +810,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -831,13 +831,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] @@ -848,9 +848,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustc-demangle" @@ -1022,9 +1022,9 @@ checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" [[package]] name = "strum_macros" -version = "0.26.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7993a8e3a9e88a00351486baae9522c91b123a088f76469e5bd5cc17198ea87" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck", "proc-macro2", @@ -1098,9 +1098,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", @@ -1119,9 +1119,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", @@ -1213,9 +1213,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unsafe-libyaml" @@ -1225,9 +1225,9 @@ checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" @@ -1384,9 +1384,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.9" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] From 554532cbef8732315d3c6b43c6c41eb64816b609 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Mon, 10 Jun 2024 13:34:16 -0400 Subject: [PATCH 126/225] Fix "unused mut" warnings created by generated code. (#3247) This change adds "unused_mut" to the list of suppressed lints for wrappers generated by the contracts macros. This will get rid of spurious errors caused by mutable parameters to functions. This fixes the example from https://github.com/model-checking/kani/issues/3010 . It can be tested by adding the example from the issues to tests/expected/test_macros/gcd.rs, creating a file tests/expected/test_macros/gcd.expected, then running ```bash cargo build-dev RUST_BACKTRACE=1 cargo run -p compiletest -- --logfile logfile.txt --suite expected --mode expected --ignored --no-fail-fast --src-base tests/expected/test_macros ``` RESOLVES #3010 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: Jacob Salzberg --- .../src/sysroot/contracts/bootstrap.rs | 2 +- .../src/sysroot/contracts/shared.rs | 2 +- .../expected/function-contract/gcd_success.rs | 20 +++++++------------ 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/library/kani_macros/src/sysroot/contracts/bootstrap.rs b/library/kani_macros/src/sysroot/contracts/bootstrap.rs index 7c35cc469254..6a48bab08070 100644 --- a/library/kani_macros/src/sysroot/contracts/bootstrap.rs +++ b/library/kani_macros/src/sysroot/contracts/bootstrap.rs @@ -85,7 +85,7 @@ impl<'a> ContractConditionsHandler<'a> { let result = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site()); self.output.extend(quote!( - #[allow(dead_code, unused_variables)] + #[allow(dead_code, unused_variables, unused_mut)] #[kanitool::is_contract_generated(recursion_wrapper)] #wrapper_sig { static mut REENTRY: bool = false; diff --git a/library/kani_macros/src/sysroot/contracts/shared.rs b/library/kani_macros/src/sysroot/contracts/shared.rs index b0f5804824ad..b0d323b12ca3 100644 --- a/library/kani_macros/src/sysroot/contracts/shared.rs +++ b/library/kani_macros/src/sysroot/contracts/shared.rs @@ -50,7 +50,7 @@ impl<'a> ContractConditionsHandler<'a> { pub fn emit_common_header(&mut self) { if self.function_state.emit_tag_attr() { self.output.extend(quote!( - #[allow(dead_code, unused_variables)] + #[allow(dead_code, unused_variables, unused_mut)] )); } self.output.extend(self.annotated_fn.attrs.iter().flat_map(Attribute::to_token_stream)); diff --git a/tests/expected/function-contract/gcd_success.rs b/tests/expected/function-contract/gcd_success.rs index 3b983a230b60..61d8fea8ec80 100644 --- a/tests/expected/function-contract/gcd_success.rs +++ b/tests/expected/function-contract/gcd_success.rs @@ -1,28 +1,22 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zfunction-contracts +#![deny(warnings)] type T = u8; /// Euclid's algorithm for calculating the GCD of two numbers #[kani::requires(x != 0 && y != 0)] #[kani::ensures(|result : &T| *result != 0 && x % *result == 0 && y % *result == 0)] -fn gcd(x: T, y: T) -> T { - let mut max = x; - let mut min = y; - if min > max { - let val = max; - max = min; - min = val; - } - +fn gcd(mut x: T, mut y: T) -> T { + (x, y) = (if x > y { x } else { y }, if x > y { y } else { x }); loop { - let res = max % min; + let res = x % y; if res == 0 { - return min; + return y; } - max = min; - min = res; + x = y; + y = res; } } From 7fd65462e35881ee5d86b241c4ddd2f18ce78d6c Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Mon, 10 Jun 2024 12:15:16 -0700 Subject: [PATCH 127/225] Refactor stubbing so Kani compiler only invoke rustc once per crate (#3245) Using stubs or function contracts as part of the `verify-std` sub-command does not work with multiple rustc executions as previous implementation. This happens because we now enable verifying dependencies, and cargo crashes due to a race condition. As soon as the first rustc invocation succeeds, cargo starts the compilation of the dependents crate. However, new executions can override files. Instead, we moved the stub logic to the new transformation framework, which is done on the top of the StableMIR body, and doesn't affect the Rust compiler session. We are now able to apply stub without restarting the compiler. This is a much better user experience as well, since multiple calls to the compiler can print the same warnings multiple times. Resolves #3072 Towards #3152 Co-authored-by: Felipe R. Monteiro --- .../compiler_interface.rs | 58 +- kani-compiler/src/kani_compiler.rs | 530 +----------------- kani-compiler/src/kani_middle/attributes.rs | 9 +- .../src/kani_middle/codegen_units.rs | 213 +++++++ kani-compiler/src/kani_middle/metadata.rs | 4 +- kani-compiler/src/kani_middle/mod.rs | 1 + kani-compiler/src/kani_middle/provide.rs | 48 +- kani-compiler/src/kani_middle/reachability.rs | 90 +-- .../src/kani_middle/stubbing/annotations.rs | 13 +- kani-compiler/src/kani_middle/stubbing/mod.rs | 101 +++- .../src/kani_middle/stubbing/transform.rs | 261 --------- .../src/kani_middle/transform/body.rs | 141 ++++- .../src/kani_middle/transform/check_values.rs | 3 +- .../src/kani_middle/transform/contracts.rs | 168 ++++++ .../src/kani_middle/transform/mod.rs | 43 +- .../src/kani_middle/transform/stubs.rs | 222 ++++++++ kani-compiler/src/kani_queries/mod.rs | 43 +- .../harness/expected | 3 +- .../modifies/check_invalid_modifies.expected | 10 +- tests/expected/issue-2589/issue_2589.expected | 2 +- .../stubbing-different-sets/stubbing.expected | 19 + .../stubbing-different-sets}/stubbing.rs | 6 +- .../check_compiler_sessions.expected | 3 - .../check_compiler_sessions.sh | 27 - .../stubbing_compiler_sessions/config.yml | 4 - .../verify_std_cmd/verify_std.expected | 5 +- .../verify_std_cmd/verify_std.sh | 17 +- .../stubbing-type-validation/expected | 17 +- 28 files changed, 987 insertions(+), 1074 deletions(-) create mode 100644 kani-compiler/src/kani_middle/codegen_units.rs delete mode 100644 kani-compiler/src/kani_middle/stubbing/transform.rs create mode 100644 kani-compiler/src/kani_middle/transform/contracts.rs create mode 100644 kani-compiler/src/kani_middle/transform/stubs.rs create mode 100644 tests/expected/stubbing-different-sets/stubbing.expected rename tests/{script-based-pre/stubbing_compiler_sessions => expected/stubbing-different-sets}/stubbing.rs (88%) delete mode 100644 tests/script-based-pre/stubbing_compiler_sessions/check_compiler_sessions.expected delete mode 100755 tests/script-based-pre/stubbing_compiler_sessions/check_compiler_sessions.sh delete mode 100644 tests/script-based-pre/stubbing_compiler_sessions/config.yml diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index 039c50eefe18..ca66e1c0e165 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -7,7 +7,8 @@ use crate::args::ReachabilityType; use crate::codegen_cprover_gotoc::GotocCtx; use crate::kani_middle::analysis; use crate::kani_middle::attributes::{is_test_harness_description, KaniAttributes}; -use crate::kani_middle::metadata::{canonical_mangled_name, gen_test_metadata}; +use crate::kani_middle::codegen_units::{CodegenUnit, CodegenUnits}; +use crate::kani_middle::metadata::gen_test_metadata; use crate::kani_middle::provide; use crate::kani_middle::reachability::{ collect_reachable_items, filter_const_crate_items, filter_crate_items, @@ -17,7 +18,7 @@ use crate::kani_middle::{check_reachable_items, dump_mir_items}; use crate::kani_queries::QueryDb; use cbmc::goto_program::Location; use cbmc::irep::goto_binary_serde::write_goto_binary_file; -use cbmc::{InternString, RoundingMode}; +use cbmc::RoundingMode; use cbmc::{InternedString, MachineModel}; use kani_metadata::artifact::convert_type; use kani_metadata::UnsupportedFeature; @@ -49,7 +50,6 @@ use stable_mir::mir::mono::{Instance, MonoItem}; use stable_mir::{CrateDef, DefId}; use std::any::Any; use std::collections::BTreeMap; -use std::collections::HashSet; use std::ffi::OsString; use std::fmt::Write; use std::fs::File; @@ -231,43 +231,43 @@ impl CodegenBackend for GotocCodegenBackend { let base_filepath = tcx.output_filenames(()).path(OutputType::Object); let base_filename = base_filepath.as_path(); let reachability = queries.args().reachability_analysis; - let mut transformer = BodyTransformation::new(&queries, tcx); let mut results = GotoCodegenResults::new(tcx, reachability); match reachability { ReachabilityType::Harnesses => { + let mut units = CodegenUnits::new(&queries, tcx); + let mut modifies_instances = vec![]; // Cross-crate collecting of all items that are reachable from the crate harnesses. - let harnesses = queries.target_harnesses(); - let mut items: HashSet<_> = HashSet::with_capacity(harnesses.len()); - items.extend(harnesses); - let harnesses = filter_crate_items(tcx, |_, instance| { - items.contains(&instance.mangled_name().intern()) - }); - for harness in harnesses { - let model_path = - queries.harness_model_path(&harness.mangled_name()).unwrap(); - let contract_metadata = - contract_metadata_for_harness(tcx, harness.def.def_id()).unwrap(); - let (gcx, items, contract_info) = self.codegen_items( - tcx, - &[MonoItem::Fn(harness)], - model_path, - &results.machine_model, - contract_metadata, - transformer, - ); - transformer = results.extend(gcx, items, None); - if let Some(assigns_contract) = contract_info { - self.queries.lock().unwrap().register_assigns_contract( - canonical_mangled_name(harness).intern(), - assigns_contract, + for unit in units.iter() { + // We reset the body cache for now because each codegen unit has different + // configurations that affect how we transform the instance body. + let mut transformer = BodyTransformation::new(&queries, tcx, &unit); + for harness in &unit.harnesses { + let model_path = units.harness_model_path(*harness).unwrap(); + let contract_metadata = + contract_metadata_for_harness(tcx, harness.def.def_id()).unwrap(); + let (gcx, items, contract_info) = self.codegen_items( + tcx, + &[MonoItem::Fn(*harness)], + model_path, + &results.machine_model, + contract_metadata, + transformer, ); + transformer = results.extend(gcx, items, None); + if let Some(assigns_contract) = contract_info { + modifies_instances.push((*harness, assigns_contract)); + } } } + units.store_modifies(&modifies_instances); + units.write_metadata(&queries, tcx); } ReachabilityType::Tests => { // We're iterating over crate items here, so what we have to codegen is the "test description" containing the // test closure that we want to execute // TODO: Refactor this code so we can guarantee that the pair (test_fn, test_desc) actually match. + let unit = CodegenUnit::default(); + let mut transformer = BodyTransformation::new(&queries, tcx, &unit); let mut descriptions = vec![]; let harnesses = filter_const_crate_items(tcx, &mut transformer, |_, item| { if is_test_harness_description(tcx, item.def) { @@ -310,6 +310,8 @@ impl CodegenBackend for GotocCodegenBackend { } ReachabilityType::None => {} ReachabilityType::PubFns => { + let unit = CodegenUnit::default(); + let transformer = BodyTransformation::new(&queries, tcx, &unit); let main_instance = stable_mir::entry_fn().map(|main_fn| Instance::try_from(main_fn).unwrap()); let local_reachable = filter_crate_items(tcx, |_, instance| { diff --git a/kani-compiler/src/kani_compiler.rs b/kani-compiler/src/kani_compiler.rs index 1793165bdf81..58c22f940352 100644 --- a/kani-compiler/src/kani_compiler.rs +++ b/kani-compiler/src/kani_compiler.rs @@ -15,34 +15,19 @@ //! in order to apply the stubs. For the subsequent runs, we add the stub configuration to //! `-C llvm-args`. -use crate::args::{Arguments, ReachabilityType}; +use crate::args::Arguments; #[cfg(feature = "cprover")] use crate::codegen_cprover_gotoc::GotocCodegenBackend; -use crate::kani_middle::attributes::is_proof_harness; use crate::kani_middle::check_crate_items; -use crate::kani_middle::metadata::gen_proof_metadata; -use crate::kani_middle::reachability::filter_crate_items; -use crate::kani_middle::stubbing::{self, harness_stub_map}; use crate::kani_queries::QueryDb; use crate::session::init_session; -use cbmc::{InternString, InternedString}; use clap::Parser; -use kani_metadata::{ArtifactType, HarnessMetadata, KaniMetadata}; use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_data_structures::fx::FxHashMap; use rustc_driver::{Callbacks, Compilation, RunCompiler}; -use rustc_hir::def_id::LOCAL_CRATE; -use rustc_hir::definitions::DefPathHash; use rustc_interface::Config; -use rustc_middle::ty::TyCtxt; -use rustc_session::config::{ErrorOutputType, OutputType}; +use rustc_session::config::ErrorOutputType; use rustc_smir::rustc_internal; use rustc_span::ErrorGuaranteed; -use std::collections::{BTreeMap, HashMap}; -use std::fs::File; -use std::io::BufWriter; -use std::mem; -use std::path::{Path, PathBuf}; use std::process::ExitCode; use std::sync::{Arc, Mutex}; use tracing::debug; @@ -69,537 +54,64 @@ fn backend(queries: Arc>) -> Box { compile_error!("No backend is available. Only supported value today is `cprover`"); } -/// A stable (across compilation sessions) identifier for the harness function. -type HarnessId = InternedString; - -/// A set of stubs. -type Stubs = BTreeMap; - -#[derive(Clone, Debug)] -struct HarnessInfo { - pub metadata: HarnessMetadata, - pub stub_map: Stubs, -} - -/// Store some relevant information about the crate compilation. -#[derive(Clone, Debug)] -struct CrateInfo { - /// The name of the crate being compiled. - pub name: String, - /// The metadata output path that shall be generated as part of the crate compilation. - pub output_path: PathBuf, -} - -/// Represents the current compilation stage. -/// -/// The Kani compiler may run the Rust compiler multiple times since stubbing has to be applied -/// to the entire Rust compiler session. -/// -/// - We always start in the [CompilationStage::Init]. -/// - After [CompilationStage::Init] we transition to either -/// - [CompilationStage::CodegenNoStubs] on a regular crate compilation, this will follow Init. -/// - [CompilationStage::CompilationSkipped], running the compiler to gather information, such as -/// `--version` will skip code generation completely, and there is no work to be done. -/// - After the [CompilationStage::CodegenNoStubs], we transition to either -/// - [CompilationStage::CodegenWithStubs] when there is at least one harness with stubs. -/// - [CompilationStage::Done] where there is no harness left to process. -/// - The [CompilationStage::CodegenWithStubs] can last multiple Rust compiler runs. Once there is -/// no harness left, we move to [CompilationStage::Done]. -/// - The final stages are either [CompilationStage::Done] or [CompilationStage::CompilationSkipped]. -/// - [CompilationStage::Done] represents the final state of the compiler after a successful -/// compilation. The crate metadata is stored here (even if no codegen was actually performed). -/// - [CompilationStage::CompilationSkipped] no compilation was actually performed. -/// No work needs to be done. -/// -/// Note: In a scenario where the compilation fails, the compiler will exit immediately, -/// independent on the stage. Any artifact produced shouldn't be used. I.e.: -/// -/// ```dot -/// graph CompilationStage { -/// Init -> {CodegenNoStubs, CompilationSkipped} -/// CodegenNoStubs -> {CodegenStubs, Done} -/// // Loop up to N harnesses times. -/// CodegenStubs -> {CodegenStubs, Done} -/// CompilationSkipped -/// Done -/// } -/// ``` -#[allow(dead_code)] -#[derive(Debug)] -enum CompilationStage { - /// Initial state that the compiler is always instantiated with. - /// In this stage, we initialize the Query and collect all harnesses. - Init, - /// State where the compiler ran but didn't actually compile anything (e.g.: --version). - CompilationSkipped, - /// Stage where the compiler will perform codegen of all harnesses that don't use stub. - CodegenNoStubs { - target_harnesses: Vec, - next_harnesses: Vec>, - all_harnesses: HashMap, - crate_info: CrateInfo, - }, - /// Stage where the compiler will codegen harnesses that use stub, one group at a time. - /// The harnesses at this stage are grouped according to the stubs they are using. For now, - /// we ensure they have the exact same set of stubs. - CodegenWithStubs { - target_harnesses: Vec, - next_harnesses: Vec>, - all_harnesses: HashMap, - crate_info: CrateInfo, - }, - Done { - metadata: Option<(KaniMetadata, CrateInfo)>, - }, -} - -impl CompilationStage { - pub fn is_init(&self) -> bool { - matches!(self, CompilationStage::Init) - } -} - /// This object controls the compiler behavior. /// /// It is responsible for initializing the query database, as well as controlling the compiler -/// state machine. For stubbing, we may require multiple iterations of the rustc driver, which is -/// controlled and configured via KaniCompiler. +/// state machine. struct KaniCompiler { - /// Store the queries database. The queries should be initialized as part of `config` when the + /// Store the query database. The queries should be initialized as part of `config` when the /// compiler state is Init. /// Note that we need to share the queries with the backend before `config` is called. pub queries: Arc>, - /// The state which the compiler is at. - stage: CompilationStage, } impl KaniCompiler { /// Create a new [KaniCompiler] instance. pub fn new() -> KaniCompiler { - KaniCompiler { queries: QueryDb::new(), stage: CompilationStage::Init } + KaniCompiler { queries: QueryDb::new() } } /// Compile the current crate with the given arguments. /// /// Since harnesses may have different attributes that affect compilation, Kani compiler can /// actually invoke the rust compiler multiple times. - pub fn run(&mut self, orig_args: Vec) -> Result<(), ErrorGuaranteed> { - loop { - debug!(next=?self.stage, "run"); - // Because this modifies `self.stage` we need to run this before - // borrowing `&self.stage` immutably - if let CompilationStage::Done { metadata: Some((metadata, _)), .. } = &mut self.stage { - let mut contracts = self - .queries - .lock() - .unwrap() - .assigns_contracts() - .map(|(k, v)| (*k, v.clone())) - .collect::>(); - for harness in - metadata.proof_harnesses.iter_mut().chain(metadata.test_harnesses.iter_mut()) - { - if let Some(modifies_contract) = - contracts.remove(&(&harness.mangled_name).intern()) - { - harness.contract = modifies_contract.into(); - } - } - assert!( - contracts.is_empty(), - "Invariant broken: not all contracts have been handled." - ) - } - match &self.stage { - CompilationStage::Init => self.run_compilation_session(&orig_args)?, - CompilationStage::CodegenNoStubs { .. } => { - unreachable!("This stage should always run in the same session as Init"); - } - CompilationStage::CodegenWithStubs { target_harnesses, all_harnesses, .. } => { - assert!(!target_harnesses.is_empty(), "expected at least one target harness"); - let target_harness_name = &target_harnesses[0]; - let target_harness = &all_harnesses[target_harness_name]; - let extra_arg = stubbing::mk_rustc_arg(&target_harness.stub_map); - let mut args = orig_args.clone(); - args.push(extra_arg); - self.run_compilation_session(&args)? - } - CompilationStage::Done { metadata: Some((kani_metadata, crate_info)) } => { - // Only store metadata for harnesses for now. - // TODO: This should only skip None. - // https://github.com/model-checking/kani/issues/2493 - if self.queries.lock().unwrap().args().reachability_analysis - == ReachabilityType::Harnesses - { - // Store metadata file. - // We delay storing the metadata so we can include information collected - // during codegen. - self.store_metadata(&kani_metadata, &crate_info.output_path); - } - return Ok(()); - } - CompilationStage::Done { metadata: None } - | CompilationStage::CompilationSkipped => { - return Ok(()); - } - }; - - self.next_stage(); - } - } - - /// Set up the next compilation stage after a `rustc` run. - fn next_stage(&mut self) { - self.stage = match &mut self.stage { - CompilationStage::Init => { - // This may occur when user passes arguments like --version or --help. - CompilationStage::Done { metadata: None } - } - CompilationStage::CodegenNoStubs { - next_harnesses, all_harnesses, crate_info, .. - } - | CompilationStage::CodegenWithStubs { - next_harnesses, - all_harnesses, - crate_info, - .. - } => { - if let Some(target_harnesses) = next_harnesses.pop() { - assert!(!target_harnesses.is_empty(), "expected at least one target harness"); - CompilationStage::CodegenWithStubs { - target_harnesses, - next_harnesses: mem::take(next_harnesses), - all_harnesses: mem::take(all_harnesses), - crate_info: crate_info.clone(), - } - } else { - CompilationStage::Done { - metadata: Some(( - generate_metadata(&crate_info, &all_harnesses), - crate_info.clone(), - )), - } - } - } - CompilationStage::Done { .. } | CompilationStage::CompilationSkipped => { - unreachable!() - } - }; - } - - /// Run the Rust compiler with the given arguments and pass `&mut self` to handle callbacks. - fn run_compilation_session(&mut self, args: &[String]) -> Result<(), ErrorGuaranteed> { + pub fn run(&mut self, args: Vec) -> Result<(), ErrorGuaranteed> { debug!(?args, "run_compilation_session"); let queries = self.queries.clone(); - let mut compiler = RunCompiler::new(args, self); + let mut compiler = RunCompiler::new(&args, self); compiler.set_make_codegen_backend(Some(Box::new(move |_cfg| backend(queries)))); compiler.run()?; Ok(()) } - - /// Gather and process all harnesses from this crate that shall be compiled. - fn process_harnesses(&self, tcx: TyCtxt) -> CompilationStage { - let crate_info = CrateInfo { - name: tcx.crate_name(LOCAL_CRATE).as_str().into(), - output_path: metadata_output_path(tcx), - }; - if self.queries.lock().unwrap().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 def_path = harness.mangled_name().intern(); - let metadata = gen_proof_metadata(tcx, harness, &base_filename); - let stub_map = harness_stub_map(tcx, harness, &metadata); - (def_path, HarnessInfo { metadata, stub_map }) - }) - .collect::>(); - - let (no_stubs, with_stubs): (Vec<_>, Vec<_>) = - if self.queries.lock().unwrap().args().stubbing_enabled { - // Partition harnesses that don't have stub with the ones with stub. - all_harnesses - .keys() - .cloned() - .partition(|harness| all_harnesses[harness].stub_map.is_empty()) - } else { - // Generate code without stubs. - (all_harnesses.keys().cloned().collect(), vec![]) - }; - - // Even if no_stubs is empty we still need to store rustc metadata. - CompilationStage::CodegenNoStubs { - target_harnesses: no_stubs, - next_harnesses: group_by_stubs(with_stubs, &all_harnesses), - all_harnesses, - crate_info, - } - } else { - // Leave other reachability type handling as is for now. - CompilationStage::CodegenNoStubs { - target_harnesses: vec![], - next_harnesses: vec![], - all_harnesses: HashMap::default(), - crate_info, - } - } - } - - /// Prepare the query for the next codegen stage. - fn prepare_codegen(&mut self) -> Compilation { - debug!(stage=?self.stage, "prepare_codegen"); - match &self.stage { - CompilationStage::CodegenNoStubs { target_harnesses, all_harnesses, .. } - | CompilationStage::CodegenWithStubs { target_harnesses, all_harnesses, .. } => { - debug!( - harnesses=?target_harnesses - .iter() - .map(|h| &all_harnesses[h].metadata.pretty_name) - .collect::>(), - "prepare_codegen" - ); - let queries = &mut (*self.queries.lock().unwrap()); - queries.harnesses_info = target_harnesses - .iter() - .map(|harness| { - (*harness, all_harnesses[harness].metadata.goto_file.clone().unwrap()) - }) - .collect(); - Compilation::Continue - } - CompilationStage::Init - | CompilationStage::Done { .. } - | CompilationStage::CompilationSkipped => unreachable!(), - } - } - - /// Write the metadata to a file - fn store_metadata(&self, metadata: &KaniMetadata, filename: &Path) { - debug!(?filename, "store_metadata"); - let out_file = File::create(filename).unwrap(); - let writer = BufWriter::new(out_file); - if self.queries.lock().unwrap().args().output_pretty_json { - serde_json::to_writer_pretty(writer, &metadata).unwrap(); - } else { - serde_json::to_writer(writer, &metadata).unwrap(); - } - } -} - -/// Group the harnesses by their stubs. -fn group_by_stubs( - harnesses: Vec, - all_harnesses: &HashMap, -) -> Vec> { - let mut per_stubs: BTreeMap<&Stubs, Vec> = BTreeMap::default(); - for harness in harnesses { - per_stubs.entry(&all_harnesses[&harness].stub_map).or_default().push(harness) - } - per_stubs.into_values().collect() } /// Use default function implementations. impl Callbacks for KaniCompiler { /// Configure the [KaniCompiler] `self` object during the [CompilationStage::Init]. fn config(&mut self, config: &mut Config) { - if self.stage.is_init() { - let mut args = vec!["kani-compiler".to_string()]; - 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 { .. })); - // Configure queries. - let queries = &mut (*self.queries.lock().unwrap()); + let mut args = vec!["kani-compiler".to_string()]; + 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 { .. })); - queries.set_args(args); - - debug!(?queries, "config end"); - } + // Configure queries. + let queries = &mut (*self.queries.lock().unwrap()); + queries.set_args(args); + debug!(?queries, "config end"); } - /// During the initialization state, we collect the crate harnesses and prepare for codegen. + /// After analysis, we check the crate items for Kani API misuse or configuration issues. fn after_analysis<'tcx>( &mut self, _compiler: &rustc_interface::interface::Compiler, rustc_queries: &'tcx rustc_interface::Queries<'tcx>, ) -> Compilation { - if self.stage.is_init() { - self.stage = rustc_queries.global_ctxt().unwrap().enter(|tcx| { - rustc_internal::run(tcx, || { - check_crate_items(tcx, self.queries.lock().unwrap().args().ignore_global_asm); - self.process_harnesses(tcx) - }) - .unwrap() + rustc_queries.global_ctxt().unwrap().enter(|tcx| { + rustc_internal::run(tcx, || { + check_crate_items(tcx, self.queries.lock().unwrap().args().ignore_global_asm); }) - } - - self.prepare_codegen() - } -} - -/// Generate [KaniMetadata] for the target crate. -fn generate_metadata( - crate_info: &CrateInfo, - all_harnesses: &HashMap, -) -> KaniMetadata { - let (proof_harnesses, test_harnesses) = all_harnesses - .values() - .map(|info| &info.metadata) - .cloned() - .partition(|md| md.attributes.proof); - KaniMetadata { - crate_name: crate_info.name.clone(), - proof_harnesses, - unsupported_features: vec![], - test_harnesses, - } -} - -/// Extract the filename for the metadata file. -fn metadata_output_path(tcx: TyCtxt) -> PathBuf { - let filepath = tcx.output_filenames(()).path(OutputType::Object); - let filename = filepath.as_path(); - filename.with_extension(ArtifactType::Metadata).to_path_buf() -} - -#[cfg(test)] -mod tests { - use super::*; - use kani_metadata::HarnessAttributes; - use rustc_data_structures::fingerprint::Fingerprint; - - fn mock_next_harness_id() -> HarnessId { - static mut COUNTER: u64 = 0; - unsafe { COUNTER += 1 }; - let id = unsafe { COUNTER }; - format!("mod::harness-{id}").intern() - } - - fn mock_next_stub_id() -> DefPathHash { - static mut COUNTER: u64 = 0; - unsafe { COUNTER += 1 }; - let id = unsafe { COUNTER }; - DefPathHash(Fingerprint::new(id, 0)) - } - - fn mock_metadata(name: String, krate: String) -> HarnessMetadata { - HarnessMetadata { - pretty_name: name.clone(), - mangled_name: name.clone(), - original_file: format!("{}.rs", krate), - crate_name: krate, - original_start_line: 10, - original_end_line: 20, - goto_file: None, - attributes: HarnessAttributes::default(), - contract: Default::default(), - } - } - - fn mock_info_with_stubs(stub_map: Stubs) -> HarnessInfo { - HarnessInfo { metadata: mock_metadata("dummy".to_string(), "crate".to_string()), stub_map } - } - - #[test] - fn test_group_by_stubs_works() { - // Set up the inputs - let harness_1 = mock_next_harness_id(); - let harness_2 = mock_next_harness_id(); - let harness_3 = mock_next_harness_id(); - let harnesses = vec![harness_1, harness_2, harness_3]; - - let stub_1 = (mock_next_stub_id(), mock_next_stub_id()); - let stub_2 = (mock_next_stub_id(), mock_next_stub_id()); - let stub_3 = (mock_next_stub_id(), mock_next_stub_id()); - let stub_4 = (stub_3.0, mock_next_stub_id()); - - let set_1 = Stubs::from([stub_1, stub_2, stub_3]); - let set_2 = Stubs::from([stub_1, stub_2, stub_4]); - let set_3 = Stubs::from([stub_1, stub_3, stub_2]); - assert_eq!(set_1, set_3); - assert_ne!(set_1, set_2); - - let harnesses_info = HashMap::from([ - (harness_1, mock_info_with_stubs(set_1)), - (harness_2, mock_info_with_stubs(set_2)), - (harness_3, mock_info_with_stubs(set_3)), - ]); - assert_eq!(harnesses_info.len(), 3); - - // Run the function under test. - let grouped = group_by_stubs(harnesses, &harnesses_info); - - // Verify output. - assert_eq!(grouped.len(), 2); - assert!( - grouped.contains(&vec![harness_1, harness_3]) - || grouped.contains(&vec![harness_3, harness_1]) - ); - assert!(grouped.contains(&vec![harness_2])); - } - - #[test] - fn test_generate_metadata() { - // Mock inputs. - let name = "my_crate".to_string(); - let crate_info = CrateInfo { name: name.clone(), output_path: PathBuf::default() }; - - let mut info = mock_info_with_stubs(Stubs::default()); - info.metadata.attributes.proof = true; - let id = mock_next_harness_id(); - let all_harnesses = HashMap::from([(id, info.clone())]); - - // Call generate metadata. - let metadata = generate_metadata(&crate_info, &all_harnesses); - - // Check output. - assert_eq!(metadata.crate_name, name); - assert_eq!(metadata.proof_harnesses.len(), 1); - assert_eq!(*metadata.proof_harnesses.first().unwrap(), info.metadata); - } - - #[test] - fn test_generate_empty_metadata() { - // Mock inputs. - let name = "my_crate".to_string(); - let crate_info = CrateInfo { name: name.clone(), output_path: PathBuf::default() }; - let all_harnesses = HashMap::new(); - - // Call generate metadata. - let metadata = generate_metadata(&crate_info, &all_harnesses); - - // Check output. - assert_eq!(metadata.crate_name, name); - assert_eq!(metadata.proof_harnesses.len(), 0); - } - - #[test] - fn test_generate_metadata_with_multiple_harness() { - // Mock inputs. - let krate = "my_crate".to_string(); - let crate_info = CrateInfo { name: krate.clone(), output_path: PathBuf::default() }; - - let harnesses = ["h1", "h2", "h3"]; - let infos = harnesses.map(|harness| { - let mut metadata = mock_metadata(harness.to_string(), krate.clone()); - metadata.attributes.proof = true; - (mock_next_harness_id(), HarnessInfo { stub_map: Stubs::default(), metadata }) + .unwrap() }); - let all_harnesses = HashMap::from(infos.clone()); - - // Call generate metadata. - let metadata = generate_metadata(&crate_info, &all_harnesses); - - // Check output. - assert_eq!(metadata.crate_name, krate); - assert_eq!(metadata.proof_harnesses.len(), infos.len()); - assert!( - metadata - .proof_harnesses - .iter() - .all(|harness| harnesses.contains(&&*harness.pretty_name)) - ); + Compilation::Continue } } diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index 4f9dce49af95..e2617739e461 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -198,10 +198,6 @@ impl<'tcx> KaniAttributes<'tcx> { .collect() } - pub(crate) fn is_contract_generated(&self) -> bool { - self.map.contains_key(&KaniAttributeKind::IsContractGenerated) - } - pub(crate) fn has_recursion(&self) -> bool { self.map.contains_key(&KaniAttributeKind::Recursion) } @@ -238,6 +234,11 @@ impl<'tcx> KaniAttributes<'tcx> { .map(|target| expect_key_string_value(self.tcx.sess, target)) } + pub fn proof_for_contract(&self) -> Option> { + self.expect_maybe_one(KaniAttributeKind::ProofForContract) + .map(|target| expect_key_string_value(self.tcx.sess, target)) + } + pub fn inner_check(&self) -> Option> { self.eval_sibling_attribute(KaniAttributeKind::InnerCheck) } diff --git a/kani-compiler/src/kani_middle/codegen_units.rs b/kani-compiler/src/kani_middle/codegen_units.rs new file mode 100644 index 000000000000..260b363a868a --- /dev/null +++ b/kani-compiler/src/kani_middle/codegen_units.rs @@ -0,0 +1,213 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This module is responsible for extracting grouping harnesses that can be processed together +//! by codegen. +//! +//! 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::kani_middle::attributes::is_proof_harness; +use crate::kani_middle::metadata::gen_proof_metadata; +use crate::kani_middle::reachability::filter_crate_items; +use crate::kani_middle::stubbing::{check_compatibility, harness_stub_map}; +use crate::kani_queries::QueryDb; +use kani_metadata::{ArtifactType, AssignsContract, HarnessMetadata, KaniMetadata}; +use rustc_hir::def_id::{DefId, DefPathHash}; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::OutputType; +use rustc_smir::rustc_internal; +use stable_mir::mir::mono::Instance; +use stable_mir::ty::{FnDef, RigidTy, TyKind}; +use stable_mir::CrateDef; +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::fs::File; +use std::io::BufWriter; +use std::path::{Path, PathBuf}; +use tracing::debug; + +/// A stable (across compilation sessions) identifier for the harness function. +type Harness = Instance; + +/// A set of stubs. +pub type Stubs = HashMap; + +/// Store some relevant information about the crate compilation. +#[derive(Clone, Debug)] +struct CrateInfo { + /// The name of the crate being compiled. + pub name: String, +} + +/// We group the harnesses that have the same stubs. +pub struct CodegenUnits { + units: Vec, + harness_info: HashMap, + crate_info: CrateInfo, +} + +#[derive(Clone, Default, Debug)] +pub struct CodegenUnit { + pub harnesses: Vec, + pub stubs: Stubs, +} + +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::>(); + + // 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 } + } + } + + pub fn iter(&self) -> impl Iterator { + self.units.iter() + } + + /// We store which instance of modifies was generated. + pub fn store_modifies(&mut self, harness_modifies: &[(Harness, AssignsContract)]) { + for (harness, modifies) in harness_modifies { + self.harness_info.get_mut(harness).unwrap().contract = Some(modifies.clone()); + } + } + + /// Write compilation metadata into a file. + pub fn write_metadata(&self, queries: &QueryDb, tcx: TyCtxt) { + let metadata = self.generate_metadata(); + let outpath = metadata_output_path(tcx); + store_metadata(queries, &metadata, &outpath); + } + + pub fn harness_model_path(&self, harness: Harness) -> Option<&PathBuf> { + self.harness_info[&harness].goto_file.as_ref() + } + + /// Generate [KaniMetadata] for the target crate. + fn generate_metadata(&self) -> KaniMetadata { + let (proof_harnesses, test_harnesses) = + self.harness_info.values().cloned().partition(|md| md.attributes.proof); + KaniMetadata { + crate_name: self.crate_info.name.clone(), + proof_harnesses, + unsupported_features: vec![], + test_harnesses, + } + } +} + +fn stub_def(tcx: TyCtxt, def_id: DefId) -> FnDef { + let ty_internal = tcx.type_of(def_id).instantiate_identity(); + let ty = rustc_internal::stable(ty_internal); + if let TyKind::RigidTy(RigidTy::FnDef(def, _)) = ty.kind() { + def + } else { + unreachable!("Expected stub function for `{:?}`, but found: {ty}", tcx.def_path(def_id)) + } +} + +/// Group the harnesses by their stubs. +fn group_by_stubs( + tcx: TyCtxt, + all_harnesses: &HashMap, +) -> Vec { + let mut per_stubs: HashMap, CodegenUnit> = + HashMap::default(); + for (harness, metadata) in all_harnesses { + let stub_ids = harness_stub_map(tcx, *harness, metadata); + let stub_map = stub_ids + .iter() + .map(|(k, v)| (tcx.def_path_hash(*k), tcx.def_path_hash(*v))) + .collect::>(); + if let Some(unit) = per_stubs.get_mut(&stub_map) { + unit.harnesses.push(*harness); + } else { + let stubs = stub_ids + .iter() + .map(|(from, to)| (stub_def(tcx, *from), stub_def(tcx, *to))) + .collect::>(); + let stubs = apply_transitivity(tcx, *harness, stubs); + per_stubs.insert(stub_map, CodegenUnit { stubs, harnesses: vec![*harness] }); + } + } + per_stubs.into_values().collect() +} + +/// Extract the filename for the metadata file. +fn metadata_output_path(tcx: TyCtxt) -> PathBuf { + let filepath = tcx.output_filenames(()).path(OutputType::Object); + let filename = filepath.as_path(); + filename.with_extension(ArtifactType::Metadata).to_path_buf() +} + +/// Write the metadata to a file +fn store_metadata(queries: &QueryDb, metadata: &KaniMetadata, filename: &Path) { + debug!(?filename, "store_metadata"); + let out_file = File::create(filename).unwrap(); + let writer = BufWriter::new(out_file); + if queries.args().output_pretty_json { + serde_json::to_writer_pretty(writer, &metadata).unwrap(); + } else { + serde_json::to_writer(writer, &metadata).unwrap(); + } +} + +/// Validate the unit configuration. +fn validate_units(tcx: TyCtxt, units: &[CodegenUnit]) { + for unit in units { + for (from, to) in &unit.stubs { + // We use harness span since we don't keep the attribute span. + let Err(msg) = check_compatibility(tcx, *from, *to) else { continue }; + let span = unit.harnesses.first().unwrap().def.span(); + tcx.dcx().span_err(rustc_internal::internal(tcx, span), msg); + } + } + tcx.dcx().abort_if_errors(); +} + +/// Apply stub transitivity operations. +/// +/// If `fn1` is stubbed by `fn2`, and `fn2` is stubbed by `fn3`, `f1` is in fact stubbed by `fn3`. +fn apply_transitivity(tcx: TyCtxt, harness: Harness, stubs: Stubs) -> Stubs { + let mut new_stubs = Stubs::with_capacity(stubs.len()); + for (orig, new) in stubs.iter() { + let mut new_fn = *new; + let mut visited = HashSet::new(); + while let Some(stub) = stubs.get(&new_fn) { + if !visited.insert(stub) { + // Visiting the same stub, i.e. found cycle. + let span = harness.def.span(); + tcx.dcx().span_err( + rustc_internal::internal(tcx, span), + format!( + "Cannot stub `{}`. Stub configuration for harness `{}` has a cycle", + orig.name(), + harness.def.name(), + ), + ); + break; + } + new_fn = *stub; + } + new_stubs.insert(*orig, new_fn); + } + new_stubs +} diff --git a/kani-compiler/src/kani_middle/metadata.rs b/kani-compiler/src/kani_middle/metadata.rs index 9236de48308f..2bf1531a2a1a 100644 --- a/kani-compiler/src/kani_middle/metadata.rs +++ b/kani-compiler/src/kani_middle/metadata.rs @@ -23,7 +23,7 @@ pub fn canonical_mangled_name(instance: Instance) -> String { /// Create the harness metadata for a proof harness for a given function. pub fn gen_proof_metadata(tcx: TyCtxt, instance: Instance, base_name: &Path) -> HarnessMetadata { let def = instance.def; - let attributes = KaniAttributes::for_instance(tcx, instance).harness_attributes(); + let kani_attributes = KaniAttributes::for_instance(tcx, instance); let pretty_name = instance.name(); let mangled_name = canonical_mangled_name(instance); @@ -40,7 +40,7 @@ pub fn gen_proof_metadata(tcx: TyCtxt, instance: Instance, base_name: &Path) -> original_file: loc.filename, original_start_line: loc.start_line, original_end_line: loc.end_line, - attributes, + attributes: kani_attributes.harness_attributes(), // TODO: This no longer needs to be an Option. goto_file: Some(model_file), contract: Default::default(), diff --git a/kani-compiler/src/kani_middle/mod.rs b/kani-compiler/src/kani_middle/mod.rs index 17992953d5c7..257d337cd576 100644 --- a/kani-compiler/src/kani_middle/mod.rs +++ b/kani-compiler/src/kani_middle/mod.rs @@ -32,6 +32,7 @@ use self::attributes::KaniAttributes; pub mod analysis; pub mod attributes; +pub mod codegen_units; pub mod coercion; mod intrinsics; pub mod metadata; diff --git a/kani-compiler/src/kani_middle/provide.rs b/kani-compiler/src/kani_middle/provide.rs index d9197493f4db..91b830a2349b 100644 --- a/kani-compiler/src/kani_middle/provide.rs +++ b/kani-compiler/src/kani_middle/provide.rs @@ -6,16 +6,10 @@ use crate::args::{Arguments, ReachabilityType}; use crate::kani_middle::intrinsics::ModelIntrinsics; -use crate::kani_middle::reachability::{collect_reachable_items, filter_crate_items}; -use crate::kani_middle::stubbing; -use crate::kani_middle::transform::BodyTransformation; use crate::kani_queries::QueryDb; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::util::Providers; -use rustc_middle::{mir::Body, query::queries, ty::TyCtxt}; -use stable_mir::mir::mono::MonoItem; - -use crate::kani_middle::KaniAttributes; +use rustc_middle::{mir::Body, ty::TyCtxt}; /// Sets up rustc's query mechanism to apply Kani's custom queries to code from /// a crate. @@ -25,10 +19,6 @@ pub fn provide(providers: &mut Providers, queries: &QueryDb) { // Don't override queries if we are only compiling our dependencies. providers.optimized_mir = run_mir_passes; providers.extern_queries.optimized_mir = run_mir_passes_extern; - if args.stubbing_enabled { - // TODO: Check if there's at least one stub being applied. - providers.collect_and_partition_mono_items = collect_and_partition_mono_items; - } } } @@ -61,42 +51,8 @@ fn run_kani_mir_passes<'tcx>( body: &'tcx Body<'tcx>, ) -> &'tcx Body<'tcx> { tracing::debug!(?def_id, "Run Kani transformation passes"); - let mut transformed_body = stubbing::transform(tcx, def_id, body); - stubbing::transform_foreign_functions(tcx, &mut transformed_body); - let item_attributes = KaniAttributes::for_item(tcx, def_id); - // If we apply `transform_any_modifies` in all contract-generated items, - // we will ended up instantiating `kani::any_modifies` for the replace function - // every time, even if we are only checking the contract, because the function - // is always included during contract instrumentation. Thus, we must only apply - // the transformation if we are using a verified stub or in the presence of recursion. - if item_attributes.is_contract_generated() - && (stubbing::get_stub_key(tcx, def_id).is_some() || item_attributes.has_recursion()) - { - stubbing::transform_any_modifies(tcx, &mut transformed_body); - } + let mut transformed_body = body.clone(); // This should be applied after stubbing so user stubs take precedence. ModelIntrinsics::run_pass(tcx, &mut transformed_body); tcx.arena.alloc(transformed_body) } - -/// Runs a reachability analysis before running the default -/// `collect_and_partition_mono_items` query. The reachability analysis finds -/// trait mismatches introduced by stubbing and performs a graceful exit in -/// these cases. Left to its own devices, the default query panics. -/// This is an issue when compiling a library, since the crate metadata is -/// generated (using this query) before code generation begins (which is -/// when we normally run the reachability analysis). -fn collect_and_partition_mono_items( - tcx: TyCtxt, - key: (), -) -> queries::collect_and_partition_mono_items::ProvidedValue { - rustc_smir::rustc_internal::run(tcx, || { - let local_reachable = - filter_crate_items(tcx, |_, _| true).into_iter().map(MonoItem::Fn).collect::>(); - - // We do not actually need the value returned here. - collect_reachable_items(tcx, &mut BodyTransformation::dummy(), &local_reachable); - }) - .unwrap(); - (rustc_interface::DEFAULT_QUERY_PROVIDERS.collect_and_partition_mono_items)(tcx, key) -} diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 71eb4931aad4..186bd65a7840 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -33,10 +33,8 @@ use stable_mir::ty::{Allocation, ClosureKind, ConstantKind, RigidTy, Ty, TyKind} use stable_mir::CrateItem; use stable_mir::{CrateDef, ItemKind}; -use crate::kani_middle::attributes::matches_diagnostic as matches_function; use crate::kani_middle::coercion; use crate::kani_middle::coercion::CoercionBase; -use crate::kani_middle::stubbing::{get_stub, validate_instance}; use crate::kani_middle::transform::BodyTransformation; /// Collect all reachable items starting from the given starting points. @@ -113,12 +111,8 @@ where 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(), - instance: &instance, - }; + let mut collector = + MonoItemsFnCollector { tcx, body: &body, collected: FxHashSet::default() }; collector.visit_body(&body); roots.extend(collector.collected.into_iter()); } @@ -185,19 +179,11 @@ impl<'tcx, 'a> MonoItemsCollector<'tcx, 'a> { /// Visit a function and collect all mono-items reachable from its instructions. fn visit_fn(&mut self, instance: Instance) -> Vec { let _guard = debug_span!("visit_fn", function=?instance).entered(); - if validate_instance(self.tcx, instance) { - let body = self.transformer.body(self.tcx, instance); - let mut collector = MonoItemsFnCollector { - tcx: self.tcx, - collected: FxHashSet::default(), - body: &body, - instance: &instance, - }; - collector.visit_body(&body); - collector.collected.into_iter().collect() - } else { - vec![] - } + let body = self.transformer.body(self.tcx, instance); + let mut collector = + MonoItemsFnCollector { tcx: self.tcx, collected: FxHashSet::default(), body: &body }; + collector.visit_body(&body); + collector.collected.into_iter().collect() } /// Visit a static object and collect drop / initialization functions. @@ -229,7 +215,6 @@ struct MonoItemsFnCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, collected: FxHashSet, body: &'a Body, - instance: &'a Instance, } impl<'a, 'tcx> MonoItemsFnCollector<'a, 'tcx> { @@ -403,65 +388,8 @@ impl<'a, 'tcx> MirVisitor for MonoItemsFnCollector<'a, 'tcx> { TerminatorKind::Call { ref func, .. } => { let fn_ty = func.ty(self.body.locals()).unwrap(); if let TyKind::RigidTy(RigidTy::FnDef(fn_def, args)) = fn_ty.kind() { - let instance_opt = Instance::resolve(fn_def, &args).ok(); - match instance_opt { - None => { - let caller = CrateItem::try_from(*self.instance).unwrap().name(); - let callee = fn_def.name(); - // Check if the current function has been stubbed. - if let Some(stub) = get_stub( - self.tcx, - rustc_internal::internal(self.tcx, self.instance).def_id(), - ) { - // During the MIR stubbing transformation, we do not - // force type variables in the stub's signature to - // implement the same traits as those in the - // original function/method. A trait mismatch shows - // up here, when we try to resolve a trait method - - // FIXME: This assumes the type resolving the - // trait is the first argument, but that isn't - // necessarily true. It could be any argument or - // even the return type, for instance for a - // trait like `FromIterator`. - 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, terminator.span), - format!( - "`{}` doesn't implement \ - `{}`. The function `{}` \ - cannot be stubbed by `{}` due to \ - generic bounds not being met. Callee: {}", - receiver_ty, - trait_, - caller, - self.tcx.def_path_str(stub), - callee, - ), - ); - } else if matches_function(self.tcx, self.instance.def, "KaniAny") { - 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, terminator.span), - format!( - "`{}` doesn't implement \ - `{}`. Callee: `{}`\nPlease, check whether the type of all \ - objects in the modifies clause (including return types) \ - implement `{}`.\nThis is a strict condition to use \ - function contracts as verified stubs.", - receiver_ty, trait_, callee, trait_, - ), - ); - } else { - panic!("unable to resolve call to `{callee}` in `{caller}`") - } - } - Some(instance) => self.collect_instance(instance, true), - }; + let instance = Instance::resolve(fn_def, &args).unwrap(); + self.collect_instance(instance, true); } else { assert!( matches!(fn_ty.kind().rigid(), Some(RigidTy::FnPtr(..))), diff --git a/kani-compiler/src/kani_middle/stubbing/annotations.rs b/kani-compiler/src/kani_middle/stubbing/annotations.rs index 26e508707207..9fc7be491d85 100644 --- a/kani-compiler/src/kani_middle/stubbing/annotations.rs +++ b/kani-compiler/src/kani_middle/stubbing/annotations.rs @@ -2,11 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! This file contains code for extracting stubbing-related attributes. -use std::collections::BTreeMap; +use std::collections::HashMap; use kani_metadata::Stub; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::definitions::DefPathHash; use rustc_middle::ty::TyCtxt; use crate::kani_middle::resolve::resolve_fn; @@ -42,21 +41,19 @@ pub fn update_stub_mapping( tcx: TyCtxt, harness: LocalDefId, stub: &Stub, - stub_pairs: &mut BTreeMap, + stub_pairs: &mut HashMap, ) { if let Some((orig_id, stub_id)) = stub_def_ids(tcx, harness, stub) { - let orig_hash = tcx.def_path_hash(orig_id); - let stub_hash = tcx.def_path_hash(stub_id); - let other_opt = stub_pairs.insert(orig_hash, stub_hash); + let other_opt = stub_pairs.insert(orig_id, stub_id); if let Some(other) = other_opt { - if other != stub_hash { + 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(tcx.def_path_hash_to_def_id(other, &())) + tcx.def_path_str(other) ), ); } diff --git a/kani-compiler/src/kani_middle/stubbing/mod.rs b/kani-compiler/src/kani_middle/stubbing/mod.rs index 37426bfe0b77..1abcc0ec4f80 100644 --- a/kani-compiler/src/kani_middle/stubbing/mod.rs +++ b/kani-compiler/src/kani_middle/stubbing/mod.rs @@ -3,21 +3,21 @@ //! This module contains code for implementing stubbing. mod annotations; -mod transform; +use itertools::Itertools; use rustc_span::DUMMY_SP; -use std::collections::BTreeMap; +use std::collections::HashMap; use tracing::{debug, trace}; -pub use self::transform::*; use kani_metadata::HarnessMetadata; -use rustc_hir::definitions::DefPathHash; +use rustc_hir::def_id::DefId; use rustc_middle::mir::Const; use rustc_middle::ty::{self, EarlyBinder, ParamEnv, TyCtxt, TypeFoldable}; use rustc_smir::rustc_internal; use stable_mir::mir::mono::Instance; use stable_mir::mir::visit::{Location, MirVisitor}; use stable_mir::mir::Constant; +use stable_mir::ty::FnDef; use stable_mir::{CrateDef, CrateItem}; use self::annotations::update_stub_mapping; @@ -27,16 +27,89 @@ pub fn harness_stub_map( tcx: TyCtxt, harness: Instance, metadata: &HarnessMetadata, -) -> BTreeMap { +) -> HashMap { let def_id = rustc_internal::internal(tcx, harness.def.def_id()); let attrs = &metadata.attributes; - let mut stub_pairs = BTreeMap::default(); + let mut stub_pairs = HashMap::default(); for stubs in &attrs.stubs { update_stub_mapping(tcx, def_id.expect_local(), stubs, &mut stub_pairs); } stub_pairs } +/// Checks whether the stub is compatible with the original function/method: do +/// the arities and types (of the parameters and return values) match up? This +/// does **NOT** check whether the type variables are constrained to implement +/// the same traits; trait mismatches are checked during monomorphization. +pub fn check_compatibility(tcx: TyCtxt, old_def: FnDef, new_def: FnDef) -> Result<(), String> { + // TODO: Validate stubs that do not have body. + // We could potentially look at the function signature to see if they match. + // However, they will include region information which can make types different. + let Some(old_body) = old_def.body() else { return Ok(()) }; + let Some(new_body) = new_def.body() else { return Ok(()) }; + // Check whether the arities match. + if old_body.arg_locals().len() != new_body.arg_locals().len() { + let msg = format!( + "arity mismatch: original function/method `{}` takes {} argument(s), stub `{}` takes {}", + old_def.name(), + old_body.arg_locals().len(), + new_def.name(), + new_body.arg_locals().len(), + ); + return Err(msg); + } + // Check whether the numbers of generic parameters match. + let old_def_id = rustc_internal::internal(tcx, old_def.def_id()); + let new_def_id = rustc_internal::internal(tcx, new_def.def_id()); + let old_num_generics = tcx.generics_of(old_def_id).count(); + let stub_num_generics = tcx.generics_of(new_def_id).count(); + if old_num_generics != stub_num_generics { + let msg = format!( + "mismatch in the number of generic parameters: original function/method `{}` takes {} generic parameters(s), stub `{}` takes {}", + old_def.name(), + old_num_generics, + new_def.name(), + stub_num_generics + ); + return Err(msg); + } + // Check whether the types match. Index 0 refers to the returned value, + // indices [1, `arg_count`] refer to the parameters. + // TODO: We currently force generic parameters in the stub to have exactly + // the same names as their counterparts in the original function/method; + // instead, we should be checking for the equivalence of types up to the + // renaming of generic parameters. + // + let old_ret_ty = old_body.ret_local().ty; + let new_ret_ty = new_body.ret_local().ty; + let mut diff = vec![]; + if old_ret_ty != new_ret_ty { + diff.push(format!("Expected return type `{old_ret_ty}`, but found `{new_ret_ty}`")); + } + for (i, (old_arg, new_arg)) in + old_body.arg_locals().iter().zip(new_body.arg_locals().iter()).enumerate() + { + if old_arg.ty != new_arg.ty { + diff.push(format!( + "Expected type `{}` for parameter {}, but found `{}`", + old_arg.ty, + i + 1, + new_arg.ty + )); + } + } + if !diff.is_empty() { + Err(format!( + "Cannot stub `{}` by `{}`.\n - {}", + old_def.name(), + new_def.name(), + diff.iter().join("\n - ") + )) + } else { + Ok(()) + } +} + /// Validate that an instance body can be instantiated. /// /// Stubbing may cause an instance to not be correctly instantiated since we delay checking its @@ -44,17 +117,13 @@ pub fn harness_stub_map( /// /// In stable MIR, trying to retrieve an `Instance::body()` will ICE if we cannot evaluate a /// constant as expected. For now, use internal APIs to anticipate this issue. -pub fn validate_instance(tcx: TyCtxt, instance: Instance) -> bool { +pub fn validate_stub_const(tcx: TyCtxt, instance: Instance) -> bool { + debug!(?instance, "validate_instance"); + let item = CrateItem::try_from(instance).unwrap(); let internal_instance = rustc_internal::internal(tcx, instance); - if get_stub(tcx, internal_instance.def_id()).is_some() { - debug!(?instance, "validate_instance"); - let item = CrateItem::try_from(instance).unwrap(); - let mut checker = StubConstChecker::new(tcx, internal_instance, item); - checker.visit_body(&item.body()); - checker.is_valid() - } else { - true - } + let mut checker = StubConstChecker::new(tcx, internal_instance, item); + checker.visit_body(&item.body()); + checker.is_valid() } struct StubConstChecker<'tcx> { diff --git a/kani-compiler/src/kani_middle/stubbing/transform.rs b/kani-compiler/src/kani_middle/stubbing/transform.rs deleted file mode 100644 index f101a6009907..000000000000 --- a/kani-compiler/src/kani_middle/stubbing/transform.rs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT -//! This module contains code related to the MIR-to-MIR pass that performs the -//! stubbing of functions and methods. The primary function of the module is -//! `transform`, which takes the `DefId` of a function/method and returns the -//! body of its stub, if appropriate. The stub mapping it uses is set via rustc -//! arguments. - -use std::collections::{BTreeMap, HashMap}; - -use lazy_static::lazy_static; -use regex::Regex; -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_hir::{def_id::DefId, definitions::DefPathHash}; -use rustc_index::IndexVec; -use rustc_middle::mir::{ - visit::MutVisitor, Body, Const, ConstValue, Local, LocalDecl, Location, Operand, -}; -use rustc_middle::ty::{self, TyCtxt}; - -use tracing::debug; - -/// Returns the `DefId` of the stub for the function/method identified by the -/// parameter `def_id`, and `None` if the function/method is not stubbed. -pub fn get_stub(tcx: TyCtxt, def_id: DefId) -> Option { - let stub_map = get_stub_mapping(tcx)?; - stub_map.get(&def_id).copied() -} - -pub fn get_stub_key(tcx: TyCtxt, def_id: DefId) -> Option { - let stub_map = get_stub_mapping(tcx)?; - stub_map.iter().find_map(|(&key, &val)| if val == def_id { Some(key) } else { None }) -} - -/// Returns the new body of a function/method if it has been stubbed out; -/// otherwise, returns the old body. -pub fn transform<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, old_body: &'tcx Body<'tcx>) -> Body<'tcx> { - if let Some(replacement) = get_stub(tcx, def_id) { - debug!( - original = tcx.def_path_debug_str(def_id), - replaced = tcx.def_path_debug_str(replacement), - "transform" - ); - let new_body = tcx.optimized_mir(replacement).clone(); - if check_compatibility(tcx, def_id, old_body, replacement, &new_body) { - return new_body; - } - } - old_body.clone() -} - -/// Traverse `body` searching for calls to foreing functions and, whevever there is -/// a stub available, replace the call to the foreign function with a call -/// to its correspondent stub. This happens as a separate step because there is no -/// body available to foreign functions at this stage. -pub fn transform_foreign_functions<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - if let Some(stub_map) = get_stub_mapping(tcx) { - let mut visitor = - ForeignFunctionTransformer { tcx, local_decls: body.clone().local_decls, stub_map }; - visitor.visit_body(body); - } -} - -/// Traverse `body` searching for calls to `kani::any_modifies` and replace these calls -/// with calls to `kani::any`. This happens as a separate step as it is only necessary -/// for contract-generated functions. -pub fn transform_any_modifies<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let mut visitor = AnyModifiesTransformer { tcx, local_decls: body.clone().local_decls }; - visitor.visit_body(body); -} - -struct AnyModifiesTransformer<'tcx> { - /// The compiler context. - tcx: TyCtxt<'tcx>, - /// Local declarations of the callee function. Kani searches here for foreign functions. - local_decls: IndexVec>, -} - -impl<'tcx> MutVisitor<'tcx> for AnyModifiesTransformer<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn visit_operand(&mut self, operand: &mut Operand<'tcx>, _location: Location) { - let func_ty = operand.ty(&self.local_decls, self.tcx); - if let ty::FnDef(reachable_function, arguments) = *func_ty.kind() { - if let Some(any_modifies) = self.tcx.get_diagnostic_name(reachable_function) - && any_modifies.as_str() == "KaniAnyModifies" - { - let Operand::Constant(function_definition) = operand else { - return; - }; - let kani_any_symbol = self - .tcx - .get_diagnostic_item(rustc_span::symbol::Symbol::intern("KaniAny")) - .expect("We should have a `kani::any()` definition at this point."); - function_definition.const_ = Const::from_value( - ConstValue::ZeroSized, - self.tcx.type_of(kani_any_symbol).instantiate(self.tcx, arguments), - ); - } - } - } -} - -struct ForeignFunctionTransformer<'tcx> { - /// The compiler context. - tcx: TyCtxt<'tcx>, - /// Local declarations of the callee function. Kani searches here for foreign functions. - local_decls: IndexVec>, - /// Map of functions/methods to their correspondent stubs. - stub_map: HashMap, -} - -impl<'tcx> MutVisitor<'tcx> for ForeignFunctionTransformer<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn visit_operand(&mut self, operand: &mut Operand<'tcx>, _location: Location) { - let func_ty = operand.ty(&self.local_decls, self.tcx); - if let ty::FnDef(reachable_function, arguments) = *func_ty.kind() { - if self.tcx.is_foreign_item(reachable_function) { - if let Some(stub) = self.stub_map.get(&reachable_function) { - let Operand::Constant(function_definition) = operand else { - return; - }; - function_definition.const_ = Const::from_value( - ConstValue::ZeroSized, - self.tcx.type_of(stub).instantiate(self.tcx, arguments), - ); - } - } - } - } -} - -/// Checks whether the stub is compatible with the original function/method: do -/// the arities and types (of the parameters and return values) match up? This -/// does **NOT** check whether the type variables are constrained to implement -/// the same traits; trait mismatches are checked during monomorphization. -fn check_compatibility<'a, 'tcx>( - tcx: TyCtxt, - old_def_id: DefId, - old_body: &'a Body<'tcx>, - stub_def_id: DefId, - stub_body: &'a Body<'tcx>, -) -> bool { - // Check whether the arities match. - if old_body.arg_count != stub_body.arg_count { - tcx.dcx().span_err( - tcx.def_span(stub_def_id), - format!( - "arity mismatch: original function/method `{}` takes {} argument(s), stub `{}` takes {}", - tcx.def_path_str(old_def_id), - old_body.arg_count, - tcx.def_path_str(stub_def_id), - stub_body.arg_count - ), - ); - return false; - } - // Check whether the numbers of generic parameters match. - let old_num_generics = tcx.generics_of(old_def_id).count(); - let stub_num_generics = tcx.generics_of(stub_def_id).count(); - if old_num_generics != stub_num_generics { - tcx.dcx().span_err( - tcx.def_span(stub_def_id), - format!( - "mismatch in the number of generic parameters: original function/method `{}` takes {} generic parameters(s), stub `{}` takes {}", - tcx.def_path_str(old_def_id), - old_num_generics, - tcx.def_path_str(stub_def_id), - stub_num_generics - ), - ); - return false; - } - // Check whether the types match. Index 0 refers to the returned value, - // indices [1, `arg_count`] refer to the parameters. - // TODO: We currently force generic parameters in the stub to have exactly - // the same names as their counterparts in the original function/method; - // instead, we should be checking for the equivalence of types up to the - // renaming of generic parameters. - // - let mut matches = true; - for i in 0..=old_body.arg_count { - let old_arg = old_body.local_decls.get(i.into()).unwrap(); - let new_arg = stub_body.local_decls.get(i.into()).unwrap(); - if old_arg.ty != new_arg.ty { - let prefix = if i == 0 { - "return type differs".to_string() - } else { - format!("type of parameter {} differs", i - 1) - }; - tcx.dcx().span_err( - new_arg.source_info.span, - format!( - "{prefix}: stub `{}` has type `{}` where original function/method `{}` has type `{}`", - tcx.def_path_str(stub_def_id), - new_arg.ty, - tcx.def_path_str(old_def_id), - old_arg.ty - ), - ); - matches = false; - } - } - matches -} - -/// The prefix we will use when serializing the stub mapping as a rustc argument. -const RUSTC_ARG_PREFIX: &str = "kani_stubs="; - -/// Serializes the stub mapping into a rustc argument. -pub fn mk_rustc_arg(stub_mapping: &BTreeMap) -> String { - // Serialize each `DefPathHash` as a pair of `u64`s, and the whole mapping - // as an association list. - let mut pairs = Vec::new(); - for (k, v) in stub_mapping { - let (k_a, k_b) = k.0.split(); - let kparts = (k_a.as_u64(), k_b.as_u64()); - let (v_a, v_b) = v.0.split(); - let vparts = (v_a.as_u64(), v_b.as_u64()); - pairs.push((kparts, vparts)); - } - // Store our serialized mapping as a fake LLVM argument (safe to do since - // LLVM will never see them). - format!("-Cllvm-args='{RUSTC_ARG_PREFIX}{}'", serde_json::to_string(&pairs).unwrap()) -} - -/// Deserializes the stub mapping from the rustc argument value. -fn deserialize_mapping(tcx: TyCtxt, val: &str) -> HashMap { - type Item = (u64, u64); - let item_to_def_id = |item: Item| -> DefId { - let hash = DefPathHash(Fingerprint::new(item.0, item.1)); - tcx.def_path_hash_to_def_id(hash, &()) - }; - let pairs: Vec<(Item, Item)> = serde_json::from_str(val).unwrap(); - let mut m = HashMap::default(); - for (k, v) in pairs { - let kid = item_to_def_id(k); - let vid = item_to_def_id(v); - m.insert(kid, vid); - } - m -} - -/// Retrieves the stub mapping from the compiler configuration. -fn get_stub_mapping(tcx: TyCtxt) -> Option> { - // Use a static so that we compile the regex only once. - lazy_static! { - static ref RE: Regex = Regex::new(&format!("'{RUSTC_ARG_PREFIX}(.*)'")).unwrap(); - } - for arg in &tcx.sess.opts.cg.llvm_args { - if let Some(captures) = RE.captures(arg) { - return Some(deserialize_mapping(tcx, captures.get(1).unwrap().as_str())); - } - } - None -} diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index 9402835c2ff2..bd6c9be6581a 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -6,11 +6,7 @@ use crate::kani_middle::find_fn_def; use rustc_middle::ty::TyCtxt; use stable_mir::mir::mono::Instance; -use stable_mir::mir::{ - BasicBlock, BasicBlockIdx, BinOp, Body, CastKind, Constant, Local, LocalDecl, Mutability, - Operand, Place, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, UnwindAction, - VarDebugInfo, -}; +use stable_mir::mir::*; use stable_mir::ty::{Const, GenericArgs, Span, Ty, UintTy}; use std::fmt::Debug; use std::mem; @@ -288,3 +284,138 @@ impl SourceInstruction { fn find_instance(tcx: TyCtxt, diagnostic: &str) -> Option { Instance::resolve(find_fn_def(tcx, diagnostic)?, &GenericArgs(vec![])).ok() } + +/// Basic mutable body visitor. +/// +/// We removed many methods for simplicity. +/// +/// TODO: Contribute this to stable_mir. +/// +/// +/// This code was based on the existing MirVisitor: +/// +pub trait MutMirVisitor { + fn visit_body(&mut self, body: &mut MutableBody) { + self.super_body(body) + } + + fn visit_basic_block(&mut self, bb: &mut BasicBlock) { + self.super_basic_block(bb) + } + + fn visit_statement(&mut self, stmt: &mut Statement) { + self.super_statement(stmt) + } + + fn visit_terminator(&mut self, term: &mut Terminator) { + self.super_terminator(term) + } + + fn visit_rvalue(&mut self, rvalue: &mut Rvalue) { + self.super_rvalue(rvalue) + } + + fn visit_operand(&mut self, _operand: &mut Operand) {} + + fn super_body(&mut self, body: &mut MutableBody) { + for bb in body.blocks.iter_mut() { + self.visit_basic_block(bb); + } + } + + fn super_basic_block(&mut self, bb: &mut BasicBlock) { + for stmt in &mut bb.statements { + self.visit_statement(stmt); + } + self.visit_terminator(&mut bb.terminator); + } + + fn super_statement(&mut self, stmt: &mut Statement) { + match &mut stmt.kind { + StatementKind::Assign(_, rvalue) => { + self.visit_rvalue(rvalue); + } + StatementKind::Intrinsic(intrisic) => match intrisic { + NonDivergingIntrinsic::Assume(operand) => { + self.visit_operand(operand); + } + NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { + src, + dst, + count, + }) => { + self.visit_operand(src); + self.visit_operand(dst); + self.visit_operand(count); + } + }, + StatementKind::FakeRead(_, _) + | StatementKind::SetDiscriminant { .. } + | StatementKind::Deinit(_) + | StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Retag(_, _) + | StatementKind::PlaceMention(_) + | StatementKind::AscribeUserType { .. } + | StatementKind::Coverage(_) + | StatementKind::ConstEvalCounter + | StatementKind::Nop => {} + } + } + + fn super_terminator(&mut self, term: &mut Terminator) { + let Terminator { kind, .. } = term; + match kind { + TerminatorKind::Assert { cond, .. } => { + self.visit_operand(cond); + } + TerminatorKind::Call { func, args, .. } => { + self.visit_operand(func); + for arg in args { + self.visit_operand(arg); + } + } + TerminatorKind::SwitchInt { discr, .. } => { + self.visit_operand(discr); + } + TerminatorKind::InlineAsm { .. } => { + // we don't support inline assembly. + } + TerminatorKind::Return + | TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Drop { .. } + | TerminatorKind::Unreachable => {} + } + } + + fn super_rvalue(&mut self, rvalue: &mut Rvalue) { + match rvalue { + Rvalue::Aggregate(_, operands) => { + for op in operands { + self.visit_operand(op); + } + } + Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { + self.visit_operand(lhs); + self.visit_operand(rhs); + } + Rvalue::Cast(_, op, _) => { + self.visit_operand(op); + } + Rvalue::Repeat(op, _) => { + self.visit_operand(op); + } + Rvalue::ShallowInitBox(op, _) => self.visit_operand(op), + Rvalue::UnaryOp(_, op) | Rvalue::Use(op) => { + self.visit_operand(op); + } + Rvalue::AddressOf(..) => {} + Rvalue::CopyForDeref(_) | Rvalue::Discriminant(_) | Rvalue::Len(_) => {} + Rvalue::Ref(..) => {} + Rvalue::ThreadLocalRef(_) => {} + Rvalue::NullaryOp(..) => {} + } + } +} diff --git a/kani-compiler/src/kani_middle/transform/check_values.rs b/kani-compiler/src/kani_middle/transform/check_values.rs index 2c05c07be7f2..893b2244f2ca 100644 --- a/kani-compiler/src/kani_middle/transform/check_values.rs +++ b/kani-compiler/src/kani_middle/transform/check_values.rs @@ -730,7 +730,8 @@ fn expect_instance(locals: &[LocalDecl], func: &Operand) -> Instance { let ty = func.ty(locals).unwrap(); match ty.kind() { TyKind::RigidTy(RigidTy::FnDef(def, args)) => Instance::resolve(def, &args).unwrap(), - _ => unreachable!(), + TyKind::RigidTy(RigidTy::FnPtr(sig)) => todo!("Add support to FnPtr: {sig:?}"), + _ => unreachable!("Found: {func:?}"), } } diff --git a/kani-compiler/src/kani_middle/transform/contracts.rs b/kani-compiler/src/kani_middle/transform/contracts.rs new file mode 100644 index 000000000000..c99874cc42a8 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/contracts.rs @@ -0,0 +1,168 @@ +// 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::kani_middle::attributes::KaniAttributes; +use crate::kani_middle::codegen_units::CodegenUnit; +use crate::kani_middle::transform::body::MutableBody; +use crate::kani_middle::transform::{TransformPass, TransformationType}; +use crate::kani_queries::QueryDb; +use cbmc::{InternString, InternedString}; +use rustc_middle::ty::TyCtxt; +use rustc_smir::rustc_internal; +use stable_mir::mir::mono::Instance; +use stable_mir::mir::{Body, Constant, Operand, TerminatorKind}; +use stable_mir::ty::{Const as MirConst, FnDef, RigidTy, TyKind}; +use stable_mir::{CrateDef, DefId}; +use std::collections::HashSet; +use std::fmt::Debug; +use tracing::{debug, trace}; + +/// Check if we can replace calls to any_modifies. +/// +/// This pass will replace the entire body, and it should only be applied to stubs +/// that have a body. +#[derive(Debug)] +pub struct AnyModifiesPass { + kani_any: Option, + kani_any_modifies: Option, + stubbed: HashSet, + target_fn: Option, +} + +impl TransformPass for AnyModifiesPass { + fn transformation_type() -> TransformationType + where + Self: Sized, + { + TransformationType::Stubbing + } + + fn is_enabled(&self, query_db: &QueryDb) -> bool + where + Self: Sized, + { + // TODO: Check if this is the harness has proof_for_contract + query_db.args().unstable_features.contains(&"function-contracts".to_string()) + && self.kani_any.is_some() + } + + /// Transform the function body by replacing it with the stub body. + fn transform(&self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + trace!(function=?instance.name(), "AnyModifiesPass::transform"); + + if instance.def.def_id() == self.kani_any.unwrap().def_id() { + // Ensure kani::any is valid. + self.any_body(tcx, body) + } else if self.should_apply(tcx, instance) { + // Replace any modifies occurrences. + self.replace_any_modifies(body) + } else { + (false, body) + } + } +} + +impl AnyModifiesPass { + /// Build the pass with non-extern function stubs. + pub fn new(tcx: TyCtxt, unit: &CodegenUnit) -> AnyModifiesPass { + let item_fn_def = |item| { + let TyKind::RigidTy(RigidTy::FnDef(def, _)) = + rustc_internal::stable(tcx.type_of(item)).value.kind() + else { + unreachable!("Expected function, but found `{:?}`", tcx.def_path_str(item)) + }; + def + }; + let kani_any = + tcx.get_diagnostic_item(rustc_span::symbol::Symbol::intern("KaniAny")).map(item_fn_def); + let kani_any_modifies = tcx + .get_diagnostic_item(rustc_span::symbol::Symbol::intern("KaniAnyModifies")) + .map(item_fn_def); + let (target_fn, stubbed) = if let Some(harness) = unit.harnesses.first() { + let attributes = KaniAttributes::for_instance(tcx, *harness); + let target_fn = + attributes.proof_for_contract().map(|symbol| symbol.unwrap().as_str().intern()); + (target_fn, unit.stubs.keys().map(|from| from.def_id()).collect::>()) + } else { + (None, HashSet::new()) + }; + AnyModifiesPass { kani_any, kani_any_modifies, target_fn, stubbed } + } + + /// If we apply `transform_any_modifies` in all contract-generated items, + /// we will end up instantiating `kani::any_modifies` for the replace function + /// every time, even if we are only checking the contract, because the function + /// is always included during contract instrumentation. Thus, we must only apply + /// the transformation if we are using a verified stub or in the presence of recursion. + fn should_apply(&self, tcx: TyCtxt, instance: Instance) -> bool { + let item_attributes = + KaniAttributes::for_item(tcx, rustc_internal::internal(tcx, instance.def.def_id())); + self.stubbed.contains(&instance.def.def_id()) || item_attributes.has_recursion() + } + + /// Replace calls to `any_modifies` by calls to `any`. + fn replace_any_modifies(&self, mut body: Body) -> (bool, Body) { + let mut changed = false; + let locals = body.locals().to_vec(); + for bb in body.blocks.iter_mut() { + let TerminatorKind::Call { func, .. } = &mut bb.terminator.kind else { continue }; + if let TyKind::RigidTy(RigidTy::FnDef(def, instance_args)) = + func.ty(&locals).unwrap().kind() + && Some(def) == self.kani_any_modifies + { + let instance = Instance::resolve(self.kani_any.unwrap(), &instance_args).unwrap(); + let literal = MirConst::try_new_zero_sized(instance.ty()).unwrap(); + let span = bb.terminator.span; + let new_func = Constant { span, user_ty: None, literal }; + *func = Operand::Constant(new_func); + changed = true; + } + } + (changed, body) + } + + /// Check if T::Arbitrary requirement for `kani::any()` is met after replacement. + /// + /// If it T does not implement arbitrary, generate error and delete body to interrupt analysis. + fn any_body(&self, tcx: TyCtxt, mut body: Body) -> (bool, Body) { + let mut valid = true; + let locals = body.locals().to_vec(); + for bb in body.blocks.iter_mut() { + let TerminatorKind::Call { func, .. } = &mut bb.terminator.kind else { continue }; + if let TyKind::RigidTy(RigidTy::FnDef(def, args)) = func.ty(&locals).unwrap().kind() { + match Instance::resolve(def, &args) { + Ok(_) => {} + Err(e) => { + valid = false; + debug!(?e, "AnyModifiesPass::any_body failed"); + let receiver_ty = args.0[0].expect_ty(); + let msg = if self.target_fn.is_some() { + format!( + "`{receiver_ty}` doesn't implement `kani::Arbitrary`.\ + Please, check `{}` contract.", + self.target_fn.unwrap(), + ) + } else { + format!("`{receiver_ty}` doesn't implement `kani::Arbitrary`.") + }; + tcx.dcx() + .struct_span_err(rustc_internal::internal(tcx, bb.terminator.span), msg) + .with_help( + "All objects in the modifies clause must implement the Arbitrary. \ + The return type must also implement the Arbitrary trait if you \ + are checking recursion or using verified stub.", + ) + .emit(); + } + } + } + } + if valid { + (true, body) + } else { + let mut new_body = MutableBody::from(body); + new_body.clear_body(); + (false, new_body.into()) + } + } +} diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index d4e06a785fb3..8d5c61f55c92 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -16,9 +16,12 @@ //! //! For all instrumentation passes, always use exhaustive matches to ensure soundness in case a new //! case is added. +use crate::kani_middle::codegen_units::CodegenUnit; use crate::kani_middle::transform::body::CheckType; use crate::kani_middle::transform::check_values::ValidValuePass; +use crate::kani_middle::transform::contracts::AnyModifiesPass; use crate::kani_middle::transform::kani_intrinsics::IntrinsicGeneratorPass; +use crate::kani_middle::transform::stubs::{ExternFnStubPass, FnStubPass}; use crate::kani_queries::QueryDb; use rustc_middle::ty::TyCtxt; use stable_mir::mir::mono::Instance; @@ -26,9 +29,11 @@ use stable_mir::mir::Body; use std::collections::HashMap; use std::fmt::Debug; -mod body; +pub(crate) mod body; mod check_values; +mod contracts; mod kani_intrinsics; +mod stubs; /// Object used to retrieve a transformed instance body. /// The transformations to be applied may be controlled by user options. @@ -37,9 +42,9 @@ mod kani_intrinsics; /// after. #[derive(Debug)] pub struct BodyTransformation { - /// The passes that may optimize the function body. - /// We store them separately from the instrumentation passes because we run the in specific order. - opt_passes: Vec>, + /// The passes that may change the function body according to harness configuration. + /// The stubbing passes should be applied before so user stubs take precedence. + stub_passes: Vec>, /// The passes that may add safety checks to the function body. inst_passes: Vec>, /// Cache transformation results. @@ -47,25 +52,22 @@ pub struct BodyTransformation { } impl BodyTransformation { - pub fn new(queries: &QueryDb, tcx: TyCtxt) -> Self { + pub fn new(queries: &QueryDb, tcx: TyCtxt, unit: &CodegenUnit) -> Self { let mut transformer = BodyTransformation { - opt_passes: vec![], + stub_passes: vec![], inst_passes: vec![], cache: Default::default(), }; let check_type = CheckType::new(tcx); + transformer.add_pass(queries, FnStubPass::new(&unit.stubs)); + transformer.add_pass(queries, ExternFnStubPass::new(&unit.stubs)); + // This has to come after stubs since we want this to replace the stubbed body. + transformer.add_pass(queries, AnyModifiesPass::new(tcx, &unit)); transformer.add_pass(queries, ValidValuePass { check_type: check_type.clone() }); transformer.add_pass(queries, IntrinsicGeneratorPass { check_type }); transformer } - /// Allow the creation of a dummy transformer that doesn't apply any transformation due to - /// the stubbing validation hack (see `collect_and_partition_mono_items` override. - /// Once we move the stubbing logic to a [TransformPass], we should be able to remove this. - pub fn dummy() -> Self { - BodyTransformation { opt_passes: vec![], inst_passes: vec![], cache: Default::default() } - } - /// Retrieve the body of an instance. /// /// Note that this assumes that the instance does have a body since existing consumers already @@ -77,7 +79,7 @@ impl BodyTransformation { None => { let mut body = instance.body().unwrap(); let mut modified = false; - for pass in self.opt_passes.iter().chain(self.inst_passes.iter()) { + for pass in self.stub_passes.iter().chain(self.inst_passes.iter()) { let result = pass.transform(tcx, body, instance); modified |= result.0; body = result.1; @@ -98,9 +100,7 @@ impl BodyTransformation { if pass.is_enabled(&query_db) { match P::transformation_type() { TransformationType::Instrumentation => self.inst_passes.push(Box::new(pass)), - TransformationType::Optimization => { - unreachable!() - } + TransformationType::Stubbing => self.stub_passes.push(Box::new(pass)), } } } @@ -108,16 +108,15 @@ impl BodyTransformation { /// The type of transformation that a pass may perform. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -enum TransformationType { +pub(crate) enum TransformationType { /// Should only add assertion checks to ensure the program is correct. Instrumentation, - /// May replace inefficient code with more performant but equivalent code. - #[allow(dead_code)] - Optimization, + /// Apply some sort of stubbing. + Stubbing, } /// A trait to represent transformation passes that can be used to modify the body of a function. -trait TransformPass: Debug { +pub(crate) trait TransformPass: Debug { /// The type of transformation that this pass implements. fn transformation_type() -> TransformationType where diff --git a/kani-compiler/src/kani_middle/transform/stubs.rs b/kani-compiler/src/kani_middle/transform/stubs.rs new file mode 100644 index 000000000000..6d8849a9f1fe --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/stubs.rs @@ -0,0 +1,222 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +//! This module contains code related to the MIR-to-MIR pass that performs the +//! stubbing of functions and methods. +use crate::kani_middle::codegen_units::Stubs; +use crate::kani_middle::stubbing::validate_stub_const; +use crate::kani_middle::transform::body::{MutMirVisitor, MutableBody}; +use crate::kani_middle::transform::{TransformPass, TransformationType}; +use crate::kani_queries::QueryDb; +use rustc_middle::ty::TyCtxt; +use rustc_smir::rustc_internal; +use stable_mir::mir::mono::Instance; +use stable_mir::mir::visit::{Location, MirVisitor}; +use stable_mir::mir::{Body, Constant, LocalDecl, Operand, Terminator, TerminatorKind}; +use stable_mir::ty::{Const as MirConst, FnDef, RigidTy, TyKind}; +use stable_mir::CrateDef; +use std::collections::HashMap; +use std::fmt::Debug; +use tracing::{debug, trace}; + +/// Replace the body of a function that is stubbed by the other. +/// +/// This pass will replace the entire body, and it should only be applied to stubs +/// that have a body. +#[derive(Debug)] +pub struct FnStubPass { + stubs: Stubs, +} + +impl TransformPass for FnStubPass { + fn transformation_type() -> TransformationType + where + Self: Sized, + { + TransformationType::Stubbing + } + + fn is_enabled(&self, query_db: &QueryDb) -> bool + where + Self: Sized, + { + query_db.args().stubbing_enabled && !self.stubs.is_empty() + } + + /// Transform the function body by replacing it with the stub body. + fn transform(&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); + } + } + } + (false, body) + } +} + +impl FnStubPass { + /// Build the pass with non-extern function stubs. + pub fn new(all_stubs: &Stubs) -> FnStubPass { + let stubs = all_stubs + .iter() + .filter_map(|(from, to)| (has_body(*from) && has_body(*to)).then_some((*from, *to))) + .collect::>(); + FnStubPass { stubs } + } +} + +/// Replace the body of a function that is stubbed by the other. +/// +/// This pass will replace the function call, since one of the functions do not have a body to +/// replace. +#[derive(Debug)] +pub struct ExternFnStubPass { + pub stubs: Stubs, +} + +impl TransformPass for ExternFnStubPass { + fn transformation_type() -> TransformationType + where + Self: Sized, + { + TransformationType::Stubbing + } + + fn is_enabled(&self, query_db: &QueryDb) -> bool + where + Self: Sized, + { + query_db.args().stubbing_enabled && !self.stubs.is_empty() + } + + /// Search for calls to extern functions that should be stubbed. + /// + /// We need to find function calls and function pointers. + /// We should replace this with a visitor once StableMIR includes a mutable one. + fn transform(&self, _tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + trace!(function=?instance.name(), "transform"); + let mut new_body = MutableBody::from(body); + let changed = false; + let locals = new_body.locals().to_vec(); + let mut visitor = ExternFnStubVisitor { changed, locals, stubs: &self.stubs }; + visitor.visit_body(&mut new_body); + (visitor.changed, new_body.into()) + } +} + +impl ExternFnStubPass { + /// Build the pass with the extern function stubs. + /// + /// This will cover any case where the stub doesn't have a body. + pub fn new(all_stubs: &Stubs) -> ExternFnStubPass { + let stubs = all_stubs + .iter() + .filter_map(|(from, to)| (!has_body(*from) || !has_body(*to)).then_some((*from, *to))) + .collect::>(); + ExternFnStubPass { stubs } + } +} + +fn has_body(def: FnDef) -> bool { + def.body().is_some() +} + +/// Validate that the body of the stub is valid for the given instantiation +struct FnStubValidator<'a, 'tcx> { + stub: (FnDef, FnDef), + tcx: TyCtxt<'tcx>, + locals: &'a [LocalDecl], + is_valid: bool, +} + +impl<'a, 'tcx> FnStubValidator<'a, 'tcx> { + fn validate(tcx: TyCtxt, stub: (FnDef, FnDef), new_instance: Instance) -> Option { + if validate_stub_const(tcx, new_instance) { + let body = new_instance.body().unwrap(); + let mut validator = + FnStubValidator { stub, tcx, locals: body.locals(), is_valid: true }; + validator.visit_body(&body); + validator.is_valid.then_some(body) + } else { + None + } + } +} + +impl<'a, 'tcx> MirVisitor for FnStubValidator<'a, 'tcx> { + 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 \ + `{}`. 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, + ), + ); + } + } + } +} + +struct ExternFnStubVisitor<'a> { + changed: bool, + locals: Vec, + stubs: &'a Stubs, +} + +impl<'a> MutMirVisitor for ExternFnStubVisitor<'a> { + 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)) = + 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 = Constant { span, user_ty: None, 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(Constant { 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 = Constant { span: *span, user_ty: None, literal }; + *operand = Operand::Constant(new_func); + self.changed = true; + } + } + } +} diff --git a/kani-compiler/src/kani_queries/mod.rs b/kani-compiler/src/kani_queries/mod.rs index d6b37a35f9cb..bb28237248d3 100644 --- a/kani-compiler/src/kani_queries/mod.rs +++ b/kani-compiler/src/kani_queries/mod.rs @@ -2,23 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Define the communication between KaniCompiler and the codegen implementation. -use cbmc::{InternString, InternedString}; -use kani_metadata::AssignsContract; -use std::{ - collections::HashMap, - path::PathBuf, - sync::{Arc, Mutex}, -}; +use std::sync::{Arc, Mutex}; use crate::args::Arguments; /// This structure should only be used behind a synchronized reference or a snapshot. +/// +/// TODO: Merge this with arguments #[derive(Debug, Default, Clone)] pub struct QueryDb { args: Option, - /// Information about all target harnesses. - pub harnesses_info: HashMap, - modifies_contracts: HashMap, } impl QueryDb { @@ -26,16 +19,6 @@ impl QueryDb { Arc::new(Mutex::new(QueryDb::default())) } - /// Get the definition hash for all harnesses that are being compiled in this compilation stage. - pub fn target_harnesses(&self) -> Vec { - self.harnesses_info.keys().cloned().collect() - } - - /// Get the model path for a given harness. - pub fn harness_model_path(&self, harness: &String) -> Option<&PathBuf> { - self.harnesses_info.get(&harness.intern()) - } - pub fn set_args(&mut self, args: Arguments) { self.args = Some(args); } @@ -43,24 +26,4 @@ impl QueryDb { pub fn args(&self) -> &Arguments { self.args.as_ref().expect("Arguments have not been initialized") } - - /// Register that a CBMC-level `assigns` contract for a function that is - /// called from this harness. - pub fn register_assigns_contract( - &mut self, - harness_name: InternedString, - contract: AssignsContract, - ) { - let replaced = self.modifies_contracts.insert(harness_name, contract); - assert!( - replaced.is_none(), - "Invariant broken, tried adding second modifies contracts to: {harness_name}", - ) - } - - /// Lookup all CBMC-level `assigns` contract were registered with - /// [`Self::add_assigns_contract`]. - pub fn assigns_contracts(&self) -> impl Iterator { - self.modifies_contracts.iter() - } } diff --git a/tests/cargo-kani/stubbing-double-extern-path/harness/expected b/tests/cargo-kani/stubbing-double-extern-path/harness/expected index adbbf31d49bd..dbca159f92d5 100644 --- a/tests/cargo-kani/stubbing-double-extern-path/harness/expected +++ b/tests/cargo-kani/stubbing-double-extern-path/harness/expected @@ -1 +1,2 @@ -error[E0391]: cycle detected when optimizing MIR for `crate_b::assert_false` +error: Cannot stub `crate_b::assert_false`. Stub configuration for harness `check_inverted` has a cycle +error: Cannot stub `crate_b::assert_true`. Stub configuration for harness `check_inverted` has a cycle diff --git a/tests/expected/function-contract/modifies/check_invalid_modifies.expected b/tests/expected/function-contract/modifies/check_invalid_modifies.expected index fa1d96f1fe3f..660430705aa2 100644 --- a/tests/expected/function-contract/modifies/check_invalid_modifies.expected +++ b/tests/expected/function-contract/modifies/check_invalid_modifies.expected @@ -1,3 +1,7 @@ -error: `&str` doesn't implement `kani::Arbitrary`. Callee: `kani::Arbitrary::any` \ - Please, check whether the type of all objects in the modifies clause (including return types) implement `kani::Arbitrary`. \ - This is a strict condition to use function contracts as verified stubs. +error: `&str` doesn't implement `kani::Arbitrary`\ + -->\ +| +| T::any() +| ^^^^^^^^ +| += help: All objects in the modifies clause must implement the Arbitrary. The return type must also implement the Arbitrary trait if you are checking recursion or using verified stub. diff --git a/tests/expected/issue-2589/issue_2589.expected b/tests/expected/issue-2589/issue_2589.expected index 0352a8933b6d..a753bc7390a0 100644 --- a/tests/expected/issue-2589/issue_2589.expected +++ b/tests/expected/issue-2589/issue_2589.expected @@ -1 +1 @@ -error: Type `std::string::String` does not implement trait `Dummy`. This is likely because `original` is used as a stub but its generic bounds are not being met. +error: Type `std::string::String` does not implement trait `Dummy`. This is likely because `stub` is used as a stub but its generic bounds are not being met. diff --git a/tests/expected/stubbing-different-sets/stubbing.expected b/tests/expected/stubbing-different-sets/stubbing.expected new file mode 100644 index 000000000000..cf362d974f2e --- /dev/null +++ b/tests/expected/stubbing-different-sets/stubbing.expected @@ -0,0 +1,19 @@ +Checking harness check_indirect_all_identity... +VERIFICATION:- SUCCESSFUL + +Checking harness check_all_identity_2... +VERIFICATION:- SUCCESSFUL + +Checking harness check_all_identity... +VERIFICATION:- SUCCESSFUL + +Checking harness check_decrement_is_increment... +VERIFICATION:- SUCCESSFUL + +Checking harness check_decrement... +VERIFICATION:- SUCCESSFUL + +Checking harness check_identity... +VERIFICATION:- SUCCESSFUL + +Complete - 6 successfully verified harnesses, 0 failures, 6 total. diff --git a/tests/script-based-pre/stubbing_compiler_sessions/stubbing.rs b/tests/expected/stubbing-different-sets/stubbing.rs similarity index 88% rename from tests/script-based-pre/stubbing_compiler_sessions/stubbing.rs rename to tests/expected/stubbing-different-sets/stubbing.rs index cde1902c3558..90905baa5bb6 100644 --- a/tests/script-based-pre/stubbing_compiler_sessions/stubbing.rs +++ b/tests/expected/stubbing-different-sets/stubbing.rs @@ -1,8 +1,8 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// -// Check that Kani handles different sets of stubbing correctly. -// I.e., not correctly replacing the stubs will cause a harness to fail. +// kani-flags: -Z stubbing +//! Check that Kani handles different sets of stubbing correctly. +//! I.e., not correctly replacing the stubs will cause a harness to fail. fn identity(i: i8) -> i8 { i diff --git a/tests/script-based-pre/stubbing_compiler_sessions/check_compiler_sessions.expected b/tests/script-based-pre/stubbing_compiler_sessions/check_compiler_sessions.expected deleted file mode 100644 index 9c6b0f53778f..000000000000 --- a/tests/script-based-pre/stubbing_compiler_sessions/check_compiler_sessions.expected +++ /dev/null @@ -1,3 +0,0 @@ -Complete - 6 successfully verified harnesses, 0 failures, 6 total. - -Rust compiler sessions: 4 diff --git a/tests/script-based-pre/stubbing_compiler_sessions/check_compiler_sessions.sh b/tests/script-based-pre/stubbing_compiler_sessions/check_compiler_sessions.sh deleted file mode 100755 index 9e4afff0b09c..000000000000 --- a/tests/script-based-pre/stubbing_compiler_sessions/check_compiler_sessions.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -# Copyright Kani Contributors -# SPDX-License-Identifier: Apache-2.0 OR MIT -# -# Checks that the Kani compiler can encode harnesses with the same set of stubs -# in one rustc session. - -set +e - -log_file=output.log - -kani stubbing.rs --enable-unstable --enable-stubbing --verbose >& ${log_file} - -echo "------- Raw output ---------" -cat $log_file -echo "----------------------------" - -# We print the reachability analysis results once for each session. -# This is the only reliable way to get the number of sessions from the compiler. -# The other option would be to use debug comments. -# Ideally, the compiler should only print one set of statistics at the end of its run. -# In that case, we should include number of sessions to those stats. -runs=$(grep -c "Reachability Analysis Result" ${log_file}) -echo "Rust compiler sessions: ${runs}" - -# Cleanup -rm ${log_file} diff --git a/tests/script-based-pre/stubbing_compiler_sessions/config.yml b/tests/script-based-pre/stubbing_compiler_sessions/config.yml deleted file mode 100644 index b1e83ed011c7..000000000000 --- a/tests/script-based-pre/stubbing_compiler_sessions/config.yml +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright Kani Contributors -# SPDX-License-Identifier: Apache-2.0 OR MIT -script: check_compiler_sessions.sh -expected: check_compiler_sessions.expected diff --git a/tests/script-based-pre/verify_std_cmd/verify_std.expected b/tests/script-based-pre/verify_std_cmd/verify_std.expected index aa965f50cdf2..028e8949815f 100644 --- a/tests/script-based-pre/verify_std_cmd/verify_std.expected +++ b/tests/script-based-pre/verify_std_cmd/verify_std.expected @@ -1,4 +1,7 @@ [TEST] Run kani verify-std +Checking harness kani::check_stub... +VERIFICATION:- SUCCESSFUL + Checking harness kani::check_success... VERIFICATION:- SUCCESSFUL @@ -8,4 +11,4 @@ VERIFICATION:- SUCCESSFUL (encountered one or more panics as expected) Checking harness num::verify::check_non_zero... VERIFICATION:- SUCCESSFUL -Complete - 3 successfully verified harnesses, 0 failures, 3 total. +Complete - 4 successfully verified harnesses, 0 failures, 4 total. diff --git a/tests/script-based-pre/verify_std_cmd/verify_std.sh b/tests/script-based-pre/verify_std_cmd/verify_std.sh index 9e18ed72ca2a..cc960ee2bfaf 100755 --- a/tests/script-based-pre/verify_std_cmd/verify_std.sh +++ b/tests/script-based-pre/verify_std_cmd/verify_std.sh @@ -54,6 +54,21 @@ pub mod kani { let new = mid as u8; assert!(orig == new, "Conversion round trip works"); } + + pub fn assert_true(cond: bool) { + assert!(cond) + } + + pub fn assert_false(cond: bool) { + assert!(!cond) + } + + #[kani_core::proof] + #[kani_core::stub(assert_true, assert_false)] + fn check_stub() { + // Check this is in fact asserting false. + assert_true(false) + } } ' @@ -85,7 +100,7 @@ cat ${TMP_DIR}/std_lib.rs >> ${TMP_DIR}/library/std/src/lib.rs echo "[TEST] Run kani verify-std" export RUST_BACKTRACE=1 -kani verify-std -Z unstable-options "${TMP_DIR}/library" --target-dir "${TMP_DIR}/target" +kani verify-std -Z unstable-options "${TMP_DIR}/library" --target-dir "${TMP_DIR}/target" -Z stubbing # Cleanup rm -r ${TMP_DIR} diff --git a/tests/ui/stubbing/stubbing-type-validation/expected b/tests/ui/stubbing/stubbing-type-validation/expected index 5c56ad015a79..177375b4f4ba 100644 --- a/tests/ui/stubbing/stubbing-type-validation/expected +++ b/tests/ui/stubbing/stubbing-type-validation/expected @@ -1,8 +1,11 @@ -error: arity mismatch: original function/method `f1` takes 1 argument(s), stub `f2` takes 0 -error: return type differs: stub `g2` has type `i32` where original function/method `g1` has type `bool` -error: type of parameter 1 differs: stub `g2` has type `u32` where original function/method `g1` has type `i32` -error: type of parameter 2 differs: stub `g2` has type `&mut bool` where original function/method `g1` has type `&bool` error: mismatch in the number of generic parameters: original function/method `h1` takes 1 generic parameters(s), stub `h2` takes 2 -error: return type differs: stub `i2` has type `Y` where original function/method `i1` has type `X` -error: type of parameter 1 differs: stub `j2` has type `&X` where original function/method `j1` has type `&Y` -error: aborting due to 7 previous errors \ No newline at end of file + +error: Cannot stub `g1` by `g2`.\ + - Expected return type `bool`, but found `i32`\ + - Expected type `i32` for parameter 2, but found `u32`\ + - Expected type `&bool` for parameter 3, but found `&mut bool`\ + +error: Cannot stub `i1` by `i2`.\ + - Expected return type `X`, but found `Y` + +error: arity mismatch: original function/method `f1` takes 1 argument(s), stub `f2` takes 0 From e7d16245f2d442cd55d07cedac348167e82895b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 20:12:21 +0000 Subject: [PATCH 128/225] Bump tests/perf/s2n-quic from `d90729d` to `59ef366` (#3249) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `d90729d` to `59ef366`.
Commits
  • 59ef366 chore: release 1.38.1 (#2231)
  • 8c2a5b9 chore: release 1.38.0 (#2230)
  • 20977ee feat(s2n-quic): allow application to configure the anti_amplification_multipl...
  • 79e5d8e feat(s2n-quic): expose configuration options for congestion control (#2228)
  • 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 d90729de3f6d..59ef366af76e 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit d90729de3f6d1fdc76ddff734591cfc2d8e61e80 +Subproject commit 59ef366af76edfb4f89bd39137865db2a1ad041d From d4a3f7b4bb1b485aae6618e016bbfd021270ca18 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Mon, 10 Jun 2024 23:55:04 +0200 Subject: [PATCH 129/225] Use cfg=kani_host for host crates (#3244) We want to run the proofs in the target crate and don't need to build (or run) the proofs in any of the host crates. This avoids a need to make available the `kani` crate to any such host crates. Resolves #3101, #3238 --- docs/src/usage.md | 5 +-- kani-driver/src/call_cargo.rs | 6 ++-- .../README.md | 2 ++ .../binary/Cargo.toml | 14 ++++++++ .../binary/build.rs | 16 ++++++++++ .../binary/src/main.rs | 32 +++++++++++++++++++ .../constants/Cargo.toml | 9 ++++++ .../constants/src/lib.rs | 32 +++++++++++++++++++ .../unsupported-lib-types/proc-macro/expected | 2 +- .../build-rs-conditional/Cargo.toml | 2 +- .../build-rs-conditional/build.rs | 2 +- 11 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 tests/cargo-kani/build-rs-plus-host-with-kani-proofs/README.md create mode 100644 tests/cargo-kani/build-rs-plus-host-with-kani-proofs/binary/Cargo.toml create mode 100644 tests/cargo-kani/build-rs-plus-host-with-kani-proofs/binary/build.rs create mode 100644 tests/cargo-kani/build-rs-plus-host-with-kani-proofs/binary/src/main.rs create mode 100644 tests/cargo-kani/build-rs-plus-host-with-kani-proofs/constants/Cargo.toml create mode 100644 tests/cargo-kani/build-rs-plus-host-with-kani-proofs/constants/src/lib.rs diff --git a/docs/src/usage.md b/docs/src/usage.md index fced7a047804..459916c87222 100644 --- a/docs/src/usage.md +++ b/docs/src/usage.md @@ -80,10 +80,11 @@ For more information please consult this [blog post](https://blog.rust-lang.org/ ## The build process -When Kani builds your code, it does two important things: +When Kani builds your code, it does three important things: -1. It sets `cfg(kani)`. +1. It sets `cfg(kani)` for target crate compilation (including dependencies). 2. It injects the `kani` crate. +3. It sets `cfg(kani_host)` for host build targets such as any build script and procedural macro crates. A proof harness (which you can [learn more about in the tutorial](./kani-tutorial.md)), is a function annotated with `#[kani::proof]` much like a test is annotated with `#[test]`. But you may experience a similar problem using Kani as you would with `dev-dependencies`: if you try writing `#[kani::proof]` directly in your code, `cargo build` will fail because it doesn't know what the `kani` crate is. diff --git a/kani-driver/src/call_cargo.rs b/kani-driver/src/call_cargo.rs index a1d976d73150..ade9785b8fdd 100644 --- a/kani-driver/src/call_cargo.rs +++ b/kani-driver/src/call_cargo.rs @@ -326,10 +326,10 @@ pub fn cargo_config_args() -> Vec { [ "--target", env!("TARGET"), - // Propagate `--cfg=kani` to build scripts. + // Propagate `--cfg=kani_host` to build scripts. "-Zhost-config", "-Ztarget-applies-to-host", - "--config=host.rustflags=[\"--cfg=kani\"]", + "--config=host.rustflags=[\"--cfg=kani_host\"]", ] .map(OsString::from) .to_vec() @@ -561,7 +561,7 @@ fn package_targets(args: &VerificationArgs, package: &Package) -> Vec SomeStruct { + SomeStruct { some_field: if b { 42 } else { 24 } } +} + +fn main() { + println!("The constant is {}", constants::SOME_CONSTANT); + + let some_struct = function_that_does_something(true); + + println!("some_field is {:?}", some_struct.some_field); +} + +#[cfg(kani)] +mod verification { + use super::*; + + #[kani::proof] + fn function_never_returns_zero_struct() { + let input: bool = kani::any(); + let output = function_that_does_something(input); + + assert!(output.some_field != 0); + } +} diff --git a/tests/cargo-kani/build-rs-plus-host-with-kani-proofs/constants/Cargo.toml b/tests/cargo-kani/build-rs-plus-host-with-kani-proofs/constants/Cargo.toml new file mode 100644 index 000000000000..cf2d84dcf946 --- /dev/null +++ b/tests/cargo-kani/build-rs-plus-host-with-kani-proofs/constants/Cargo.toml @@ -0,0 +1,9 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +[package] +name = "constants" +version = "0.1.0" +edition = "2021" + + +[dependencies] diff --git a/tests/cargo-kani/build-rs-plus-host-with-kani-proofs/constants/src/lib.rs b/tests/cargo-kani/build-rs-plus-host-with-kani-proofs/constants/src/lib.rs new file mode 100644 index 000000000000..e485182b24ba --- /dev/null +++ b/tests/cargo-kani/build-rs-plus-host-with-kani-proofs/constants/src/lib.rs @@ -0,0 +1,32 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// From https://github.com/model-checking/kani/issues/3101 + +#[cfg(not(any(kani, kani_host)))] +pub const SOME_CONSTANT: u32 = 0; +#[cfg(kani)] +pub const SOME_CONSTANT: u32 = 1; +#[cfg(kani_host)] +pub const SOME_CONSTANT: u32 = 2; + +pub struct SomeStruct { + pub some_field: u32, +} + +#[cfg(kani)] +impl kani::Arbitrary for SomeStruct { + fn any() -> Self { + SomeStruct { some_field: kani::any() } + } +} + +#[cfg(kani)] +mod verification { + use super::*; + + #[kani::proof] + fn one() { + assert_eq!(constants::SOME_CONSTANT, 1); + } +} diff --git a/tests/cargo-ui/unsupported-lib-types/proc-macro/expected b/tests/cargo-ui/unsupported-lib-types/proc-macro/expected index 2a7badb42720..9703300da1c2 100644 --- a/tests/cargo-ui/unsupported-lib-types/proc-macro/expected +++ b/tests/cargo-ui/unsupported-lib-types/proc-macro/expected @@ -1,2 +1,2 @@ -Skipped the following unsupported targets: 'lib'. +Skipped verification of the following unsupported targets: 'lib'. error: No supported targets were found. diff --git a/tests/script-based-pre/build-rs-conditional/Cargo.toml b/tests/script-based-pre/build-rs-conditional/Cargo.toml index ecf681268dfe..8f9a7794f325 100644 --- a/tests/script-based-pre/build-rs-conditional/Cargo.toml +++ b/tests/script-based-pre/build-rs-conditional/Cargo.toml @@ -8,4 +8,4 @@ edition = "2021" [dependencies] [lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)', 'cfg(kani_host)'] } diff --git a/tests/script-based-pre/build-rs-conditional/build.rs b/tests/script-based-pre/build-rs-conditional/build.rs index 0b083ff95f02..4b13475ec510 100644 --- a/tests/script-based-pre/build-rs-conditional/build.rs +++ b/tests/script-based-pre/build-rs-conditional/build.rs @@ -3,7 +3,7 @@ //! Verify that build scripts can check if they are running under `kani`. fn main() { - if cfg!(kani) { + if cfg!(kani_host) { println!("cargo:rustc-env=RUNNING_KANI=Yes"); } else { println!("cargo:rustc-env=RUNNING_KANI=No"); From eeb5fe74629355ba2612416c8ad05aa31bcb2fb4 Mon Sep 17 00:00:00 2001 From: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> Date: Tue, 11 Jun 2024 00:45:28 -0400 Subject: [PATCH 130/225] Add intrinsics and Arbitrary support for no_core (#3230) ### Approach So far The approach so far has been to shift the `kani` library's functionality to `kani_core` instead, which has no dependencies of its own. By moving these API functions to macros, we delay the compilation of these to when Kani is invoked. Tested by using the regression tests added @celinval in https://github.com/model-checking/kani/pull/3236 using the new `verify-std` subcommand. ### Running Kani To test Kani itself, we injected a proof inside the core library by making these changes. ``` rust #[cfg(kani)] kani_core::kani_lib!(core); #[cfg(kani)] #[unstable(feature = "kani", issue = "none")] pub mod verify { use crate::kani; #[kani::proof] pub fn harness() { kani::assert(true, "yay"); } } ``` 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 --- Cargo.lock | 2 +- .../codegen_cprover_gotoc/codegen/contract.rs | 1 + kani-driver/src/args_toml.rs | 2 +- library/kani/build.rs | 7 + library/kani_core/Cargo.toml | 9 +- library/kani_core/src/arbitrary.rs | 168 +++++++++++ library/kani_core/src/lib.rs | 283 +++++++++++++++++- .../src/sysroot/contracts/shared.rs | 3 + .../verify_std_cmd/verify_std.expected | 10 +- .../verify_std_cmd/verify_std.sh | 55 +--- tools/build-kani/src/sysroot.rs | 2 +- 11 files changed, 490 insertions(+), 52 deletions(-) create mode 100644 library/kani/build.rs create mode 100644 library/kani_core/src/arbitrary.rs diff --git a/Cargo.lock b/Cargo.lock index 31eac9d1663e..bf34011c5f04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -470,7 +470,7 @@ dependencies = [ [[package]] name = "kani_core" -version = "0.51.0" +version = "0.52.0" dependencies = [ "kani_macros", ] diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs index a720d1457606..7edd64438728 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs @@ -47,6 +47,7 @@ impl<'tcx> GotocCtx<'tcx> { } _ => None, }); + let recursion_tracker_def = recursion_tracker .next() .expect("There should be at least one recursion tracker (REENTRY) in scope"); diff --git a/kani-driver/src/args_toml.rs b/kani-driver/src/args_toml.rs index b9e994b38fec..37b38a425597 100644 --- a/kani-driver/src/args_toml.rs +++ b/kani-driver/src/args_toml.rs @@ -315,6 +315,6 @@ mod tests { #[test] fn check_unstable_entry_invalid() { let name = String::from("feature"); - assert!(matches!(unstable_entry(&name, &Value::String("".to_string())), Err(_))); + assert!(unstable_entry(&name, &Value::String("".to_string())).is_err()); } } diff --git a/library/kani/build.rs b/library/kani/build.rs new file mode 100644 index 000000000000..c094cc0254c6 --- /dev/null +++ b/library/kani/build.rs @@ -0,0 +1,7 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +fn main() { + // Make sure `kani_sysroot` is a recognized config + println!("cargo::rustc-check-cfg=cfg(kani_sysroot)"); +} diff --git a/library/kani_core/Cargo.toml b/library/kani_core/Cargo.toml index ad96017a2a28..6f5230c6d19b 100644 --- a/library/kani_core/Cargo.toml +++ b/library/kani_core/Cargo.toml @@ -1,11 +1,16 @@ # Copyright Kani Contributors # SPDX-License-Identifier: Apache-2.0 OR MIT + [package] name = "kani_core" -version = "0.51.0" +version = "0.52.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false +description = "Define core constructs to use with Kani" [dependencies] -kani_macros = { path = "../kani_macros", features = ["no_core"] } \ No newline at end of file +kani_macros = { path = "../kani_macros", features = ["no_core"] } + +[features] +no_core=["kani_macros/no_core"] diff --git a/library/kani_core/src/arbitrary.rs b/library/kani_core/src/arbitrary.rs new file mode 100644 index 000000000000..e7ac4dc81508 --- /dev/null +++ b/library/kani_core/src/arbitrary.rs @@ -0,0 +1,168 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This macro generates implementations of the `Arbitrary` trait for various types. The `Arbitrary` trait defines +//! methods for generating arbitrary (unconstrained) values of the implementing type. +//! trivial_arbitrary and nonzero_arbitrary are implementations of Arbitrary for types that can be represented +//! by an unconstrained symbolic value of their size (e.g., `u8`, `u16`, `u32`, etc.). +//! +//! TODO: Use this inside kani library so that we dont have to maintain two copies of the same proc macro for arbitrary. +#[macro_export] +macro_rules! generate_arbitrary { + ($core:path) => { + use core_path::marker::{PhantomData, PhantomPinned}; + use $core as core_path; + + pub trait Arbitrary + where + Self: Sized, + { + fn any() -> Self; + #[cfg(kani_sysroot)] + fn any_array() -> [Self; MAX_ARRAY_LENGTH] + // the requirement defined in the where clause must appear on the `impl`'s method `any_array` + // but also on the corresponding trait's method + where + [(); core_path::mem::size_of::<[Self; MAX_ARRAY_LENGTH]>()]:, + { + [(); MAX_ARRAY_LENGTH].map(|_| Self::any()) + } + } + + /// The given type can be represented by an unconstrained symbolic value of size_of::. + macro_rules! trivial_arbitrary { + ( $type: ty ) => { + impl Arbitrary for $type { + #[inline(always)] + fn any() -> Self { + // This size_of call does not use generic_const_exprs feature. It's inside a macro, and Self isn't generic. + unsafe { any_raw_internal::() }>() } + } + // Disable this for standard library since we cannot enable generic constant expr. + #[cfg(kani_sysroot)] + fn any_array() -> [Self; MAX_ARRAY_LENGTH] + where + // `generic_const_exprs` requires all potential errors to be reflected in the signature/header. + // We must repeat the expression in the header, to make sure that if the body can fail the header will also fail. + [(); { core_path::mem::size_of::<[$type; MAX_ARRAY_LENGTH]>() }]:, + { + unsafe { + any_raw_internal::< + [Self; MAX_ARRAY_LENGTH], + { core_path::mem::size_of::<[Self; MAX_ARRAY_LENGTH]>() }, + >() + } + } + } + }; + } + + macro_rules! nonzero_arbitrary { + ( $type: ty, $base: ty ) => { + use core_path::num::*; + impl Arbitrary for $type { + #[inline(always)] + fn any() -> Self { + let val = <$base>::any(); + assume(val != 0); + unsafe { <$type>::new_unchecked(val) } + } + } + }; + } + + // Generate trivial arbitrary values + trivial_arbitrary!(u8); + trivial_arbitrary!(u16); + trivial_arbitrary!(u32); + trivial_arbitrary!(u64); + trivial_arbitrary!(u128); + trivial_arbitrary!(usize); + + trivial_arbitrary!(i8); + trivial_arbitrary!(i16); + trivial_arbitrary!(i32); + trivial_arbitrary!(i64); + trivial_arbitrary!(i128); + trivial_arbitrary!(isize); + + nonzero_arbitrary!(NonZeroU8, u8); + nonzero_arbitrary!(NonZeroU16, u16); + nonzero_arbitrary!(NonZeroU32, u32); + nonzero_arbitrary!(NonZeroU64, u64); + nonzero_arbitrary!(NonZeroU128, u128); + nonzero_arbitrary!(NonZeroUsize, usize); + + nonzero_arbitrary!(NonZeroI8, i8); + nonzero_arbitrary!(NonZeroI16, i16); + nonzero_arbitrary!(NonZeroI32, i32); + nonzero_arbitrary!(NonZeroI64, i64); + nonzero_arbitrary!(NonZeroI128, i128); + nonzero_arbitrary!(NonZeroIsize, isize); + + // Implement arbitrary for non-trivial types + impl Arbitrary for bool { + #[inline(always)] + fn any() -> Self { + let byte = u8::any(); + assume(byte < 2); + byte == 1 + } + } + + /// Validate that a char is not outside the ranges [0x0, 0xD7FF] and [0xE000, 0x10FFFF] + /// Ref: + impl Arbitrary for char { + #[inline(always)] + fn any() -> Self { + // Generate an arbitrary u32 and constrain it to make it a valid representation of char. + + let val = u32::any(); + assume(val <= 0xD7FF || (0xE000..=0x10FFFF).contains(&val)); + unsafe { char::from_u32_unchecked(val) } + } + } + + impl Arbitrary for Option + where + T: Arbitrary, + { + fn any() -> Self { + if bool::any() { Some(T::any()) } else { None } + } + } + + impl Arbitrary for Result + where + T: Arbitrary, + E: Arbitrary, + { + fn any() -> Self { + if bool::any() { Ok(T::any()) } else { Err(E::any()) } + } + } + + impl Arbitrary for PhantomData { + fn any() -> Self { + PhantomData + } + } + + impl Arbitrary for PhantomPinned { + fn any() -> Self { + PhantomPinned + } + } + + #[cfg(kani_sysroot)] + impl Arbitrary for [T; N] + where + T: Arbitrary, + [(); core_path::mem::size_of::<[T; N]>()]:, + { + fn any() -> Self { + T::any_array() + } + } + }; +} diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index a5cd40d71e92..c31e964eeb0a 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -1,8 +1,289 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT +//! This crate is a macro_only crate. It is designed to be used in `no_core` and `no_std` +//! environment. +//! +//! It will contain macros that generate core components of Kani. +//! +//! For regular usage, the kani library will invoke these macros to generate its components as if +//! they were declared in that library. +//! +//! For `no_core` and `no_std` crates, they will have to directly invoke those macros inside a +//! `kani` module in order to generate all the required components. +//! I.e., the components will be part of the crate being compiled. +//! +//! Note that any crate level attribute should be added by kani_driver as RUSTC_FLAGS. +//! E.g.: `register_tool(kanitool)` -//! This is placeholder for the new `kani_core` library. #![feature(no_core)] #![no_core] +mod arbitrary; + pub use kani_macros::*; + +/// Users should only need to invoke this. +/// +/// Options are: +/// - `kani`: Add definitions needed for Kani library. +/// - `core`: Define a `kani` module inside `core` crate. +/// - `std`: TODO: Define a `kani` module inside `std` crate. Users must define kani inside core. +#[macro_export] +macro_rules! kani_lib { + (core) => { + #[cfg(kani)] + #[unstable(feature = "kani", issue = "none")] + pub mod kani { + pub use kani_core::{ensures, proof, proof_for_contract, requires, should_panic}; + kani_core::kani_intrinsics!(); + kani_core::generate_arbitrary!(core); + } + }; + + (kani) => { + pub use kani_core::*; + kani_core::kani_intrinsics!(); + kani_core::generate_arbitrary!(std); + }; +} + +/// Kani intrinsics contains the public APIs used by users to verify their harnesses. +/// This macro is a part of kani_core as that allows us to verify even libraries that are no_core +/// such as core in rust's std library itself. +/// +/// TODO: Use this inside kani library so that we dont have to maintain two copies of the same intrinsics. +#[macro_export] +macro_rules! kani_intrinsics { + () => { + /// 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. + /// + /// # Example: + /// + /// The code snippet below should never panic. + /// + /// ```rust + /// let i : i32 = kani::any(); + /// kani::assume(i > 10); + /// if i < 0 { + /// panic!("This will never panic"); + /// } + /// ``` + /// + /// The following code may panic though: + /// + /// ```rust + /// let i : i32 = kani::any(); + /// assert!(i < 0, "This may panic and verification should fail."); + /// kani::assume(i > 10); + /// ``` + #[inline(never)] + #[rustc_diagnostic_item = "KaniAssume"] + #[cfg(not(feature = "concrete_playback"))] + pub fn assume(cond: bool) { + let _ = cond; + } + + #[inline(never)] + #[rustc_diagnostic_item = "KaniAssume"] + #[cfg(feature = "concrete_playback")] + pub fn assume(cond: bool) { + assert!(cond, "`kani::assume` should always hold"); + } + + /// Creates an assertion of the specified condition and message. + /// + /// # Example: + /// + /// ```rust + /// let x: bool = kani::any(); + /// let y = !x; + /// kani::assert(x || y, "ORing a boolean variable with its negation must be true") + /// ``` + #[cfg(not(feature = "concrete_playback"))] + #[inline(never)] + #[rustc_diagnostic_item = "KaniAssert"] + pub const fn assert(cond: bool, msg: &'static str) { + let _ = cond; + let _ = msg; + } + + #[cfg(feature = "concrete_playback")] + #[inline(never)] + #[rustc_diagnostic_item = "KaniAssert"] + pub const fn assert(cond: bool, msg: &'static str) { + assert!(cond, "{}", msg); + } + + /// Creates a cover property with the specified condition and message. + /// + /// # Example: + /// + /// ```rust + /// kani::cover(slice.len() == 0, "The slice may have a length of 0"); + /// ``` + /// + /// A cover property checks if there is at least one execution that satisfies + /// the specified condition at the location in which the function is called. + /// + /// Cover properties are reported as: + /// - SATISFIED: if Kani found an execution that satisfies the condition + /// - UNSATISFIABLE: if Kani proved that the condition cannot be satisfied + /// - UNREACHABLE: if Kani proved that the cover property itself is unreachable (i.e. it is vacuously UNSATISFIABLE) + /// + /// This function is called by the [`cover!`] macro. The macro is more + /// convenient to use. + /// + #[inline(never)] + #[rustc_diagnostic_item = "KaniCover"] + pub const fn cover(_cond: bool, _msg: &'static str) {} + + /// This creates an symbolic *valid* value of type `T`. You can assign the return value of this + /// function to a variable that you want to make symbolic. + /// + /// # Example: + /// + /// In the snippet below, we are verifying the behavior of the function `fn_under_verification` + /// under all possible `NonZeroU8` input values, i.e., all possible `u8` values except zero. + /// + /// ```rust + /// let inputA = kani::any::(); + /// fn_under_verification(inputA); + /// ``` + /// + /// Note: This is a safe construct and can only be used with types that implement the `Arbitrary` + /// trait. The Arbitrary trait is used to build a symbolic value that represents all possible + /// valid values for type `T`. + #[rustc_diagnostic_item = "KaniAny"] + #[inline(always)] + pub fn any() -> T { + T::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 + /// contracts only for verification. + #[rustc_diagnostic_item = "KaniAnyModifies"] + #[inline(never)] + #[doc(hidden)] + pub fn any_modifies() -> T { + // This function should not be reacheable. + // Users must include `#[kani::recursion]` in any function contracts for recursive functions; + // otherwise, this might not be properly instantiate. We mark this as unreachable to make + // sure Kani doesn't report any false positives. + unreachable!() + } + + /// This creates a symbolic *valid* value of type `T`. + /// The value is constrained to be a value accepted by the predicate passed to the filter. + /// You can assign the return value of this function to a variable that you want to make symbolic. + /// + /// # Example: + /// + /// In the snippet below, we are verifying the behavior of the function `fn_under_verification` + /// under all possible `u8` input values between 0 and 12. + /// + /// ```rust + /// let inputA: u8 = kani::any_where(|x| *x < 12); + /// fn_under_verification(inputA); + /// ``` + /// + /// Note: This is a safe construct and can only be used with types that implement the `Arbitrary` + /// trait. The Arbitrary trait is used to build a symbolic value that represents all possible + /// valid values for type `T`. + #[inline(always)] + pub fn any_where bool>(f: F) -> T { + let result = T::any(); + assume(f(&result)); + result + } + + /// This function creates a symbolic value of type `T`. This may result in an invalid value. + /// + /// # Safety + /// + /// This function is unsafe and it may represent invalid `T` values which can lead to many + /// undesirable undefined behaviors. Because of that, this function can only be used + /// internally when we can guarantee that the type T has no restriction regarding its bit level + /// representation. + /// + /// This function is also used to find concrete values in the CBMC output trace + /// and return those concrete values in concrete playback mode. + /// + /// Note that SIZE_T must be equal the size of type T in bytes. + #[inline(never)] + #[cfg(not(feature = "concrete_playback"))] + pub(crate) unsafe fn any_raw_internal() -> T { + any_raw_inner::() + } + + #[inline(never)] + #[cfg(feature = "concrete_playback")] + pub(crate) unsafe fn any_raw_internal() -> T { + concrete_playback::any_raw_internal::() + } + + /// This low-level function returns nondet bytes of size T. + #[rustc_diagnostic_item = "KaniAnyRaw"] + #[inline(never)] + #[allow(dead_code)] + pub fn any_raw_inner() -> T { + kani_intrinsic() + } + + /// Function used to generate panic with a static message as this is the only one currently + /// supported by Kani display. + /// + /// During verification this will get replaced by `assert(false)`. For concrete executions, we just + /// invoke the regular `std::panic!()` function. This function is used by our standard library + /// overrides, but not the other way around. + #[inline(never)] + #[rustc_diagnostic_item = "KaniPanic"] + #[doc(hidden)] + pub const fn panic(message: &'static str) -> ! { + panic!("{}", message) + } + + /// An empty body that can be used to define Kani intrinsic functions. + /// + /// A Kani intrinsic is a function that is interpreted by Kani compiler. + /// While we could use `unreachable!()` or `panic!()` as the body of a kani intrinsic + /// function, both cause Kani to produce a warning since we don't support caller location. + /// (see https://github.com/model-checking/kani/issues/2010). + /// + /// This function is dead, since its caller is always handled via a hook anyway, + /// so we just need to put a body that rustc does not complain about. + /// An infinite loop works out nicely. + fn kani_intrinsic() -> T { + #[allow(clippy::empty_loop)] + loop {} + } + + pub mod internal { + /// A way to break the ownerhip rules. Only used by contracts where we can + /// guarantee it is done safely. + #[inline(never)] + #[doc(hidden)] + #[rustc_diagnostic_item = "KaniUntrackedDeref"] + pub fn untracked_deref(_: &T) -> T { + todo!() + } + + /// CBMC contracts currently has a limitation where `free` has to be in scope. + /// However, if there is no dynamic allocation in the harness, slicing removes `free` from the + /// scope. + /// + /// Thus, this function will basically translate into: + /// ```c + /// // This is a no-op. + /// free(NULL); + /// ``` + #[inline(never)] + #[doc(hidden)] + #[rustc_diagnostic_item = "KaniInitContracts"] + pub fn init_contracts() {} + } + }; +} diff --git a/library/kani_macros/src/sysroot/contracts/shared.rs b/library/kani_macros/src/sysroot/contracts/shared.rs index b0d323b12ca3..3bef8b68f688 100644 --- a/library/kani_macros/src/sysroot/contracts/shared.rs +++ b/library/kani_macros/src/sysroot/contracts/shared.rs @@ -82,7 +82,10 @@ pub fn make_unsafe_argument_copies( let arg_values = renaming_map.keys(); ( quote!(#(let #arg_names = kani::internal::untracked_deref(&#arg_values);)*), + #[cfg(not(feature = "no_core"))] quote!(#(std::mem::forget(#also_arg_names);)*), + #[cfg(feature = "no_core")] + quote!(#(core::mem::forget(#also_arg_names);)*), ) } diff --git a/tests/script-based-pre/verify_std_cmd/verify_std.expected b/tests/script-based-pre/verify_std_cmd/verify_std.expected index 028e8949815f..449148c5d904 100644 --- a/tests/script-based-pre/verify_std_cmd/verify_std.expected +++ b/tests/script-based-pre/verify_std_cmd/verify_std.expected @@ -1,14 +1,12 @@ [TEST] Run kani verify-std -Checking harness kani::check_stub... -VERIFICATION:- SUCCESSFUL -Checking harness kani::check_success... +Checking harness verify::dummy_proof... VERIFICATION:- SUCCESSFUL -Checking harness kani::check_panic... -VERIFICATION:- SUCCESSFUL (encountered one or more panics as expected) +Checking harness verify::harness... +VERIFICATION:- SUCCESSFUL Checking harness num::verify::check_non_zero... VERIFICATION:- SUCCESSFUL -Complete - 4 successfully verified harnesses, 0 failures, 4 total. +Complete - 3 successfully verified harnesses, 0 failures, 3 total. diff --git a/tests/script-based-pre/verify_std_cmd/verify_std.sh b/tests/script-based-pre/verify_std_cmd/verify_std.sh index cc960ee2bfaf..9897a036dd22 100755 --- a/tests/script-based-pre/verify_std_cmd/verify_std.sh +++ b/tests/script-based-pre/verify_std_cmd/verify_std.sh @@ -23,51 +23,26 @@ cp -r "${STD_PATH}" "${TMP_DIR}" # Insert a small harness in one of the standard library modules. CORE_CODE=' #[cfg(kani)] -#[unstable(feature = "kani", issue = "none")] -pub mod kani { - pub use kani_core::proof; - - #[rustc_diagnostic_item = "KaniAnyRaw"] - #[inline(never)] - pub fn any_raw_inner() -> T { - loop {} - } +kani_core::kani_lib!(core); - #[inline(never)] - #[rustc_diagnostic_item = "KaniAssert"] - pub const fn assert(cond: bool, msg: &'\''static str) { - let _ = cond; - let _ = msg; - } - - #[kani_core::proof] - #[kani_core::should_panic] - fn check_panic() { - let num: u8 = any_raw_inner(); - assert!(num != 0, "Found zero"); - } - - #[kani_core::proof] - fn check_success() { - let orig: u8 = any_raw_inner(); - let mid = orig as i8; - let new = mid as u8; - assert!(orig == new, "Conversion round trip works"); - } +#[cfg(kani)] +#[unstable(feature = "kani", issue = "none")] +pub mod verify { + use crate::kani; - pub fn assert_true(cond: bool) { - assert!(cond) + #[kani::proof] + pub fn harness() { + kani::assert(true, "yay"); } - pub fn assert_false(cond: bool) { - assert!(!cond) + #[kani::proof_for_contract(fake_function)] + fn dummy_proof() { + fake_function(true); } - #[kani_core::proof] - #[kani_core::stub(assert_true, assert_false)] - fn check_stub() { - // Check this is in fact asserting false. - assert_true(false) + #[kani::requires(x == true)] + fn fake_function(x: bool) -> bool { + x } } ' @@ -100,7 +75,7 @@ cat ${TMP_DIR}/std_lib.rs >> ${TMP_DIR}/library/std/src/lib.rs echo "[TEST] Run kani verify-std" export RUST_BACKTRACE=1 -kani verify-std -Z unstable-options "${TMP_DIR}/library" --target-dir "${TMP_DIR}/target" -Z stubbing +kani verify-std -Z unstable-options "${TMP_DIR}/library" --target-dir "${TMP_DIR}/target" -Z function-contracts -Z stubbing # Cleanup rm -r ${TMP_DIR} diff --git a/tools/build-kani/src/sysroot.rs b/tools/build-kani/src/sysroot.rs index f3ece1ee006a..ffd08fa7eda0 100644 --- a/tools/build-kani/src/sysroot.rs +++ b/tools/build-kani/src/sysroot.rs @@ -103,7 +103,7 @@ fn build_playback_lib(compiler_path: &Path) -> Result<()> { /// Build the no core library folder that will be used during std verification. fn build_no_core_lib(compiler_path: &Path) -> Result<()> { - let extra_args = ["--features=kani_macros/no_core"]; + let extra_args = ["--features=kani_macros/no_core", "--features=kani_core/no_core"]; let packages = ["kani_core", "kani_macros"]; let artifacts = build_kani_lib(compiler_path, &packages, &extra_args, &[])?; copy_artifacts(&artifacts, &kani_no_core_lib(), false) From e922d73e0b1a2768b070b49d93e7ebd97d695c10 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 11 Jun 2024 10:22:19 +0200 Subject: [PATCH 131/225] Update the rust toolchain to nightly-2024-06-11 (#3225) Changes required due to: - rust-lang/rust@a34c26e7ec Make body_owned_by return the body directly. - rust-lang/rust@333458c2cb Uplift TypeRelation and Relate - rust-lang/rust@459ce3f6bb Add an intrinsic for `ptr::metadata` - rust-lang/rust@7e08f80b34 Split smir `Const` into `TyConst` and `MirConst` - rust-lang/rust@eb584a23bf offset_of: allow (unstably) taking the offset of slice tail fields - rust-lang/rust@16e8803579 Update cargo Resolves: #3218 --- Cargo.lock | 8 +-- cprover_bindings/Cargo.toml | 2 +- kani-compiler/Cargo.toml | 2 +- .../codegen_cprover_gotoc/codegen/operand.rs | 38 ++++++++++-- .../codegen_cprover_gotoc/codegen/rvalue.rs | 60 ++++++++++++++++--- .../src/codegen_cprover_gotoc/codegen/typ.rs | 4 +- .../src/codegen_cprover_gotoc/utils/utils.rs | 2 +- kani-compiler/src/kani_middle/attributes.rs | 2 +- kani-compiler/src/kani_middle/reachability.rs | 4 ++ .../src/kani_middle/transform/body.rs | 6 +- .../src/kani_middle/transform/check_values.rs | 28 +++++---- .../src/kani_middle/transform/contracts.rs | 2 +- .../kani_middle/transform/kani_intrinsics.rs | 6 +- .../src/kani_middle/transform/stubs.rs | 2 +- rust-toolchain.toml | 2 +- tests/kani/FatPointers/metadata.rs | 24 ++++++++ tools/build-kani/src/sysroot.rs | 4 +- 17 files changed, 150 insertions(+), 46 deletions(-) create mode 100644 tests/kani/FatPointers/metadata.rs diff --git a/Cargo.lock b/Cargo.lock index bf34011c5f04..7660ff5c83ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -390,9 +390,9 @@ checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -999,9 +999,9 @@ dependencies = [ [[package]] name = "string-interner" -version = "0.15.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07f9fdfdd31a0ff38b59deb401be81b73913d76c9cc5b1aed4e1330a223420b9" +checksum = "1c6a0d765f5807e98a091107bae0a56ea3799f66a5de47b2c84c94a39c09974e" dependencies = [ "cfg-if", "hashbrown", diff --git a/cprover_bindings/Cargo.toml b/cprover_bindings/Cargo.toml index b98b09c68a58..63dfbf6781cd 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.15.0" +string-interner = "0.17.0" tracing = "0.1" linear-map = {version = "1.2", features = ["serde_impl"]} diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index 2ef273802028..05002ed1eb27 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -12,7 +12,7 @@ publish = false cbmc = { path = "../cprover_bindings", package = "cprover_bindings", optional = true } clap = { version = "4.4.11", features = ["derive", "cargo"] } home = "0.5" -itertools = "0.12" +itertools = "0.13" kani_metadata = {path = "../kani_metadata"} lazy_static = "1.4.0" num = { version = "0.4.0", optional = true } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs index c8aba08ba5ed..a7a3c43edbff 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs @@ -11,8 +11,8 @@ use stable_mir::mir::alloc::{AllocId, GlobalAlloc}; use stable_mir::mir::mono::{Instance, StaticDef}; use stable_mir::mir::Operand; use stable_mir::ty::{ - Allocation, Const, ConstantKind, FloatTy, FnDef, GenericArgs, IntTy, RigidTy, Size, Span, Ty, - TyKind, UintTy, + Allocation, ConstantKind, FloatTy, FnDef, GenericArgs, IntTy, MirConst, RigidTy, Size, Span, + Ty, TyConst, TyConstKind, TyKind, UintTy, }; use stable_mir::{CrateDef, CrateItem}; use tracing::{debug, trace}; @@ -63,17 +63,17 @@ impl<'tcx> GotocCtx<'tcx> { ) -> Expr { let stable_const = rustc_internal::stable(constant); let stable_span = rustc_internal::stable(span); - self.codegen_const(&stable_const, stable_span) + self.codegen_const_ty(&stable_const, stable_span) } - /// Generate a goto expression that represents a constant. + /// Generate a goto expression that represents a MIR-level constant. /// /// There are two possible constants included in the body of an instance: /// - Allocated: It will have its byte representation already defined. We try to eagerly /// generate code for it as simple literals or constants if possible. Otherwise, we create /// a memory allocation for them and access them indirectly. /// - ZeroSized: These are ZST constants and they just need to match the right type. - pub fn codegen_const(&mut self, constant: &Const, span: Option) -> Expr { + pub fn codegen_const(&mut self, constant: &MirConst, span: Option) -> Expr { trace!(?constant, "codegen_constant"); match constant.kind() { ConstantKind::Allocated(alloc) => self.codegen_allocation(alloc, constant.ty(), span), @@ -90,6 +90,34 @@ impl<'tcx> GotocCtx<'tcx> { ConstantKind::Param(..) | ConstantKind::Unevaluated(..) => { unreachable!() } + ConstantKind::Ty(t) => self.codegen_const_ty(t, span), + } + } + + /// Generate a goto expression that represents a type-level constant. + /// + /// There are two possible constants included in the body of an instance: + /// - Allocated: It will have its byte representation already defined. We try to eagerly + /// generate code for it as simple literals or constants if possible. Otherwise, we create + /// a memory allocation for them and access them indirectly. + /// - ZeroSized: These are ZST constants and they just need to match the right type. + pub fn codegen_const_ty(&mut self, constant: &TyConst, span: Option) -> Expr { + trace!(?constant, "codegen_constant"); + match constant.kind() { + TyConstKind::ZSTValue(lit_ty) => { + match lit_ty.kind() { + // Rust "function items" (not closures, not function pointers, see `codegen_fndef`) + TyKind::RigidTy(RigidTy::FnDef(def, args)) => { + self.codegen_fndef(def, &args, span) + } + _ => Expr::init_unit(self.codegen_ty_stable(*lit_ty), &self.symbol_table), + } + } + TyConstKind::Value(ty, alloc) => self.codegen_allocation(alloc, *ty, span), + TyConstKind::Bound(..) => unreachable!(), + TyConstKind::Param(..) | TyConstKind::Unevaluated(..) => { + unreachable!() + } } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index 89a50971243b..21185624942c 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -18,7 +18,7 @@ use cbmc::goto_program::{ use cbmc::MachineModel; use cbmc::{btree_string_map, InternString, InternedString}; use num::bigint::BigInt; -use rustc_middle::ty::{TyCtxt, VtblEntry}; +use rustc_middle::ty::{ParamEnv, TyCtxt, VtblEntry}; use rustc_smir::rustc_internal; use rustc_target::abi::{FieldsShape, TagEncoding, Variants}; use stable_mir::abi::{Primitive, Scalar, ValueAbi}; @@ -26,7 +26,7 @@ use stable_mir::mir::mono::Instance; use stable_mir::mir::{ AggregateKind, BinOp, CastKind, NullOp, Operand, Place, PointerCoercion, Rvalue, UnOp, }; -use stable_mir::ty::{ClosureKind, Const, IntTy, RigidTy, Size, Ty, TyKind, UintTy, VariantIdx}; +use stable_mir::ty::{ClosureKind, IntTy, RigidTy, Size, Ty, TyConst, TyKind, UintTy, VariantIdx}; use std::collections::BTreeMap; use tracing::{debug, trace, warn}; @@ -161,7 +161,7 @@ impl<'tcx> GotocCtx<'tcx> { } /// Codegens expressions of the type `let a = [4u8; 6];` - fn codegen_rvalue_repeat(&mut self, op: &Operand, sz: &Const, loc: Location) -> Expr { + fn codegen_rvalue_repeat(&mut self, op: &Operand, sz: &TyConst, loc: Location) -> Expr { let op_expr = self.codegen_operand_stable(op); let width = sz.eval_target_usize().unwrap(); op_expr.array_constant(width).with_location(loc) @@ -170,7 +170,7 @@ impl<'tcx> GotocCtx<'tcx> { fn codegen_rvalue_len(&mut self, p: &Place) -> Expr { let pt = self.place_ty_stable(p); match pt.kind() { - TyKind::RigidTy(RigidTy::Array(_, sz)) => self.codegen_const(&sz, None), + TyKind::RigidTy(RigidTy::Array(_, sz)) => self.codegen_const_ty(&sz, None), TyKind::RigidTy(RigidTy::Slice(_)) => { unwrap_or_return_codegen_unimplemented!(self, self.codegen_place_stable(p)) .fat_ptr_goto_expr @@ -779,9 +779,10 @@ impl<'tcx> GotocCtx<'tcx> { .with_size_of_annotation(self.codegen_ty_stable(*t)), NullOp::AlignOf => Expr::int_constant(layout.align.abi.bytes(), Type::size_t()), NullOp::OffsetOf(fields) => Expr::int_constant( - layout + self.tcx .offset_of_subfield( - self, + ParamEnv::reveal_all(), + layout, fields.iter().map(|(var_idx, field_idx)| { ( rustc_internal::internal(self.tcx, var_idx), @@ -814,6 +815,51 @@ impl<'tcx> GotocCtx<'tcx> { } } UnOp::Neg => self.codegen_operand_stable(e).neg(), + UnOp::PtrMetadata => { + let src_goto_expr = self.codegen_operand_stable(e); + let dst_goto_typ = self.codegen_ty_stable(res_ty); + debug!( + "PtrMetadata |{:?}| with result type |{:?}|", + src_goto_expr, dst_goto_typ + ); + if let Some(_vtable_typ) = + src_goto_expr.typ().lookup_field_type("vtable", &self.symbol_table) + { + let vtable_expr = src_goto_expr.member("vtable", &self.symbol_table); + let dst_components = + dst_goto_typ.lookup_components(&self.symbol_table).unwrap(); + assert_eq!(dst_components.len(), 2); + assert_eq!(dst_components[0].name(), "_vtable_ptr"); + assert!(dst_components[0].typ().is_pointer()); + assert_eq!(dst_components[1].name(), "_phantom"); + self.assert_is_rust_phantom_data_like(&dst_components[1].typ()); + Expr::struct_expr( + dst_goto_typ, + btree_string_map![ + ("_vtable_ptr", vtable_expr.cast_to(dst_components[0].typ())), + ( + "_phantom", + Expr::struct_expr( + dst_components[1].typ(), + [].into(), + &self.symbol_table + ) + ) + ], + &self.symbol_table, + ) + } else if let Some(len_typ) = + src_goto_expr.typ().lookup_field_type("len", &self.symbol_table) + { + assert_eq!(len_typ, dst_goto_typ); + src_goto_expr.member("len", &self.symbol_table) + } else { + unreachable!( + "fat pointer with neither vtable nor len: {:?}", + src_goto_expr + ); + } + } }, Rvalue::Discriminant(p) => { let place = @@ -1453,7 +1499,7 @@ impl<'tcx> GotocCtx<'tcx> { ) => { // Cast to a slice fat pointer. assert_eq!(src_elt_type, dst_elt_type); - let dst_goto_len = self.codegen_const(&src_elt_count, None); + let dst_goto_len = self.codegen_const_ty(&src_elt_count, None); let src_pointee_ty = pointee_type_stable(coerce_info.src_ty).unwrap(); let dst_data_expr = if src_pointee_ty.kind().is_array() { src_goto_expr.cast_to(self.codegen_ty_stable(src_elt_type).to_pointer()) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index c091d0b74ead..6d00bda5bce4 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -11,8 +11,8 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::print::FmtPrinter; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{ - self, AdtDef, Const, CoroutineArgs, FloatTy, Instance, IntTy, PolyFnSig, Ty, TyCtxt, TyKind, - UintTy, VariantDef, VtblEntry, + self, AdtDef, Const, CoroutineArgs, CoroutineArgsExt, FloatTy, Instance, IntTy, PolyFnSig, Ty, + TyCtxt, TyKind, UintTy, VariantDef, VtblEntry, }; use rustc_middle::ty::{List, TypeFoldable}; use rustc_smir::rustc_internal; diff --git a/kani-compiler/src/codegen_cprover_gotoc/utils/utils.rs b/kani-compiler/src/codegen_cprover_gotoc/utils/utils.rs index e369f64beda9..16edca2a826c 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/utils/utils.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/utils/utils.rs @@ -125,7 +125,7 @@ impl<'tcx> GotocCtx<'tcx> { } /// Best effort check if the struct represents a rust `std::marker::PhantomData` - fn assert_is_rust_phantom_data_like(&self, t: &Type) { + pub fn assert_is_rust_phantom_data_like(&self, t: &Type) { // TODO: A `std::marker::PhantomData` appears to be an empty struct, in the cases we've seen. // Is there something smarter we can do here? assert!(t.is_struct_like()); diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index e2617739e461..a9c777d84506 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -633,7 +633,7 @@ fn parse_modify_values<'a>( TokenTree::Token(token, _) => { if let TokenKind::Ident(id, _) = &token.kind { let hir = tcx.hir(); - let bid = hir.body_owned_by(local_def_id); + let bid = hir.body_owned_by(local_def_id).id(); Some( hir.body_param_names(bid) .zip(mir.args_iter()) diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 186bd65a7840..9c6c7e0ec350 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -376,6 +376,10 @@ impl<'a, 'tcx> MirVisitor for MonoItemsFnCollector<'a, 'tcx> { // Nothing to do here. return; } + ConstantKind::Ty(_) => { + // Nothing to do here. + return; + } }; self.collect_allocation(&allocation); } diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index bd6c9be6581a..e058c5aedc98 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -7,7 +7,7 @@ use crate::kani_middle::find_fn_def; use rustc_middle::ty::TyCtxt; use stable_mir::mir::mono::Instance; use stable_mir::mir::*; -use stable_mir::ty::{Const, GenericArgs, Span, Ty, UintTy}; +use stable_mir::ty::{GenericArgs, MirConst, Span, Ty, UintTy}; use std::fmt::Debug; use std::mem; @@ -80,12 +80,12 @@ impl MutableBody { } pub fn new_str_operand(&mut self, msg: &str, span: Span) -> Operand { - let literal = Const::from_str(msg); + let literal = MirConst::from_str(msg); Operand::Constant(Constant { span, user_ty: None, literal }) } pub fn new_const_operand(&mut self, val: u128, uint_ty: UintTy, span: Span) -> Operand { - let literal = Const::try_from_uint(val, uint_ty).unwrap(); + let literal = MirConst::try_from_uint(val, uint_ty).unwrap(); Operand::Constant(Constant { span, user_ty: None, literal }) } diff --git a/kani-compiler/src/kani_middle/transform/check_values.rs b/kani-compiler/src/kani_middle/transform/check_values.rs index 893b2244f2ca..c7c9548bd609 100644 --- a/kani-compiler/src/kani_middle/transform/check_values.rs +++ b/kani-compiler/src/kani_middle/transform/check_values.rs @@ -18,7 +18,8 @@ use crate::kani_middle::transform::body::{CheckType, MutableBody, SourceInstruct use crate::kani_middle::transform::check_values::SourceOp::UnsupportedCheck; use crate::kani_middle::transform::{TransformPass, TransformationType}; use crate::kani_queries::QueryDb; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{Const, TyCtxt}; +use rustc_smir::rustc_internal; use stable_mir::abi::{FieldsShape, Scalar, TagEncoding, ValueAbi, VariantsShape, WrappingRange}; use stable_mir::mir::mono::{Instance, InstanceKind}; use stable_mir::mir::visit::{Location, PlaceContext, PlaceRef}; @@ -28,7 +29,7 @@ use stable_mir::mir::{ Statement, StatementKind, Terminator, TerminatorKind, }; use stable_mir::target::{MachineInfo, MachineSize}; -use stable_mir::ty::{AdtKind, Const, IndexedVal, RigidTy, Ty, TyKind, UintTy}; +use stable_mir::ty::{AdtKind, IndexedVal, MirConst, RigidTy, Ty, TyKind, UintTy}; use stable_mir::CrateDef; use std::fmt::Debug; use strum_macros::AsRefStr; @@ -65,7 +66,7 @@ impl TransformPass for ValidValuePass { // Do not cache body.blocks().len() since it will change as we add new checks. for bb_idx in 0..new_body.blocks().len() { let Some(candidate) = - CheckValueVisitor::find_next(&new_body, bb_idx, bb_idx >= orig_len) + CheckValueVisitor::find_next(tcx, &new_body, bb_idx, bb_idx >= orig_len) else { continue; }; @@ -118,7 +119,7 @@ impl ValidValuePass { ) { let span = source.span(body.blocks()); let rvalue = Rvalue::Use(Operand::Constant(Constant { - literal: Const::from_bool(false), + literal: MirConst::from_bool(false), span, user_ty: None, })); @@ -262,7 +263,8 @@ struct UnsafeInstruction { /// - Transmute /// - MemCopy /// - Cast -struct CheckValueVisitor<'a> { +struct CheckValueVisitor<'a, 'b> { + tcx: TyCtxt<'b>, locals: &'a [LocalDecl], /// Whether we should skip the next instruction, since it might've been instrumented already. /// When we instrument an instruction, we partition the basic block, and the instruction that @@ -279,13 +281,15 @@ struct CheckValueVisitor<'a> { machine: MachineInfo, } -impl<'a> CheckValueVisitor<'a> { +impl<'a, 'b> CheckValueVisitor<'a, 'b> { fn find_next( + tcx: TyCtxt<'b>, body: &'a MutableBody, bb: BasicBlockIdx, skip_first: bool, ) -> Option { let mut visitor = CheckValueVisitor { + tcx, locals: body.locals(), skip_next: skip_first, current: SourceInstruction::Statement { idx: 0, bb }, @@ -305,7 +309,7 @@ impl<'a> CheckValueVisitor<'a> { } } -impl<'a> MirVisitor for CheckValueVisitor<'a> { +impl<'a, 'b> MirVisitor for CheckValueVisitor<'a, 'b> { fn visit_statement(&mut self, stmt: &Statement, location: Location) { if self.skip_next { self.skip_next = false; @@ -388,12 +392,10 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> { match validity { Ok(ranges) if ranges.is_empty() => {} Ok(ranges) => { - let sz = Const::try_from_uint( - target_ty.layout().unwrap().shape().size.bytes() - as u128, - UintTy::Usize, - ) - .unwrap(); + let sz = rustc_internal::stable(Const::from_target_usize( + self.tcx, + target_ty.layout().unwrap().shape().size.bytes() as u64, + )); self.push_target(SourceOp::BytesValidity { target_ty, rvalue: Rvalue::Repeat(args[1].clone(), sz), diff --git a/kani-compiler/src/kani_middle/transform/contracts.rs b/kani-compiler/src/kani_middle/transform/contracts.rs index c99874cc42a8..10fa6775a0d9 100644 --- a/kani-compiler/src/kani_middle/transform/contracts.rs +++ b/kani-compiler/src/kani_middle/transform/contracts.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::TyCtxt; use rustc_smir::rustc_internal; use stable_mir::mir::mono::Instance; use stable_mir::mir::{Body, Constant, Operand, TerminatorKind}; -use stable_mir::ty::{Const as MirConst, FnDef, RigidTy, TyKind}; +use stable_mir::ty::{FnDef, MirConst, RigidTy, TyKind}; use stable_mir::{CrateDef, DefId}; use std::collections::HashSet; use std::fmt::Debug; diff --git a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs index 6354e3777aca..053b210babb0 100644 --- a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs +++ b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs @@ -18,7 +18,7 @@ use stable_mir::mir::{ BinOp, Body, Constant, Operand, Place, Rvalue, Statement, StatementKind, RETURN_LOCAL, }; use stable_mir::target::MachineInfo; -use stable_mir::ty::{Const, RigidTy, TyKind}; +use stable_mir::ty::{MirConst, RigidTy, TyKind}; use std::fmt::Debug; use strum_macros::AsRefStr; use tracing::trace; @@ -82,7 +82,7 @@ impl IntrinsicGeneratorPass { Rvalue::Use(Operand::Constant(Constant { span, user_ty: None, - literal: Const::from_bool(true), + literal: MirConst::from_bool(true), })), ); let stmt = Statement { kind: assign, span }; @@ -116,7 +116,7 @@ impl IntrinsicGeneratorPass { Err(msg) => { // We failed to retrieve all the valid ranges. let rvalue = Rvalue::Use(Operand::Constant(Constant { - literal: Const::from_bool(false), + literal: MirConst::from_bool(false), span, user_ty: None, })); diff --git a/kani-compiler/src/kani_middle/transform/stubs.rs b/kani-compiler/src/kani_middle/transform/stubs.rs index 6d8849a9f1fe..7c4319319b8e 100644 --- a/kani-compiler/src/kani_middle/transform/stubs.rs +++ b/kani-compiler/src/kani_middle/transform/stubs.rs @@ -12,7 +12,7 @@ use rustc_smir::rustc_internal; use stable_mir::mir::mono::Instance; use stable_mir::mir::visit::{Location, MirVisitor}; use stable_mir::mir::{Body, Constant, LocalDecl, Operand, Terminator, TerminatorKind}; -use stable_mir::ty::{Const as MirConst, FnDef, RigidTy, TyKind}; +use stable_mir::ty::{FnDef, MirConst, RigidTy, TyKind}; use stable_mir::CrateDef; use std::collections::HashMap; use std::fmt::Debug; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 7490dc74d65f..d1baa70550de 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-05-28" +channel = "nightly-2024-06-11" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/kani/FatPointers/metadata.rs b/tests/kani/FatPointers/metadata.rs new file mode 100644 index 000000000000..f76798e0f9f4 --- /dev/null +++ b/tests/kani/FatPointers/metadata.rs @@ -0,0 +1,24 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#![feature(ptr_metadata)] + +struct S { + x: i32, +} + +trait T {} + +impl T for S {} + +#[kani::proof] +fn ptr_metadata() { + assert_eq!(std::ptr::metadata("foo"), 3_usize); + + let s = S { x: 42 }; + let p: &dyn T = &s; + assert_eq!(std::ptr::metadata(p).size_of(), 4_usize); + + let c: char = 'c'; + assert_eq!(std::ptr::metadata(&c), ()); +} diff --git a/tools/build-kani/src/sysroot.rs b/tools/build-kani/src/sysroot.rs index ffd08fa7eda0..4f2265515762 100644 --- a/tools/build-kani/src/sysroot.rs +++ b/tools/build-kani/src/sysroot.rs @@ -265,11 +265,11 @@ fn build_artifacts(cargo_cmd: &mut Child) -> Vec { /// Extra arguments to be given to `cargo build` while building Kani's binaries. /// Note that the following arguments are always provided: /// ```bash -/// cargo build --bins -Z unstable-options --out-dir $KANI_SYSROOT/bin/ +/// cargo build --bins -Z unstable-options --artifact-dir $KANI_SYSROOT/bin/ /// ``` pub fn build_bin>(extra_args: &[T]) -> Result { let out_dir = kani_sysroot_bin(); - let args = ["--bins", "-Z", "unstable-options", "--out-dir", out_dir.to_str().unwrap()]; + let args = ["--bins", "-Z", "unstable-options", "--artifact-dir", out_dir.to_str().unwrap()]; Command::new("cargo") .arg("build") .args(extra_args) From b6e4972b303b21fdbbd92af522409de4e10a628f Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 11 Jun 2024 19:28:07 +0200 Subject: [PATCH 132/225] Pointers-out-of-scope test no longer is "fixme" (#3252) We handle these adequately as of 0cc4b2463828. Resolves: #77 --- .../dead-invalid-access-via-raw/{expected => main.expected} | 0 tests/expected/dead-invalid-access-via-raw/value.expected | 2 ++ .../dead-invalid-access-via-raw/value.rs} | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) rename tests/expected/dead-invalid-access-via-raw/{expected => main.expected} (100%) create mode 100644 tests/expected/dead-invalid-access-via-raw/value.expected rename tests/{kani/Pointers_OutOfScopeFail/fixme_main.rs => expected/dead-invalid-access-via-raw/value.rs} (96%) diff --git a/tests/expected/dead-invalid-access-via-raw/expected b/tests/expected/dead-invalid-access-via-raw/main.expected similarity index 100% rename from tests/expected/dead-invalid-access-via-raw/expected rename to tests/expected/dead-invalid-access-via-raw/main.expected diff --git a/tests/expected/dead-invalid-access-via-raw/value.expected b/tests/expected/dead-invalid-access-via-raw/value.expected new file mode 100644 index 000000000000..858d44d54ea4 --- /dev/null +++ b/tests/expected/dead-invalid-access-via-raw/value.expected @@ -0,0 +1,2 @@ +Failed Checks: assertion failed: *p_subscoped == 7 +Failed Checks: dereference failure: dead object diff --git a/tests/kani/Pointers_OutOfScopeFail/fixme_main.rs b/tests/expected/dead-invalid-access-via-raw/value.rs similarity index 96% rename from tests/kani/Pointers_OutOfScopeFail/fixme_main.rs rename to tests/expected/dead-invalid-access-via-raw/value.rs index 5bbcb93930bb..6160325174b7 100644 --- a/tests/kani/Pointers_OutOfScopeFail/fixme_main.rs +++ b/tests/expected/dead-invalid-access-via-raw/value.rs @@ -1,6 +1,5 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-verify-fail #[kani::proof] fn main() { From ca110f7b2c1d7cf219595863ef355713b306dc28 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Tue, 11 Jun 2024 12:12:04 -0700 Subject: [PATCH 133/225] Fix a few issues with std verification (#3255) - Contracts cannot duplicate some attributes. I think we need a better solution than just duplicate all attributes, but for now just filter the ones we know are problematic. - Do not make contract generated functions `const` when annotating constant functions. - I also moved the compilation of no_core up since it is much faster. ## Call-out Need to add a test. Resolves #3251 Resolves #3254 --- kani-driver/src/call_cargo.rs | 15 +- library/kani_core/src/lib.rs | 83 ++++- library/kani_core/src/mem.rs | 313 ++++++++++++++++++ .../src/sysroot/contracts/bootstrap.rs | 2 + .../src/sysroot/contracts/check.rs | 2 + .../kani_macros/src/sysroot/contracts/mod.rs | 10 +- .../src/sysroot/contracts/replace.rs | 2 + .../src/sysroot/contracts/shared.rs | 27 ++ .../function-contract/const_fn.expected | 2 + tests/expected/function-contract/const_fn.rs | 16 + .../verify_std_cmd/verify_std.sh | 3 + tools/build-kani/src/sysroot.rs | 4 +- 12 files changed, 466 insertions(+), 13 deletions(-) create mode 100644 library/kani_core/src/mem.rs create mode 100644 tests/expected/function-contract/const_fn.expected create mode 100644 tests/expected/function-contract/const_fn.rs diff --git a/kani-driver/src/call_cargo.rs b/kani-driver/src/call_cargo.rs index ade9785b8fdd..4e8e83b562af 100644 --- a/kani-driver/src/call_cargo.rs +++ b/kani-driver/src/call_cargo.rs @@ -84,8 +84,19 @@ impl KaniSession { .env("CARGO_TERM_PROGRESS_WHEN", "never") .env("__CARGO_TESTS_ONLY_SRC_ROOT", std_path.as_os_str()); - let build_artifacts = self.run_build(cmd)?; - Ok(build_artifacts.into_iter().filter_map(map_kani_artifact).collect()) + Ok(self + .run_build(cmd)? + .into_iter() + .filter_map(|artifact| { + if artifact.target.crate_types.contains(&CRATE_TYPE_LIB.to_string()) + || artifact.target.crate_types.contains(&CRATE_TYPE_RLIB.to_string()) + { + map_kani_artifact(artifact) + } else { + None + } + }) + .collect()) } /// Calls `cargo_build` to generate `*.symtab.json` files in `target_dir` diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index c31e964eeb0a..05ccb91eacb6 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -19,6 +19,7 @@ #![no_core] mod arbitrary; +mod mem; pub use kani_macros::*; @@ -34,15 +35,21 @@ macro_rules! kani_lib { #[cfg(kani)] #[unstable(feature = "kani", issue = "none")] pub mod kani { - pub use kani_core::{ensures, proof, proof_for_contract, requires, should_panic}; - kani_core::kani_intrinsics!(); + pub use kani_core::{ + ensures, modifies, proof, proof_for_contract, requires, should_panic, + }; + kani_core::kani_intrinsics!(core); kani_core::generate_arbitrary!(core); + + pub mod mem { + kani_core::kani_mem!(core); + } } }; (kani) => { pub use kani_core::*; - kani_core::kani_intrinsics!(); + kani_core::kani_intrinsics!(std); kani_core::generate_arbitrary!(std); }; } @@ -54,7 +61,7 @@ macro_rules! kani_lib { /// TODO: Use this inside kani library so that we dont have to maintain two copies of the same intrinsics. #[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. @@ -262,6 +269,74 @@ macro_rules! kani_intrinsics { } pub mod internal { + + /// Helper trait for code generation for `modifies` contracts. + /// + /// We allow the user to provide us with a pointer-like object that we convert as needed. + #[doc(hidden)] + pub trait Pointer<'a> { + /// Type of the pointed-to data + type Inner; + + /// Used for checking assigns contracts where we pass immutable references to the function. + /// + /// We're using a reference to self here, because the user can use just a plain function + /// argument, for instance one of type `&mut _`, in the `modifies` clause which would move it. + unsafe fn decouple_lifetime(&self) -> &'a Self::Inner; + + /// used for havocking on replecement of a `modifies` clause. + unsafe fn assignable(self) -> &'a mut Self::Inner; + } + + impl<'a, 'b, T> Pointer<'a> for &'b T { + type Inner = T; + unsafe fn decouple_lifetime(&self) -> &'a Self::Inner { + $core::mem::transmute(*self) + } + + #[allow(clippy::transmute_ptr_to_ref)] + unsafe fn assignable(self) -> &'a mut Self::Inner { + $core::mem::transmute(self as *const T) + } + } + + impl<'a, 'b, T> Pointer<'a> for &'b mut T { + type Inner = T; + + #[allow(clippy::transmute_ptr_to_ref)] + unsafe fn decouple_lifetime(&self) -> &'a Self::Inner { + $core::mem::transmute::<_, &&'a T>(self) + } + + unsafe fn assignable(self) -> &'a mut Self::Inner { + $core::mem::transmute(self) + } + } + + impl<'a, T> Pointer<'a> for *const T { + type Inner = T; + unsafe fn decouple_lifetime(&self) -> &'a Self::Inner { + &**self as &'a T + } + + #[allow(clippy::transmute_ptr_to_ref)] + unsafe fn assignable(self) -> &'a mut Self::Inner { + $core::mem::transmute(self) + } + } + + impl<'a, T> Pointer<'a> for *mut T { + type Inner = T; + unsafe fn decouple_lifetime(&self) -> &'a Self::Inner { + &**self as &'a T + } + + #[allow(clippy::transmute_ptr_to_ref)] + unsafe fn assignable(self) -> &'a mut Self::Inner { + $core::mem::transmute(self) + } + } + /// A way to break the ownerhip rules. Only used by contracts where we can /// guarantee it is done safely. #[inline(never)] diff --git a/library/kani_core/src/mem.rs b/library/kani_core/src/mem.rs new file mode 100644 index 000000000000..8dd4a27cb03a --- /dev/null +++ b/library/kani_core/src/mem.rs @@ -0,0 +1,313 @@ +// 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. + +#[macro_export] +macro_rules! kani_mem { + ($core:tt) => { + use super::kani_intrinsic; + use private::Internal; + use $core::mem::{align_of, size_of}; + use $core::ptr::{DynMetadata, NonNull, Pointee}; + + /// Check if the pointer is valid for write access according to [crate::mem] conditions 1, 2 + /// and 3. + /// + /// Note this function also checks for pointer alignment. Use [self::can_write_unaligned] + /// if you don't want to fail for unaligned pointers. + /// + /// This function does not check if the value stored is valid for the given type. Use + /// [self::can_dereference] for that. + /// + /// This function will panic today if the pointer is not null, and it points to an unallocated or + /// deallocated memory location. This is an existing Kani limitation. + /// See for more details. + // TODO: Add this back! We might need to rename the attribute. + //#[crate::unstable( + // feature = "mem-predicates", + // issue = 2690, + // reason = "experimental memory predicate API" + //)] + pub fn can_write(ptr: *mut T) -> bool + where + T: ?Sized, + ::Metadata: PtrProperties, + { + // The interface takes a mutable pointer to improve readability of the signature. + // However, using constant pointer avoid unnecessary instrumentation, and it is as powerful. + // Hence, cast to `*const T`. + let ptr: *const T = ptr; + let (thin_ptr, metadata) = ptr.to_raw_parts(); + metadata.is_ptr_aligned(thin_ptr, Internal) && is_inbounds(&metadata, thin_ptr) + } + + /// Check if the pointer is valid for unaligned write access according to [crate::mem] conditions + /// 1, 2 and 3. + /// + /// Note this function succeeds for unaligned pointers. See [self::can_write] if you also + /// want to check pointer alignment. + /// + /// This function will panic today if the pointer is not null, and it points to an unallocated or + /// deallocated memory location. This is an existing Kani limitation. + /// See for more details. + // TODO: Add this back! We might need to rename the attribute. + //#[crate::unstable( + // feature = "mem-predicates", + // issue = 2690, + // reason = "experimental memory predicate API" + //)] + pub fn can_write_unaligned(ptr: *const T) -> bool + where + T: ?Sized, + ::Metadata: PtrProperties, + { + let (thin_ptr, metadata) = ptr.to_raw_parts(); + is_inbounds(&metadata, thin_ptr) + } + + /// Checks that pointer `ptr` point to a valid value of type `T`. + /// + /// For that, the pointer has to be a valid pointer according to [crate::mem] conditions 1, 2 + /// and 3, + /// and the value stored must respect the validity invariants for type `T`. + /// + /// TODO: Kani should automatically add those checks when a de-reference happens. + /// + /// + /// This function will panic today if the pointer is not null, and it points to an unallocated or + /// deallocated memory location. This is an existing Kani limitation. + /// See for more details. + //#[crate::unstable( + // feature = "mem-predicates", + // issue = 2690, + // reason = "experimental memory predicate API" + //)] + #[allow(clippy::not_unsafe_ptr_arg_deref)] + pub fn can_dereference(ptr: *const T) -> bool + where + T: ?Sized, + ::Metadata: PtrProperties, + { + let (thin_ptr, metadata) = ptr.to_raw_parts(); + metadata.is_ptr_aligned(thin_ptr, Internal) + && is_inbounds(&metadata, thin_ptr) + && unsafe { has_valid_value(ptr) } + } + + /// Checks that pointer `ptr` point to a valid value of type `T`. + /// + /// For that, the pointer has to be a valid pointer according to [crate::mem] conditions 1, 2 + /// and 3, + /// and the value stored must respect the validity invariants for type `T`. + /// + /// Note this function succeeds for unaligned pointers. See [self::can_dereference] if you also + /// want to check pointer alignment. + /// + /// This function will panic today if the pointer is not null, and it points to an unallocated or + /// deallocated memory location. This is an existing Kani limitation. + /// See for more details. + // TODO: Add this back! We might need to rename the attribute. + //#[crate::unstable( + // feature = "mem-predicates", + // issue = 2690, + // reason = "experimental memory predicate API" + //)] + #[allow(clippy::not_unsafe_ptr_arg_deref)] + pub fn can_read_unaligned(ptr: *const T) -> bool + where + T: ?Sized, + ::Metadata: PtrProperties, + { + let (thin_ptr, metadata) = ptr.to_raw_parts(); + is_inbounds(&metadata, thin_ptr) && unsafe { has_valid_value(ptr) } + } + + /// Checks that `data_ptr` points to an allocation that can hold data of size calculated from `T`. + /// + /// This will panic if `data_ptr` points to an invalid `non_null` + fn is_inbounds(metadata: &M, data_ptr: *const ()) -> bool + where + M: PtrProperties, + T: ?Sized, + { + let sz = metadata.pointee_size(Internal); + if sz == 0 { + true // ZST pointers are always valid including nullptr. + } else if data_ptr.is_null() { + false + } 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 + assert!( + unsafe { is_allocated(data_ptr, 0) }, + "Kani does not support reasoning about pointer to unallocated memory", + ); + unsafe { is_allocated(data_ptr, sz) } + } + } + + mod private { + /// Define like this to restrict usage of PtrProperties functions outside Kani. + #[derive(Copy, Clone)] + pub struct Internal; + } + + /// Trait that allow us to extract information from pointers without de-referencing them. + #[doc(hidden)] + pub trait PtrProperties { + fn pointee_size(&self, _: Internal) -> usize; + + /// A pointer is aligned if its address is a multiple of its minimum alignment. + fn is_ptr_aligned(&self, ptr: *const (), internal: Internal) -> bool { + let min = self.min_alignment(internal); + ptr as usize % min == 0 + } + + fn min_alignment(&self, _: Internal) -> usize; + + fn dangling(&self, _: Internal) -> *const (); + } + + /// Get the information for sized types (they don't have metadata). + impl PtrProperties for () { + fn pointee_size(&self, _: Internal) -> usize { + size_of::() + } + + fn min_alignment(&self, _: Internal) -> usize { + align_of::() + } + + fn dangling(&self, _: Internal) -> *const () { + NonNull::::dangling().as_ptr() as *const _ + } + } + + /// Get the information from the str metadata. + impl PtrProperties for usize { + #[inline(always)] + fn pointee_size(&self, _: Internal) -> usize { + *self + } + + /// String slices are a UTF-8 representation of characters that have the same layout as slices + /// of type [u8]. + /// + fn min_alignment(&self, _: Internal) -> usize { + align_of::() + } + + fn dangling(&self, _: Internal) -> *const () { + NonNull::::dangling().as_ptr() as _ + } + } + + /// Get the information from the slice metadata. + impl PtrProperties<[T]> for usize { + fn pointee_size(&self, _: Internal) -> usize { + *self * size_of::() + } + + fn min_alignment(&self, _: Internal) -> usize { + align_of::() + } + + fn dangling(&self, _: Internal) -> *const () { + NonNull::::dangling().as_ptr() as _ + } + } + + /// Get the information from the vtable. + impl PtrProperties for DynMetadata + where + T: ?Sized, + { + fn pointee_size(&self, _: Internal) -> usize { + self.size_of() + } + + fn min_alignment(&self, _: Internal) -> usize { + self.align_of() + } + + fn dangling(&self, _: Internal) -> *const () { + NonNull::<&T>::dangling().as_ptr() as _ + } + } + + /// Check if the pointer `_ptr` contains an allocated address of size equal or greater than `_size`. + /// + /// # Safety + /// + /// This function should only be called to ensure a pointer is always valid, i.e., in an assertion + /// context. + /// + /// I.e.: This function always returns `true` if the pointer is valid. + /// Otherwise, it returns non-det boolean. + #[rustc_diagnostic_item = "KaniIsAllocated"] + #[inline(never)] + unsafe fn is_allocated(_ptr: *const (), _size: usize) -> bool { + kani_intrinsic() + } + + /// Check if the value stored in the given location satisfies type `T` validity requirements. + /// + /// # Safety + /// + /// - Users have to ensure that the pointer is aligned the pointed memory is allocated. + #[rustc_diagnostic_item = "KaniValidValue"] + #[inline(never)] + unsafe fn has_valid_value(_ptr: *const T) -> bool { + kani_intrinsic() + } + + /// Get the object ID of the given pointer. + #[rustc_diagnostic_item = "KaniPointerObject"] + #[inline(never)] + pub fn pointer_object(_ptr: *const T) -> usize { + kani_intrinsic() + } + + /// Get the object offset of the given pointer. + #[rustc_diagnostic_item = "KaniPointerOffset"] + #[inline(never)] + pub fn pointer_offset(_ptr: *const T) -> usize { + kani_intrinsic() + } + }; +} diff --git a/library/kani_macros/src/sysroot/contracts/bootstrap.rs b/library/kani_macros/src/sysroot/contracts/bootstrap.rs index 6a48bab08070..dee0bca42c54 100644 --- a/library/kani_macros/src/sysroot/contracts/bootstrap.rs +++ b/library/kani_macros/src/sysroot/contracts/bootstrap.rs @@ -74,6 +74,8 @@ impl<'a> ContractConditionsHandler<'a> { let mut wrapper_sig = sig.clone(); wrapper_sig.ident = recursion_wrapper_name; + // We use non-constant functions, thus, the wrapper cannot be constant. + wrapper_sig.constness = None; let args = pats_to_idents(&mut wrapper_sig.inputs).collect::>(); let also_args = args.iter(); diff --git a/library/kani_macros/src/sysroot/contracts/check.rs b/library/kani_macros/src/sysroot/contracts/check.rs index 473535e8d8fd..bd4c09b07796 100644 --- a/library/kani_macros/src/sysroot/contracts/check.rs +++ b/library/kani_macros/src/sysroot/contracts/check.rs @@ -120,6 +120,8 @@ impl<'a> ContractConditionsHandler<'a> { } let body = self.make_check_body(); let mut sig = self.annotated_fn.sig.clone(); + // We use non-constant functions, thus, the wrapper cannot be constant. + sig.constness = None; if let Some(ident) = override_function_dent { sig.ident = ident; } diff --git a/library/kani_macros/src/sysroot/contracts/mod.rs b/library/kani_macros/src/sysroot/contracts/mod.rs index de3b5da123ed..7a25cc390100 100644 --- a/library/kani_macros/src/sysroot/contracts/mod.rs +++ b/library/kani_macros/src/sysroot/contracts/mod.rs @@ -36,12 +36,12 @@ //! expanded. When we expand the first (outermost) `requires` or `ensures` //! attribute on such a function we re-emit the function unchanged but we also //! generate fresh "check" and "replace" functions that enforce the condition -//! carried by the attribute currently being expanded. We copy all additional -//! attributes from the original function to both the "check" and the "replace". -//! This allows us to deal both with renaming and also support non-contract -//! attributes. +//! carried by the attribute currently being expanded. //! -//! In addition to copying attributes we also add new marker attributes to +//! We don't copy all attributes from the original function since they may have +//! unintended consequences for the stubs, such as `inline` or `rustc_diagnostic_item`. +//! +//! We also add new marker attributes to //! advance the state machine. The "check" function gets a //! `kanitool::is_contract_generated(check)` attributes and analogous for //! replace. The re-emitted original meanwhile is decorated with diff --git a/library/kani_macros/src/sysroot/contracts/replace.rs b/library/kani_macros/src/sysroot/contracts/replace.rs index 546568dc8a1d..1a84e40523c2 100644 --- a/library/kani_macros/src/sysroot/contracts/replace.rs +++ b/library/kani_macros/src/sysroot/contracts/replace.rs @@ -116,6 +116,8 @@ impl<'a> ContractConditionsHandler<'a> { self.output.extend(quote!(#[kanitool::is_contract_generated(replace)])); } let mut sig = self.annotated_fn.sig.clone(); + // We use non-constant functions, thus, the wrapper cannot be constant. + sig.constness = None; let body = self.make_replace_body(); if let Some(ident) = override_function_ident { sig.ident = ident; diff --git a/library/kani_macros/src/sysroot/contracts/shared.rs b/library/kani_macros/src/sysroot/contracts/shared.rs index 3bef8b68f688..ed6b5467b260 100644 --- a/library/kani_macros/src/sysroot/contracts/shared.rs +++ b/library/kani_macros/src/sysroot/contracts/shared.rs @@ -53,7 +53,34 @@ impl<'a> ContractConditionsHandler<'a> { #[allow(dead_code, unused_variables, unused_mut)] )); } + + #[cfg(not(feature = "no_core"))] self.output.extend(self.annotated_fn.attrs.iter().flat_map(Attribute::to_token_stream)); + + // When verifying core and standard library, we need to add an unstable attribute to + // the functions generated by Kani. + // We also need to filter `rustc_diagnostic_item` attribute. + // We should consider a better strategy than just duplicating all attributes. + #[cfg(feature = "no_core")] + { + self.output.extend(quote!( + #[unstable(feature="kani", issue="none")] + )); + self.output.extend( + self.annotated_fn + .attrs + .iter() + .filter(|attr| { + if let Some(ident) = attr.path().get_ident() { + let name = ident.to_string(); + !name.starts_with("rustc") && !(name == "stable") + } else { + true + } + }) + .flat_map(Attribute::to_token_stream), + ); + } } } diff --git a/tests/expected/function-contract/const_fn.expected b/tests/expected/function-contract/const_fn.expected new file mode 100644 index 000000000000..10cf9fe451f0 --- /dev/null +++ b/tests/expected/function-contract/const_fn.expected @@ -0,0 +1,2 @@ +Checking harness check... +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/const_fn.rs b/tests/expected/function-contract/const_fn.rs new file mode 100644 index 000000000000..44b937cedce4 --- /dev/null +++ b/tests/expected/function-contract/const_fn.rs @@ -0,0 +1,16 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts -Zmem-predicates + +//! Check that Kani contract can be applied to a constant function. +//! + +#[kani::requires(kani::mem::can_dereference(arg))] +const unsafe fn dummy(arg: *const T) -> T { + std::ptr::read(arg) +} + +#[kani::proof_for_contract(dummy)] +fn check() { + unsafe { dummy(&kani::any::()) }; +} diff --git a/tests/script-based-pre/verify_std_cmd/verify_std.sh b/tests/script-based-pre/verify_std_cmd/verify_std.sh index 9897a036dd22..0cc2bab48429 100755 --- a/tests/script-based-pre/verify_std_cmd/verify_std.sh +++ b/tests/script-based-pre/verify_std_cmd/verify_std.sh @@ -40,7 +40,10 @@ pub mod verify { fake_function(true); } + /// Add a `rustc_diagnostic_item` to ensure this works. + /// See for more details. #[kani::requires(x == true)] + #[rustc_diagnostic_item = "fake_function"] fn fake_function(x: bool) -> bool { x } diff --git a/tools/build-kani/src/sysroot.rs b/tools/build-kani/src/sysroot.rs index 4f2265515762..ca42fea3f441 100644 --- a/tools/build-kani/src/sysroot.rs +++ b/tools/build-kani/src/sysroot.rs @@ -75,9 +75,9 @@ fn build_target() -> &'static str { /// - The `lib-playback/` folder contains the sysroot used for playback. pub fn build_lib(bin_folder: &Path) -> Result<()> { let compiler_path = bin_folder.join("kani-compiler"); + build_no_core_lib(&compiler_path)?; build_verification_lib(&compiler_path)?; - build_playback_lib(&compiler_path)?; - build_no_core_lib(&compiler_path) + build_playback_lib(&compiler_path) } /// Build the `lib/` folder for the new sysroot used during verification. From 34b35d8bfb10b4520ac0d738e29d62544aecc2de Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Tue, 11 Jun 2024 22:28:14 -0700 Subject: [PATCH 134/225] Fix contract of constant fn with effect feature (#3259) We now check if the host effect index is present. If so, remove it before performing stub operation. Resolves #3258 --- kani-compiler/src/kani_middle/stubbing/mod.rs | 41 ++++++++++++++++--- .../src/kani_middle/transform/stubs.rs | 8 +++- .../const_fn_with_effect.expected | 2 + .../function-contract/const_fn_with_effect.rs | 18 ++++++++ .../verify_std_cmd/verify_std.expected | 5 ++- .../verify_std_cmd/verify_std.sh | 13 ++++++ 6 files changed, 78 insertions(+), 9 deletions(-) create mode 100644 tests/expected/function-contract/const_fn_with_effect.expected create mode 100644 tests/expected/function-contract/const_fn_with_effect.rs diff --git a/kani-compiler/src/kani_middle/stubbing/mod.rs b/kani-compiler/src/kani_middle/stubbing/mod.rs index 1abcc0ec4f80..f21a02b7681e 100644 --- a/kani-compiler/src/kani_middle/stubbing/mod.rs +++ b/kani-compiler/src/kani_middle/stubbing/mod.rs @@ -17,7 +17,7 @@ use rustc_smir::rustc_internal; use stable_mir::mir::mono::Instance; use stable_mir::mir::visit::{Location, MirVisitor}; use stable_mir::mir::Constant; -use stable_mir::ty::FnDef; +use stable_mir::ty::{FnDef, RigidTy, TyKind}; use stable_mir::{CrateDef, CrateItem}; use self::annotations::update_stub_mapping; @@ -37,6 +37,24 @@ pub fn harness_stub_map( stub_pairs } +/// Retrieve the index of the host parameter if old definition has one, but not the new definition. +/// +/// This is to allow constant functions to be stubbed by non-constant functions when the +/// `effect` feature is on. +/// +/// Note that the opposite is not supported today, but users should be able to change their stubs. +/// +/// Note that this has no effect at runtime. +pub fn contract_host_param(tcx: TyCtxt, old_def: FnDef, new_def: FnDef) -> Option { + let old_generics = tcx.generics_of(rustc_internal::internal(tcx, old_def.def_id())); + let new_generics = tcx.generics_of(rustc_internal::internal(tcx, new_def.def_id())); + if old_generics.host_effect_index.is_some() && new_generics.host_effect_index.is_none() { + old_generics.host_effect_index + } else { + None + } +} + /// Checks whether the stub is compatible with the original function/method: do /// the arities and types (of the parameters and return values) match up? This /// does **NOT** check whether the type variables are constrained to implement @@ -61,15 +79,26 @@ pub fn check_compatibility(tcx: TyCtxt, old_def: FnDef, new_def: FnDef) -> Resul // Check whether the numbers of generic parameters match. let old_def_id = rustc_internal::internal(tcx, old_def.def_id()); let new_def_id = rustc_internal::internal(tcx, new_def.def_id()); - let old_num_generics = tcx.generics_of(old_def_id).count(); - let stub_num_generics = tcx.generics_of(new_def_id).count(); - if old_num_generics != stub_num_generics { + let old_ty = rustc_internal::stable(tcx.type_of(old_def_id)).value; + let new_ty = rustc_internal::stable(tcx.type_of(new_def_id)).value; + let TyKind::RigidTy(RigidTy::FnDef(_, mut old_args)) = old_ty.kind() else { + unreachable!("Expected function, but found {old_ty}") + }; + let TyKind::RigidTy(RigidTy::FnDef(_, new_args)) = new_ty.kind() else { + unreachable!("Expected function, but found {new_ty}") + }; + if let Some(idx) = contract_host_param(tcx, old_def, new_def) { + old_args.0.remove(idx); + } + + // TODO: We should check for the parameter type too or replacement will fail. + if old_args.0.len() != new_args.0.len() { let msg = format!( "mismatch in the number of generic parameters: original function/method `{}` takes {} generic parameters(s), stub `{}` takes {}", old_def.name(), - old_num_generics, + old_args.0.len(), new_def.name(), - stub_num_generics + new_args.0.len(), ); return Err(msg); } diff --git a/kani-compiler/src/kani_middle/transform/stubs.rs b/kani-compiler/src/kani_middle/transform/stubs.rs index 7c4319319b8e..53db34dc902f 100644 --- a/kani-compiler/src/kani_middle/transform/stubs.rs +++ b/kani-compiler/src/kani_middle/transform/stubs.rs @@ -3,7 +3,7 @@ //! This module contains code related to the MIR-to-MIR pass that performs the //! stubbing of functions and methods. use crate::kani_middle::codegen_units::Stubs; -use crate::kani_middle::stubbing::validate_stub_const; +use crate::kani_middle::stubbing::{contract_host_param, validate_stub_const}; use crate::kani_middle::transform::body::{MutMirVisitor, MutableBody}; use crate::kani_middle::transform::{TransformPass, TransformationType}; use crate::kani_queries::QueryDb; @@ -46,8 +46,12 @@ impl TransformPass for FnStubPass { fn transform(&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 TyKind::RigidTy(RigidTy::FnDef(fn_def, mut args)) = ty.kind() { if let Some(replace) = self.stubs.get(&fn_def) { + if let Some(idx) = contract_host_param(tcx, fn_def, *replace) { + debug!(?idx, "FnStubPass::transform remove_host_param"); + args.0.remove(idx); + } 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) diff --git a/tests/expected/function-contract/const_fn_with_effect.expected b/tests/expected/function-contract/const_fn_with_effect.expected new file mode 100644 index 000000000000..10cf9fe451f0 --- /dev/null +++ b/tests/expected/function-contract/const_fn_with_effect.expected @@ -0,0 +1,2 @@ +Checking harness check... +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/const_fn_with_effect.rs b/tests/expected/function-contract/const_fn_with_effect.rs new file mode 100644 index 000000000000..d57c1f42fe16 --- /dev/null +++ b/tests/expected/function-contract/const_fn_with_effect.rs @@ -0,0 +1,18 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts -Zmem-predicates + +//! Check that Kani contract can be applied to a constant function. +//! + +#![feature(effects)] + +#[kani::requires(kani::mem::can_dereference(arg))] +const unsafe fn dummy(arg: *const T) -> T { + std::ptr::read(arg) +} + +#[kani::proof_for_contract(dummy)] +fn check() { + unsafe { dummy(&kani::any::()) }; +} diff --git a/tests/script-based-pre/verify_std_cmd/verify_std.expected b/tests/script-based-pre/verify_std_cmd/verify_std.expected index 449148c5d904..aa30da375dd3 100644 --- a/tests/script-based-pre/verify_std_cmd/verify_std.expected +++ b/tests/script-based-pre/verify_std_cmd/verify_std.expected @@ -6,7 +6,10 @@ VERIFICATION:- SUCCESSFUL Checking harness verify::harness... VERIFICATION:- SUCCESSFUL +Checking harness verify::check_dummy_read... +VERIFICATION:- SUCCESSFUL + Checking harness num::verify::check_non_zero... VERIFICATION:- SUCCESSFUL -Complete - 3 successfully verified harnesses, 0 failures, 3 total. +Complete - 4 successfully verified harnesses, 0 failures, 4 total. diff --git a/tests/script-based-pre/verify_std_cmd/verify_std.sh b/tests/script-based-pre/verify_std_cmd/verify_std.sh index 0cc2bab48429..3253ad29756e 100755 --- a/tests/script-based-pre/verify_std_cmd/verify_std.sh +++ b/tests/script-based-pre/verify_std_cmd/verify_std.sh @@ -47,6 +47,19 @@ pub mod verify { fn fake_function(x: bool) -> bool { x } + + #[kani::proof_for_contract(dummy_read)] + fn check_dummy_read() { + let val: char = kani::any(); + assert_eq!(unsafe { dummy_read(&val) }, val); + } + + /// Ensure we can verify constant functions. + #[kani::requires(kani::mem::can_dereference(ptr))] + #[rustc_diagnostic_item = "dummy_read"] + const unsafe fn dummy_read(ptr: *const T) -> T { + *ptr + } } ' From db54783933b8f90e892a54a39a1a815007faca8d Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 12 Jun 2024 03:48:28 -0700 Subject: [PATCH 135/225] Fix a few more issues with the std library (#3261) - Enable Kani to use fallback fn body for intrinsics, so they can be verified. - Until is implemented, we have to check has_body and must_be_overridden - Export all kani_macro definitions. Rename `unstable` to avoid conflict with the Rust standard library one. - Dump stable mir body since transformations are made at that level. - I just did this as I was debugging things. Co-authored-by: Michael Tautschnig --- .../codegen_cprover_gotoc/codegen/operand.rs | 2 +- .../codegen/statement.rs | 22 ++++++++---- .../compiler_interface.rs | 2 +- kani-compiler/src/kani_middle/attributes.rs | 4 +-- kani-compiler/src/kani_middle/mod.rs | 30 ++++++++-------- kani-compiler/src/kani_middle/reachability.rs | 13 ++++++- library/kani/src/lib.rs | 2 ++ library/kani_core/src/arbitrary.rs | 2 ++ library/kani_core/src/lib.rs | 5 ++- library/kani_macros/src/lib.rs | 2 +- .../src/sysroot/contracts/shared.rs | 4 ++- tests/cargo-ui/unstable-attr/defs/src/lib.rs | 4 +-- tests/cargo-ui/unstable-attr/invalid/expected | 34 +++++++++---------- .../cargo-ui/unstable-attr/invalid/src/lib.rs | 8 ++--- 14 files changed, 80 insertions(+), 54 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs index a7a3c43edbff..3b71cfec91c2 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs @@ -614,7 +614,7 @@ impl<'tcx> GotocCtx<'tcx> { /// Ensure that the given instance is in the symbol table, returning the symbol. fn codegen_func_symbol(&mut self, instance: Instance) -> &Symbol { - let sym = if instance.is_foreign_item() { + let sym = if instance.is_foreign_item() && !instance.has_body() { // Get the symbol that represents a foreign instance. self.codegen_foreign_fn(instance) } else { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 0c43f566e5ac..1e81cccd0c07 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -518,13 +518,21 @@ impl<'tcx> GotocCtx<'tcx> { if let Some(instance) = instance_opt && matches!(instance.kind, InstanceKind::Intrinsic) { - return self.codegen_funcall_of_intrinsic( - instance, - &args, - &destination, - target.map(|bb| bb), - span, - ); + let TyKind::RigidTy(RigidTy::FnDef(def, _)) = instance.ty().kind() else { + unreachable!("Expected function type for intrinsic: {instance:?}") + }; + // The compiler is currently transitioning how to handle intrinsic fallback body. + // Until https://github.com/rust-lang/project-stable-mir/issues/79 is implemented + // we have to check `must_be_overridden` and `has_body`. + if def.as_intrinsic().unwrap().must_be_overridden() || !instance.has_body() { + return self.codegen_funcall_of_intrinsic( + instance, + &args, + &destination, + target.map(|bb| bb), + span, + ); + } } let loc = self.codegen_span_stable(span); diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index ca66e1c0e165..6e2d5cb46e96 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -93,7 +93,7 @@ impl GotocCodegenBackend { || collect_reachable_items(tcx, &mut transformer, starting_items), "codegen reachability analysis", ); - dump_mir_items(tcx, &items, &symtab_goto.with_extension("kani.mir")); + dump_mir_items(tcx, &mut transformer, &items, &symtab_goto.with_extension("kani.mir")); // Follow rustc naming convention (cx is abbrev for context). // https://rustc-dev-guide.rust-lang.org/conventions.html#naming-conventions diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index a9c777d84506..c74f0c744093 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -782,10 +782,10 @@ impl<'a> UnstableAttrParseError<'a> { tcx.dcx() .struct_span_err( self.attr.span, - format!("failed to parse `#[kani::unstable]`: {}", self.reason), + format!("failed to parse `#[kani::unstable_feature]`: {}", self.reason), ) .with_note(format!( - "expected format: #[kani::unstable({}, {}, {})]", + "expected format: #[kani::unstable_feature({}, {}, {})]", r#"feature="""#, r#"issue="""#, r#"reason="""# )) .emit() diff --git a/kani-compiler/src/kani_middle/mod.rs b/kani-compiler/src/kani_middle/mod.rs index 257d337cd576..ce9a80fe1f80 100644 --- a/kani-compiler/src/kani_middle/mod.rs +++ b/kani-compiler/src/kani_middle/mod.rs @@ -6,9 +6,9 @@ use std::collections::HashSet; use std::path::Path; +use crate::kani_middle::transform::BodyTransformation; use crate::kani_queries::QueryDb; use rustc_hir::{def::DefKind, def_id::LOCAL_CRATE}; -use rustc_middle::mir::write_mir_pretty; use rustc_middle::span_bug; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOf, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, @@ -21,9 +21,9 @@ use rustc_span::source_map::respan; use rustc_span::Span; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{HasDataLayout, TargetDataLayout}; -use stable_mir::mir::mono::{InstanceKind, MonoItem}; +use stable_mir::mir::mono::{Instance, MonoItem}; use stable_mir::ty::{FnDef, RigidTy, Span as SpanStable, TyKind}; -use stable_mir::{CrateDef, DefId}; +use stable_mir::CrateDef; use std::fs::File; use std::io::BufWriter; use std::io::Write; @@ -93,17 +93,19 @@ pub fn check_reachable_items(tcx: TyCtxt, queries: &QueryDb, items: &[MonoItem]) } /// Print MIR for the reachable items if the `--emit mir` option was provided to rustc. -pub fn dump_mir_items(tcx: TyCtxt, items: &[MonoItem], output: &Path) { +pub fn dump_mir_items( + tcx: TyCtxt, + transformer: &mut BodyTransformation, + items: &[MonoItem], + output: &Path, +) { /// Convert MonoItem into a DefId. /// Skip stuff that we cannot generate the MIR items. - fn visible_item(item: &MonoItem) -> Option<(MonoItem, DefId)> { + fn get_instance(item: &MonoItem) -> Option { match item { // Exclude FnShims and others that cannot be dumped. - MonoItem::Fn(instance) if matches!(instance.kind, InstanceKind::Item) => { - Some((item.clone(), instance.def.def_id())) - } - MonoItem::Fn(..) => None, - MonoItem::Static(def) => Some((item.clone(), def.def_id())), + MonoItem::Fn(instance) => Some(*instance), + MonoItem::Static(def) => Some((*def).into()), MonoItem::GlobalAsm(_) => None, } } @@ -114,10 +116,10 @@ pub fn dump_mir_items(tcx: TyCtxt, items: &[MonoItem], output: &Path) { let mut writer = BufWriter::new(out_file); // For each def_id, dump their MIR - for (item, def_id) in items.iter().filter_map(visible_item) { - writeln!(writer, "// Item: {item:?}").unwrap(); - write_mir_pretty(tcx, Some(rustc_internal::internal(tcx, def_id)), &mut writer) - .unwrap(); + for instance in items.iter().filter_map(get_instance) { + writeln!(writer, "// Item: {} ({})", instance.name(), instance.mangled_name()).unwrap(); + let body = transformer.body(tcx, instance); + let _ = body.dump(&mut writer, &instance.name()); } } } diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 9c6c7e0ec350..4ebff6868bf5 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -262,11 +262,22 @@ impl<'a, 'tcx> MonoItemsFnCollector<'a, 'tcx> { /// Collect an instance depending on how it is used (invoked directly or via fn_ptr). fn collect_instance(&mut self, instance: Instance, is_direct_call: bool) { let should_collect = match instance.kind { - InstanceKind::Virtual { .. } | InstanceKind::Intrinsic => { + InstanceKind::Virtual { .. } => { // Instance definition has no body. assert!(is_direct_call, "Expected direct call {instance:?}"); false } + InstanceKind::Intrinsic => { + // Intrinsics may have a fallback body. + assert!(is_direct_call, "Expected direct call {instance:?}"); + let TyKind::RigidTy(RigidTy::FnDef(def, _)) = instance.ty().kind() else { + unreachable!("Expected function type for intrinsic: {instance:?}") + }; + // The compiler is currently transitioning how to handle intrinsic fallback body. + // Until https://github.com/rust-lang/project-stable-mir/issues/79 is implemented + // we have to check `must_be_overridden` and `has_body`. + !def.as_intrinsic().unwrap().must_be_overridden() && instance.has_body() + } InstanceKind::Shim | InstanceKind::Item => true, }; if should_collect && should_codegen_locally(&instance) { diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index e81ddbe7904b..acf1e08e0441 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -323,4 +323,6 @@ pub use core::assert as __kani__workaround_core_assert; // Kani proc macros must be in a separate crate pub use kani_macros::*; +pub(crate) use kani_macros::unstable_feature as unstable; + pub mod contracts; diff --git a/library/kani_core/src/arbitrary.rs b/library/kani_core/src/arbitrary.rs index e7ac4dc81508..d202df4ead1d 100644 --- a/library/kani_core/src/arbitrary.rs +++ b/library/kani_core/src/arbitrary.rs @@ -72,6 +72,8 @@ macro_rules! generate_arbitrary { } // Generate trivial arbitrary values + trivial_arbitrary!(()); + trivial_arbitrary!(u8); trivial_arbitrary!(u16); trivial_arbitrary!(u32); diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index 05ccb91eacb6..de808ffaf918 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -35,9 +35,8 @@ macro_rules! kani_lib { #[cfg(kani)] #[unstable(feature = "kani", issue = "none")] pub mod kani { - pub use kani_core::{ - ensures, modifies, proof, proof_for_contract, requires, should_panic, - }; + // We need to list them all today because there is conflict with unstable. + pub use kani_core::*; kani_core::kani_intrinsics!(core); kani_core::generate_arbitrary!(core); diff --git a/library/kani_macros/src/lib.rs b/library/kani_macros/src/lib.rs index 44a0ca78ea41..0a000910174a 100644 --- a/library/kani_macros/src/lib.rs +++ b/library/kani_macros/src/lib.rs @@ -96,7 +96,7 @@ pub fn solver(attr: TokenStream, item: TokenStream) -> TokenStream { /// See https://model-checking.github.io/kani/rfc/rfcs/0006-unstable-api.html for more details. #[doc(hidden)] #[proc_macro_attribute] -pub fn unstable(attr: TokenStream, item: TokenStream) -> TokenStream { +pub fn unstable_feature(attr: TokenStream, item: TokenStream) -> TokenStream { attr_impl::unstable(attr, item) } diff --git a/library/kani_macros/src/sysroot/contracts/shared.rs b/library/kani_macros/src/sysroot/contracts/shared.rs index ed6b5467b260..bf204e6bca32 100644 --- a/library/kani_macros/src/sysroot/contracts/shared.rs +++ b/library/kani_macros/src/sysroot/contracts/shared.rs @@ -73,7 +73,9 @@ impl<'a> ContractConditionsHandler<'a> { .filter(|attr| { if let Some(ident) = attr.path().get_ident() { let name = ident.to_string(); - !name.starts_with("rustc") && !(name == "stable") + !name.starts_with("rustc") + && !(name == "stable") + && !(name == "unstable") } else { true } diff --git a/tests/cargo-ui/unstable-attr/defs/src/lib.rs b/tests/cargo-ui/unstable-attr/defs/src/lib.rs index 9515cf1d48a8..91b5a4d539bf 100644 --- a/tests/cargo-ui/unstable-attr/defs/src/lib.rs +++ b/tests/cargo-ui/unstable-attr/defs/src/lib.rs @@ -1,13 +1,13 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -#[kani::unstable(feature = "always_fails", reason = "do not enable", issue = "")] +#[kani::unstable_feature(feature = "always_fails", reason = "do not enable", issue = "")] pub fn always_fails() { assert!(false, "don't call me"); } /// We use "gen-c" since it has to be an existing feature. -#[kani::unstable(feature = "gen-c", reason = "internal fake api", issue = "")] +#[kani::unstable_feature(feature = "gen-c", reason = "internal fake api", issue = "")] pub fn no_op() { kani::cover!(true); } diff --git a/tests/cargo-ui/unstable-attr/invalid/expected b/tests/cargo-ui/unstable-attr/invalid/expected index 49db2367b832..fd6ee1c694c2 100644 --- a/tests/cargo-ui/unstable-attr/invalid/expected +++ b/tests/cargo-ui/unstable-attr/invalid/expected @@ -1,32 +1,32 @@ -error: failed to parse `#[kani::unstable]`: missing `feature` field\ +error: failed to parse `#[kani::unstable_feature]`: missing `feature` field\ lib.rs |\ -9 | #[kani::unstable(reason = "just checking", issue = "")]\ - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\ +9 | #[kani::unstable_feature(reason = "just checking", issue = "")]\ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\ |\ - = note: expected format: #[kani::unstable(feature="", issue="", reason="")]\ - = note: this error originates in the attribute macro `kani::unstable` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: expected format: #[kani::unstable_feature(feature="", issue="", reason="")]\ + = note: this error originates in the attribute macro `kani::unstable_feature` (in Nightly builds, run with -Z macro-backtrace for more info) -error: failed to parse `#[kani::unstable]`: expected "key = value" pair, but found `feature("invalid_args")`\ +error: failed to parse `#[kani::unstable_feature]`: expected "key = value" pair, but found `feature("invalid_args")`\ lib.rs\ |\ -| #[kani::unstable(feature("invalid_args"))]\ -| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\ +| #[kani::unstable_feature(feature("invalid_args"))]\ +| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\ |\ - = note: expected format: #[kani::unstable(feature="", issue="", reason="")] + = note: expected format: #[kani::unstable_feature(feature="", issue="", reason="")] -error: failed to parse `#[kani::unstable]`: expected "key = value" pair, but found `feature`\ +error: failed to parse `#[kani::unstable_feature]`: expected "key = value" pair, but found `feature`\ lib.rs\ |\ -| #[kani::unstable(feature, issue)]\ -| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\ +| #[kani::unstable_feature(feature, issue)]\ +| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\ |\ - = note: expected format: #[kani::unstable(feature="", issue="", reason="")] + = note: expected format: #[kani::unstable_feature(feature="", issue="", reason="")] -error: failed to parse `#[kani::unstable]`: expected "key = value" pair, but found `1010`\ +error: failed to parse `#[kani::unstable_feature]`: expected "key = value" pair, but found `1010`\ lib.rs\ |\ -| #[kani::unstable(1010)]\ -| ^^^^^^^^^^^^^^^^^^^^^^^\ +| #[kani::unstable_feature(1010)]\ +| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\ |\ - = note: expected format: #[kani::unstable(feature="", issue="", reason="")] + = note: expected format: #[kani::unstable_feature(feature="", issue="", reason="")] diff --git a/tests/cargo-ui/unstable-attr/invalid/src/lib.rs b/tests/cargo-ui/unstable-attr/invalid/src/lib.rs index 5fbfc768c883..f0d92f0882d7 100644 --- a/tests/cargo-ui/unstable-attr/invalid/src/lib.rs +++ b/tests/cargo-ui/unstable-attr/invalid/src/lib.rs @@ -6,18 +6,18 @@ //! we don't guarantee the order that these will be evaluated. //! TODO: We should break down this test to ensure all of these fail. -#[kani::unstable(reason = "just checking", issue = "")] +#[kani::unstable_feature(reason = "just checking", issue = "")] pub fn missing_feature() { todo!() } -#[kani::unstable(feature("invalid_args"))] +#[kani::unstable_feature(feature("invalid_args"))] pub fn invalid_fn_style() {} -#[kani::unstable(feature, issue)] +#[kani::unstable_feature(feature, issue)] pub fn invalid_list() {} -#[kani::unstable(1010)] +#[kani::unstable_feature(1010)] pub fn invalid_argument() {} #[kani::proof] From d588c011c88b82bb1a3bee2d4a17a0be89d7ce19 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 12 Jun 2024 14:27:21 +0200 Subject: [PATCH 136/225] Fix typed_swap for ZSTs (#3256) typed_swap needs to be a no-op on ZSTs as pointers to those have an arbitrary value in Kani. Resolves: #3182 --- .../codegen/intrinsic.rs | 61 +++++++++++-------- tests/kani/Intrinsics/typed_swap.rs | 7 +++ tests/std-checks/core/src/mem.rs | 2 - 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 4e965b4105ef..942c39203083 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -1880,12 +1880,11 @@ impl<'tcx> GotocCtx<'tcx> { "`dst` must be properly aligned", loc, ); - let deref = dst.dereference(); - if deref.typ().sizeof(&self.symbol_table) == 0 { + if self.is_zst_stable(pointee_type_stable(dst_typ).unwrap()) { // do not attempt to dereference (and assign) a ZST align_check } else { - let expr = deref.assign(src, loc); + let expr = dst.dereference().assign(src, loc); Stmt::block(vec![align_check, expr], loc) } } @@ -1991,30 +1990,42 @@ impl<'tcx> GotocCtx<'tcx> { let x = fargs.remove(0); let y = fargs.remove(0); - // if(same_object(x, y)) { - // assert(x + 1 <= y || y + 1 <= x); - // assume(x + 1 <= y || y + 1 <= x); - // } - let one = Expr::int_constant(1, Type::c_int()); - let non_overlapping = - x.clone().plus(one.clone()).le(y.clone()).or(y.clone().plus(one.clone()).le(x.clone())); - let non_overlapping_check = self.codegen_assert_assume( - non_overlapping, - PropertyClass::SafetyCheck, - "memory regions pointed to by `x` and `y` must not overlap", - loc, - ); - let non_overlapping_stmt = - Stmt::if_then_else(x.clone().same_object(y.clone()), non_overlapping_check, None, loc); + if self.is_zst_stable(pointee_type_stable(farg_types[0]).unwrap()) { + // do not attempt to dereference (and assign) a ZST + Stmt::skip(loc) + } else { + // if(same_object(x, y)) { + // assert(x + 1 <= y || y + 1 <= x); + // assume(x + 1 <= y || y + 1 <= x); + // } + let one = Expr::int_constant(1, Type::c_int()); + let non_overlapping = x + .clone() + .plus(one.clone()) + .le(y.clone()) + .or(y.clone().plus(one.clone()).le(x.clone())); + let non_overlapping_check = self.codegen_assert_assume( + non_overlapping, + PropertyClass::SafetyCheck, + "memory regions pointed to by `x` and `y` must not overlap", + loc, + ); + let non_overlapping_stmt = Stmt::if_then_else( + x.clone().same_object(y.clone()), + non_overlapping_check, + None, + loc, + ); - // T t = *y; *y = *x; *x = t; - let deref_y = y.clone().dereference(); - let (temp_var, assign_to_t) = - self.decl_temp_variable(deref_y.typ().clone(), Some(deref_y), loc); - let assign_to_y = y.dereference().assign(x.clone().dereference(), loc); - let assign_to_x = x.dereference().assign(temp_var, loc); + // T t = *y; *y = *x; *x = t; + let deref_y = y.clone().dereference(); + let (temp_var, assign_to_t) = + self.decl_temp_variable(deref_y.typ().clone(), Some(deref_y), loc); + let assign_to_y = y.dereference().assign(x.clone().dereference(), loc); + let assign_to_x = x.dereference().assign(temp_var, loc); - Stmt::block(vec![non_overlapping_stmt, assign_to_t, assign_to_y, assign_to_x], loc) + Stmt::block(vec![non_overlapping_stmt, assign_to_t, assign_to_y, assign_to_x], loc) + } } } diff --git a/tests/kani/Intrinsics/typed_swap.rs b/tests/kani/Intrinsics/typed_swap.rs index 4279bb713ece..996784b5181d 100644 --- a/tests/kani/Intrinsics/typed_swap.rs +++ b/tests/kani/Intrinsics/typed_swap.rs @@ -19,3 +19,10 @@ fn test_typed_swap_u32() { assert!(b == a_before); assert!(a == b_before); } + +#[kani::proof] +pub fn check_swap_unit() { + let mut x: () = kani::any(); + let mut y: () = kani::any(); + std::mem::swap(&mut x, &mut y) +} diff --git a/tests/std-checks/core/src/mem.rs b/tests/std-checks/core/src/mem.rs index 5ca439a18e1c..4f41d176a73a 100644 --- a/tests/std-checks/core/src/mem.rs +++ b/tests/std-checks/core/src/mem.rs @@ -45,8 +45,6 @@ mod verify { /// FIX-ME: Modifies clause fail with pointer to ZST. /// - /// FIX-ME: `typed_swap` intrisic fails for ZST. - /// #[kani::proof_for_contract(contracts::swap)] pub fn check_swap_unit() { let mut x: () = kani::any(); From 7dad84740cb34d3b2dd79487f1b810abf0bbe231 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 12 Jun 2024 15:35:11 +0200 Subject: [PATCH 137/225] Remove further uses of Location::none (#3253) We now only have 12 uses of `Location::none` left, all of which have no reasonable location to use. Resolves: #136 --- cprover_bindings/src/goto_program/expr.rs | 5 +- cprover_bindings/src/irep/to_irep.rs | 4 +- .../codegen_cprover_gotoc/codegen/assert.rs | 4 +- .../codegen_cprover_gotoc/codegen/contract.rs | 13 +- .../codegen/foreign_function.rs | 8 +- .../codegen/intrinsic.rs | 19 +-- .../codegen_cprover_gotoc/codegen/operand.rs | 113 ++++++++++-------- .../codegen_cprover_gotoc/codegen/place.rs | 30 ++--- .../codegen_cprover_gotoc/codegen/rvalue.rs | 65 +++++----- .../src/codegen_cprover_gotoc/codegen/span.rs | 4 - .../codegen/statement.rs | 38 +++--- .../codegen/static_var.rs | 2 +- .../codegen_cprover_gotoc/context/goto_ctx.rs | 15 ++- .../codegen_cprover_gotoc/overrides/hooks.rs | 35 +++--- kani-driver/src/cbmc_property_renderer.rs | 5 +- tests/cargo-kani/simple-extern/src/lib.rs | 3 + 16 files changed, 204 insertions(+), 159 deletions(-) diff --git a/cprover_bindings/src/goto_program/expr.rs b/cprover_bindings/src/goto_program/expr.rs index c76b58023784..75eb18df0abb 100644 --- a/cprover_bindings/src/goto_program/expr.rs +++ b/cprover_bindings/src/goto_program/expr.rs @@ -140,6 +140,7 @@ pub enum ExprValue { /// `({ op1; op2; ...})` StatementExpression { statements: Vec, + location: Location, }, /// A raw string constant. Note that you normally actually want a pointer to the first element. /// `"s"` @@ -739,10 +740,10 @@ impl Expr { /// /// e.g. `({ int y = foo (); int z; if (y > 0) z = y; else z = - y; z; })` /// `({ op1; op2; ...})` - pub fn statement_expression(ops: Vec, typ: Type) -> Self { + pub fn statement_expression(ops: Vec, typ: Type, loc: Location) -> Self { assert!(!ops.is_empty()); assert_eq!(ops.last().unwrap().get_expression().unwrap().typ, typ); - expr!(StatementExpression { statements: ops }, typ) + expr!(StatementExpression { statements: ops, location: loc }, typ).with_location(loc) } /// Internal helper function for Struct initalizer diff --git a/cprover_bindings/src/irep/to_irep.rs b/cprover_bindings/src/irep/to_irep.rs index 9ec8c49e80bf..8716c16d88e0 100644 --- a/cprover_bindings/src/irep/to_irep.rs +++ b/cprover_bindings/src/irep/to_irep.rs @@ -299,9 +299,9 @@ 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 } => side_effect_irep( + ExprValue::StatementExpression { statements: ops, location: loc } => side_effect_irep( IrepId::StatementExpression, - vec![Stmt::block(ops.to_vec(), Location::none()).to_irep(mm)], + vec![Stmt::block(ops.to_vec(), *loc).to_irep(mm)], ), ExprValue::StringConstant { s } => Irep { id: IrepId::StringConstant, diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs index 70a65ad6ebca..f78cf3eba707 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/assert.rs @@ -252,7 +252,7 @@ impl<'tcx> GotocCtx<'tcx> { t.nondet().as_stmt(loc), ]; - Expr::statement_expression(body, t).with_location(loc) + Expr::statement_expression(body, t, loc) } /// Kani does not currently support all MIR constructs. @@ -356,7 +356,7 @@ impl<'tcx> GotocCtx<'tcx> { // Encode __CPROVER_r_ok(ptr, size). // First, generate a CBMC expression representing the pointer. let ptr = { - let ptr_projection = self.codegen_place_stable(&ptr_place).unwrap(); + let ptr_projection = self.codegen_place_stable(&ptr_place, *loc).unwrap(); let place_ty = self.place_ty_stable(place); if self.use_thin_pointer_stable(place_ty) { ptr_projection.goto_expr().clone() diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs index 7edd64438728..5494a3a666bd 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs @@ -3,7 +3,7 @@ use crate::codegen_cprover_gotoc::GotocCtx; use crate::kani_middle::attributes::KaniAttributes; use cbmc::goto_program::FunctionContract; -use cbmc::goto_program::Lambda; +use cbmc::goto_program::{Lambda, Location}; use kani_metadata::AssignsContract; use rustc_hir::def_id::DefId as InternalDefId; use rustc_smir::rustc_internal; @@ -105,7 +105,11 @@ impl<'tcx> GotocCtx<'tcx> { /// Convert the Kani level contract into a CBMC level contract by creating a /// CBMC lambda. - fn codegen_modifies_contract(&mut self, modified_places: Vec) -> FunctionContract { + fn codegen_modifies_contract( + &mut self, + modified_places: Vec, + loc: Location, + ) -> FunctionContract { let goto_annotated_fn_name = self.current_fn().name(); let goto_annotated_fn_typ = self .symbol_table @@ -120,7 +124,7 @@ impl<'tcx> GotocCtx<'tcx> { Lambda::as_contract_for( &goto_annotated_fn_typ, None, - self.codegen_place_stable(&local.into()).unwrap().goto_expr.dereference(), + self.codegen_place_stable(&local.into(), loc).unwrap().goto_expr.dereference(), ) }) .collect(); @@ -138,7 +142,8 @@ impl<'tcx> GotocCtx<'tcx> { assert!(self.current_fn.is_none()); let body = instance.body().unwrap(); self.set_current_fn(instance, &body); - let goto_contract = self.codegen_modifies_contract(modified_places); + let goto_contract = + self.codegen_modifies_contract(modified_places, self.codegen_span_stable(body.span)); let name = self.current_fn().name(); self.symbol_table.attach_contract(name, goto_contract); 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 4ab72eb3a705..9d5374ca22a8 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/foreign_function.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/foreign_function.rs @@ -50,6 +50,7 @@ impl<'tcx> GotocCtx<'tcx> { pub fn codegen_foreign_fn(&mut self, instance: Instance) -> &Symbol { debug!(?instance, "codegen_foreign_function"); let fn_name = self.symbol_name_stable(instance).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() @@ -63,8 +64,7 @@ impl<'tcx> GotocCtx<'tcx> { // https://github.com/model-checking/kani/issues/2426 self.ensure(fn_name, |gcx, _| { let typ = gcx.codegen_ffi_type(instance); - Symbol::function(fn_name, typ, None, instance.name(), Location::none()) - .with_is_extern(true) + Symbol::function(fn_name, typ, None, instance.name(), loc).with_is_extern(true) }) } else { let shim_name = format!("{fn_name}_ffi_shim"); @@ -77,7 +77,7 @@ impl<'tcx> GotocCtx<'tcx> { typ, Some(gcx.codegen_ffi_shim(shim_name.as_str().into(), instance)), instance.name(), - Location::none(), + loc, ) }) } @@ -109,7 +109,7 @@ impl<'tcx> GotocCtx<'tcx> { } else { let ret_expr = unwrap_or_return_codegen_unimplemented_stmt!( self, - self.codegen_place_stable(ret_place) + self.codegen_place_stable(ret_place, loc) ) .goto_expr; let ret_type = ret_expr.typ().clone(); diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs index 942c39203083..29ea69eeb39b 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs @@ -33,11 +33,12 @@ impl<'tcx> GotocCtx<'tcx> { place: &Place, mut fargs: Vec, f: F, + loc: Location, ) -> Stmt { let arg1 = fargs.remove(0); let arg2 = fargs.remove(0); let expr = f(arg1, arg2); - self.codegen_expr_to_place_stable(place, expr, Location::none()) + self.codegen_expr_to_place_stable(place, expr, loc) } /// Given a call to an compiler intrinsic, generate the call and the `goto` terminator @@ -178,7 +179,7 @@ impl<'tcx> GotocCtx<'tcx> { // Intrinsics which encode a simple binary operation macro_rules! codegen_intrinsic_binop { - ($f:ident) => {{ self.binop(place, fargs, |a, b| a.$f(b)) }}; + ($f:ident) => {{ self.binop(place, fargs, |a, b| a.$f(b), loc) }}; } // Intrinsics which encode a simple binary operation which need a machine model @@ -208,7 +209,7 @@ impl<'tcx> GotocCtx<'tcx> { let alloc = stable_instance.try_const_eval(place_ty).unwrap(); // We assume that the intrinsic has type checked at this point, so // we can use the place type as the expression type. - let expr = self.codegen_allocation(&alloc, place_ty, Some(span)); + let expr = self.codegen_allocation(&alloc, place_ty, loc); self.codegen_expr_to_place_stable(&place, expr, loc) }}; } @@ -727,7 +728,7 @@ impl<'tcx> GotocCtx<'tcx> { let res = self.codegen_binop_with_overflow(binop, left, right, result_type.clone(), loc); self.codegen_expr_to_place_stable( place, - Expr::statement_expression(vec![res.as_stmt(loc)], result_type), + Expr::statement_expression(vec![res.as_stmt(loc)], result_type, loc), loc, ) } @@ -1042,9 +1043,11 @@ impl<'tcx> GotocCtx<'tcx> { let is_lhs_ok = lhs_var.clone().is_nonnull(); let is_rhs_ok = rhs_var.clone().is_nonnull(); let should_skip_pointer_checks = is_len_zero.and(is_lhs_ok).and(is_rhs_ok); - let place_expr = - unwrap_or_return_codegen_unimplemented_stmt!(self, self.codegen_place_stable(place)) - .goto_expr; + let place_expr = unwrap_or_return_codegen_unimplemented_stmt!( + self, + self.codegen_place_stable(place, loc) + ) + .goto_expr; let res = should_skip_pointer_checks.ternary( Expr::int_constant(0, place_expr.typ().clone()), // zero bytes are always equal (as long as pointers are nonnull and aligned) BuiltinFn::Memcmp @@ -1634,7 +1637,7 @@ impl<'tcx> GotocCtx<'tcx> { loc, ) } else { - self.binop(p, fargs, op_fun) + self.binop(p, fargs, op_fun, loc) } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs index 3b71cfec91c2..4c45b475a3a7 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs @@ -11,8 +11,8 @@ use stable_mir::mir::alloc::{AllocId, GlobalAlloc}; use stable_mir::mir::mono::{Instance, StaticDef}; use stable_mir::mir::Operand; use stable_mir::ty::{ - Allocation, ConstantKind, FloatTy, FnDef, GenericArgs, IntTy, MirConst, RigidTy, Size, Span, - Ty, TyConst, TyConstKind, TyKind, UintTy, + Allocation, ConstantKind, FloatTy, FnDef, GenericArgs, IntTy, MirConst, RigidTy, Size, Ty, + TyConst, TyConstKind, TyKind, UintTy, }; use stable_mir::{CrateDef, CrateItem}; use tracing::{debug, trace}; @@ -38,8 +38,10 @@ impl<'tcx> GotocCtx<'tcx> { Operand::Copy(place) | Operand::Move(place) => // TODO: move is an opportunity to poison/nondet the original memory. { - let projection = - unwrap_or_return_codegen_unimplemented!(self, self.codegen_place_stable(place)); + let projection = unwrap_or_return_codegen_unimplemented!( + self, + self.codegen_place_stable(place, Location::none()) + ); // If the operand itself is a Dynamic (like when passing a boxed closure), // we need to pull off the fat pointer. In that case, the rustc kind() on // both the operand and the inner type are Dynamic. @@ -51,7 +53,7 @@ impl<'tcx> GotocCtx<'tcx> { } } Operand::Constant(constant) => { - self.codegen_const(&constant.literal, Some(constant.span)) + self.codegen_const(&constant.literal, self.codegen_span_stable(constant.span)) } } } @@ -62,8 +64,11 @@ impl<'tcx> GotocCtx<'tcx> { span: Option, ) -> Expr { let stable_const = rustc_internal::stable(constant); - let stable_span = rustc_internal::stable(span); - self.codegen_const_ty(&stable_const, stable_span) + if let Some(stable_span) = rustc_internal::stable(span) { + self.codegen_const_ty(&stable_const, self.codegen_span_stable(stable_span)) + } else { + self.codegen_const_ty(&stable_const, Location::none()) + } } /// Generate a goto expression that represents a MIR-level constant. @@ -73,16 +78,16 @@ impl<'tcx> GotocCtx<'tcx> { /// generate code for it as simple literals or constants if possible. Otherwise, we create /// a memory allocation for them and access them indirectly. /// - ZeroSized: These are ZST constants and they just need to match the right type. - pub fn codegen_const(&mut self, constant: &MirConst, span: Option) -> Expr { + pub fn codegen_const(&mut self, constant: &MirConst, loc: Location) -> Expr { trace!(?constant, "codegen_constant"); match constant.kind() { - ConstantKind::Allocated(alloc) => self.codegen_allocation(alloc, constant.ty(), span), + ConstantKind::Allocated(alloc) => self.codegen_allocation(alloc, constant.ty(), loc), ConstantKind::ZeroSized => { let lit_ty = constant.ty(); match lit_ty.kind() { // Rust "function items" (not closures, not function pointers, see `codegen_fndef`) TyKind::RigidTy(RigidTy::FnDef(def, args)) => { - self.codegen_fndef(def, &args, span) + self.codegen_fndef(def, &args, loc) } _ => Expr::init_unit(self.codegen_ty_stable(lit_ty), &self.symbol_table), } @@ -90,7 +95,7 @@ impl<'tcx> GotocCtx<'tcx> { ConstantKind::Param(..) | ConstantKind::Unevaluated(..) => { unreachable!() } - ConstantKind::Ty(t) => self.codegen_const_ty(t, span), + ConstantKind::Ty(t) => self.codegen_const_ty(t, loc), } } @@ -101,19 +106,19 @@ impl<'tcx> GotocCtx<'tcx> { /// generate code for it as simple literals or constants if possible. Otherwise, we create /// a memory allocation for them and access them indirectly. /// - ZeroSized: These are ZST constants and they just need to match the right type. - pub fn codegen_const_ty(&mut self, constant: &TyConst, span: Option) -> Expr { + pub fn codegen_const_ty(&mut self, constant: &TyConst, loc: Location) -> Expr { trace!(?constant, "codegen_constant"); match constant.kind() { TyConstKind::ZSTValue(lit_ty) => { match lit_ty.kind() { // Rust "function items" (not closures, not function pointers, see `codegen_fndef`) TyKind::RigidTy(RigidTy::FnDef(def, args)) => { - self.codegen_fndef(def, &args, span) + self.codegen_fndef(def, &args, loc) } _ => Expr::init_unit(self.codegen_ty_stable(*lit_ty), &self.symbol_table), } } - TyConstKind::Value(ty, alloc) => self.codegen_allocation(alloc, *ty, span), + TyConstKind::Value(ty, alloc) => self.codegen_allocation(alloc, *ty, loc), TyConstKind::Bound(..) => unreachable!(), TyConstKind::Param(..) | TyConstKind::Unevaluated(..) => { unreachable!() @@ -121,11 +126,11 @@ impl<'tcx> GotocCtx<'tcx> { } } - pub fn codegen_allocation(&mut self, alloc: &Allocation, ty: Ty, span: Option) -> Expr { + pub fn codegen_allocation(&mut self, alloc: &Allocation, ty: Ty, loc: Location) -> Expr { // First try to generate the constant without allocating memory. - let expr = self.try_codegen_constant(alloc, ty, span).unwrap_or_else(|| { + let expr = self.try_codegen_constant(alloc, ty, loc).unwrap_or_else(|| { debug!("codegen_allocation try_fail"); - let mem_var = self.codegen_const_allocation(alloc, None); + let mem_var = self.codegen_const_allocation(alloc, None, loc); mem_var .cast_to(Type::unsigned_int(8).to_pointer()) .cast_to(self.codegen_ty_stable(ty).to_pointer()) @@ -143,12 +148,7 @@ impl<'tcx> GotocCtx<'tcx> { /// 3. enums that don't carry data /// 4. unit, tuples (may be multi-ary!), or size-0 arrays /// 5. pointers to an allocation - fn try_codegen_constant( - &mut self, - alloc: &Allocation, - ty: Ty, - span: Option, - ) -> Option { + fn try_codegen_constant(&mut self, alloc: &Allocation, ty: Ty, loc: Location) -> Option { debug!(?alloc, ?ty, "try_codegen_constant"); match ty.kind() { TyKind::RigidTy(RigidTy::Int(it)) => { @@ -194,7 +194,7 @@ impl<'tcx> GotocCtx<'tcx> { } TyKind::RigidTy(RigidTy::RawPtr(inner_ty, _)) | TyKind::RigidTy(RigidTy::Ref(_, inner_ty, _)) => { - Some(self.codegen_const_ptr(alloc, ty, inner_ty, span)) + Some(self.codegen_const_ptr(alloc, ty, inner_ty, loc)) } TyKind::RigidTy(RigidTy::Adt(adt, args)) if adt.kind().is_struct() => { // Structs only have one variant. @@ -221,7 +221,7 @@ impl<'tcx> GotocCtx<'tcx> { &self.symbol_table, )) } else { - self.try_codegen_constant(alloc, *t, span) + self.try_codegen_constant(alloc, *t, loc) } }) .collect::>>()?; @@ -238,7 +238,7 @@ impl<'tcx> GotocCtx<'tcx> { } TyKind::RigidTy(RigidTy::Tuple(tys)) if tys.len() == 1 => { let overall_t = self.codegen_ty_stable(ty); - let inner_expr = self.try_codegen_constant(alloc, tys[0], span)?; + let inner_expr = self.try_codegen_constant(alloc, tys[0], loc)?; Some(inner_expr.transmute_to(overall_t, &self.symbol_table)) } // Everything else we encode as an allocation. @@ -251,7 +251,7 @@ impl<'tcx> GotocCtx<'tcx> { alloc: &Allocation, ty: Ty, inner_ty: Ty, - span: Option, + loc: Location, ) -> Expr { debug!(?ty, ?alloc, "codegen_const_ptr"); if self.use_fat_pointer_stable(inner_ty) { @@ -268,7 +268,7 @@ impl<'tcx> GotocCtx<'tcx> { let GlobalAlloc::Memory(data) = GlobalAlloc::from(alloc_id) else { unreachable!() }; - let mem_var = self.codegen_const_allocation(&data, None); + let mem_var = self.codegen_const_allocation(&data, None, loc); // Extract identifier for static variable. // codegen_allocation_auto_imm_name returns the *address* of @@ -309,7 +309,7 @@ impl<'tcx> GotocCtx<'tcx> { let GlobalAlloc::Memory(data) = GlobalAlloc::from(alloc_id) else { unreachable!() }; - let mem_var = self.codegen_const_allocation(&data, None); + let mem_var = self.codegen_const_allocation(&data, None, loc); let inner_typ = self.codegen_ty_stable(inner_ty); let len = data.bytes.len() / inner_typ.sizeof(&self.symbol_table) as usize; let data_expr = mem_var.cast_to(inner_typ.to_pointer()); @@ -325,7 +325,6 @@ impl<'tcx> GotocCtx<'tcx> { TyKind::RigidTy(RigidTy::Adt(def, _)) if def.name().ends_with("::CStr") => { // TODO: Handle CString // - let loc = self.codegen_span_option_stable(span); let typ = self.codegen_ty_stable(ty); let operation_name = "C string literal"; self.codegen_unimplemented_expr( @@ -343,7 +342,7 @@ impl<'tcx> GotocCtx<'tcx> { let ptr = alloc.provenance.ptrs[0]; let alloc_id = ptr.1.0; let typ = self.codegen_ty_stable(ty); - self.codegen_alloc_pointer(typ, alloc_id, ptr.0, span) + self.codegen_alloc_pointer(typ, alloc_id, ptr.0, loc) } else { // If there's no provenance, just codegen the pointer address. trace!("codegen_const_ptr no_prov"); @@ -359,13 +358,13 @@ impl<'tcx> GotocCtx<'tcx> { res_t: Type, alloc_id: AllocId, offset: Size, - span: Option, + loc: Location, ) -> Expr { debug!(?res_t, ?alloc_id, "codegen_alloc_pointer"); let base_addr = match GlobalAlloc::from(alloc_id) { GlobalAlloc::Function(instance) => { // We want to return the function pointer (not to be confused with function item) - self.codegen_func_expr(instance, span).address_of() + self.codegen_func_expr(instance, loc).address_of() } GlobalAlloc::Static(def) => self.codegen_static_pointer(def), GlobalAlloc::Memory(alloc) => { @@ -373,7 +372,7 @@ impl<'tcx> GotocCtx<'tcx> { // crates do not conflict. The name alone is insufficient because Rust // allows different versions of the same crate to be used. let name = format!("{}::{alloc_id:?}", self.full_crate_name()); - self.codegen_const_allocation(&alloc, Some(name)) + self.codegen_const_allocation(&alloc, Some(name), loc) } alloc @ GlobalAlloc::VTable(..) => { // This is similar to GlobalAlloc::Memory but the type is opaque to rust and it @@ -383,7 +382,7 @@ impl<'tcx> GotocCtx<'tcx> { unreachable!() }; let name = format!("{}::{alloc_id:?}", self.full_crate_name()); - self.codegen_const_allocation(&alloc, Some(name)) + self.codegen_const_allocation(&alloc, Some(name), loc) } }; assert!(res_t.is_pointer() || res_t.is_transparent_type(&self.symbol_table)); @@ -459,12 +458,17 @@ impl<'tcx> GotocCtx<'tcx> { /// /// These constants can be named constants which are declared by the user, or constant values /// used scattered throughout the source - fn codegen_const_allocation(&mut self, alloc: &Allocation, name: Option) -> Expr { + fn codegen_const_allocation( + &mut self, + alloc: &Allocation, + name: Option, + loc: Location, + ) -> Expr { debug!(?name, "codegen_const_allocation"); let alloc_name = match self.alloc_map.get(alloc) { None => { let alloc_name = if let Some(name) = name { name } else { self.next_global_name() }; - self.codegen_alloc_in_memory(alloc.clone(), alloc_name.clone()); + self.codegen_alloc_in_memory(alloc.clone(), alloc_name.clone(), loc); alloc_name } Some(name) => name.clone(), @@ -479,7 +483,7 @@ impl<'tcx> GotocCtx<'tcx> { /// /// This function is ultimately responsible for creating new statically initialized global variables /// in our goto binaries. - pub fn codegen_alloc_in_memory(&mut self, alloc: Allocation, name: String) { + pub fn codegen_alloc_in_memory(&mut self, alloc: Allocation, name: String, loc: Location) { debug!(?alloc, ?name, "codegen_alloc_in_memory"); let struct_name = &format!("{name}::struct"); @@ -488,7 +492,7 @@ impl<'tcx> GotocCtx<'tcx> { // initializers. For example, for a boolean static variable, the variable will have type // CBool and the initializer will be a single byte (a one-character array) representing the // bit pattern for the boolean value. - let alloc_data = self.codegen_allocation_data(&alloc); + let alloc_data = self.codegen_allocation_data(&alloc, loc); let alloc_typ_ref = self.ensure_struct(struct_name, struct_name, |_, _| { alloc_data .iter() @@ -511,7 +515,7 @@ impl<'tcx> GotocCtx<'tcx> { &name, false, //TODO is this correct? alloc_typ_ref.clone(), - Location::none(), + loc, |_, _| None, ); let var_typ = var.typ().clone(); @@ -538,13 +542,13 @@ impl<'tcx> GotocCtx<'tcx> { &self.symbol_table, ); let fn_name = Self::initializer_fn_name(&name); - let temp_var = self.gen_function_local_variable(0, &fn_name, alloc_typ_ref).to_expr(); + let temp_var = self.gen_function_local_variable(0, &fn_name, alloc_typ_ref, loc).to_expr(); let body = Stmt::block( vec![ - Stmt::decl(temp_var.clone(), Some(val), Location::none()), - var.assign(temp_var.transmute_to(var_typ, &self.symbol_table), Location::none()), + Stmt::decl(temp_var.clone(), Some(val), loc), + var.assign(temp_var.transmute_to(var_typ, &self.symbol_table), loc), ], - Location::none(), + loc, ); self.register_initializer(&name, body); @@ -556,7 +560,11 @@ impl<'tcx> GotocCtx<'tcx> { /// We codegen global statics as their own unique struct types, and this creates a field-by-field /// representation of what those fields should be initialized with. /// (A field is either bytes, or initialized with an expression.) - fn codegen_allocation_data<'a>(&mut self, alloc: &'a Allocation) -> Vec> { + fn codegen_allocation_data<'a>( + &mut self, + alloc: &'a Allocation, + loc: Location, + ) -> Vec> { let mut alloc_vals = Vec::with_capacity(alloc.provenance.ptrs.len() + 1); let pointer_size = self.symbol_table.machine_model().pointer_width_in_bytes(); @@ -571,7 +579,7 @@ impl<'tcx> GotocCtx<'tcx> { Type::signed_int(8).to_pointer(), prov.0, ptr_offset.try_into().unwrap(), - None, + loc, ))); next_offset = offset + pointer_size; @@ -607,9 +615,9 @@ impl<'tcx> GotocCtx<'tcx> { /// function types. /// /// See - pub fn codegen_fndef(&mut self, def: FnDef, args: &GenericArgs, span: Option) -> Expr { + pub fn codegen_fndef(&mut self, def: FnDef, args: &GenericArgs, loc: Location) -> Expr { let instance = Instance::resolve(def, args).unwrap(); - self.codegen_fn_item(instance, span) + self.codegen_fn_item(instance, loc) } /// Ensure that the given instance is in the symbol table, returning the symbol. @@ -633,10 +641,9 @@ impl<'tcx> GotocCtx<'tcx> { /// Note: In general with this `Expr` you should immediately either `.address_of()` or `.call(...)`. /// /// This should not be used where Rust expects a "function item" (See `codegen_fn_item`) - pub fn codegen_func_expr(&mut self, instance: Instance, span: Option) -> Expr { + pub fn codegen_func_expr(&mut self, instance: Instance, loc: Location) -> Expr { let func_symbol = self.codegen_func_symbol(instance); - Expr::symbol_expression(func_symbol.name, func_symbol.typ.clone()) - .with_location(self.codegen_span_option_stable(span)) + Expr::symbol_expression(func_symbol.name, func_symbol.typ.clone()).with_location(loc) } /// Generate a goto expression referencing the singleton value for a MIR "function item". @@ -644,7 +651,7 @@ impl<'tcx> GotocCtx<'tcx> { /// For a given function instance, generate a ZST struct and return a singleton reference to that. /// This is the Rust "function item". See /// This is not the function pointer, for that use `codegen_func_expr`. - fn codegen_fn_item(&mut self, instance: Instance, span: Option) -> Expr { + fn codegen_fn_item(&mut self, instance: Instance, loc: Location) -> Expr { let func_symbol = self.codegen_func_symbol(instance); let mangled_name = func_symbol.name; let fn_item_struct_ty = self.codegen_fndef_type_stable(instance); @@ -654,7 +661,7 @@ impl<'tcx> GotocCtx<'tcx> { &fn_singleton_name, false, fn_item_struct_ty, - self.codegen_span_option_stable(span), + loc, |_, _| None, // zero-sized, so no initialization necessary ) } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs index 771a38ac922d..d0e3fc3f442f 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs @@ -364,22 +364,20 @@ impl<'tcx> GotocCtx<'tcx> { /// a named variable. /// /// Recursively finds the actual FnDef from a pointer or box. - fn codegen_local_fndef(&mut self, ty: Ty) -> Option { + fn codegen_local_fndef(&mut self, ty: Ty, loc: Location) -> Option { match ty.kind() { // A local that is itself a FnDef, like Fn::call_once - TyKind::RigidTy(RigidTy::FnDef(def, args)) => { - Some(self.codegen_fndef(def, &args, None)) - } + TyKind::RigidTy(RigidTy::FnDef(def, args)) => Some(self.codegen_fndef(def, &args, loc)), // A local can be pointer to a FnDef, like Fn::call and Fn::call_mut TyKind::RigidTy(RigidTy::RawPtr(inner, _)) => self - .codegen_local_fndef(inner) + .codegen_local_fndef(inner, loc) .map(|f| if f.can_take_address_of() { f.address_of() } else { f }), // A local can be a boxed function pointer TyKind::RigidTy(RigidTy::Adt(def, args)) if def.is_box() => { let boxed_ty = self.codegen_ty_stable(ty); // The type of `T` for `Box` can be derived from the first definition args. let inner_ty = args.0[0].ty().unwrap(); - self.codegen_local_fndef(*inner_ty) + self.codegen_local_fndef(*inner_ty, loc) .map(|f| self.box_value(f.address_of(), boxed_ty)) } _ => None, @@ -387,10 +385,10 @@ impl<'tcx> GotocCtx<'tcx> { } /// Codegen for a local - pub fn codegen_local(&mut self, l: Local) -> Expr { + pub fn codegen_local(&mut self, l: Local, loc: Location) -> Expr { let local_ty = self.local_ty_stable(l); // Check if the local is a function definition (see comment above) - if let Some(fn_def) = self.codegen_local_fndef(local_ty) { + if let Some(fn_def) = self.codegen_local_fndef(local_ty, loc) { return fn_def; } @@ -408,6 +406,7 @@ impl<'tcx> GotocCtx<'tcx> { &mut self, before: Result, proj: &ProjectionElem, + loc: Location, ) -> Result { let before = before?; trace!(?before, ?proj, "codegen_projection"); @@ -500,7 +499,7 @@ impl<'tcx> GotocCtx<'tcx> { } ProjectionElem::Index(i) => { let base_type = before.mir_typ(); - let idxe = self.codegen_local(*i); + let idxe = self.codegen_local(*i, loc); let typ = match base_type.kind() { TyKind::RigidTy(RigidTy::Array(elemt, _)) | TyKind::RigidTy(RigidTy::Slice(elemt)) => TypeOrVariant::Type(elemt), @@ -651,10 +650,10 @@ impl<'tcx> GotocCtx<'tcx> { /// build the fat pointer from there. /// - For `*(Wrapper)` where `T: Unsized`, the projection's `goto_expr` returns an object, /// and we need to take it's address and build the fat pointer. - pub fn codegen_place_ref_stable(&mut self, place: &Place) -> Expr { + pub fn codegen_place_ref_stable(&mut self, place: &Place, loc: Location) -> Expr { let place_ty = self.place_ty_stable(place); let projection = - unwrap_or_return_codegen_unimplemented!(self, self.codegen_place_stable(place)); + unwrap_or_return_codegen_unimplemented!(self, self.codegen_place_stable(place, loc)); if self.use_thin_pointer_stable(place_ty) { // For ZST objects rustc does not necessarily generate any actual objects. let need_not_be_an_object = self.is_zst_object(&projection.goto_expr); @@ -678,6 +677,7 @@ impl<'tcx> GotocCtx<'tcx> { Expr::statement_expression( vec![decl, assume_non_zero, assume_aligned, cast_to_pointer_type], address_of.typ().clone(), + *loc, ) } else { // Just return the address of the place dereferenced. @@ -711,9 +711,10 @@ impl<'tcx> GotocCtx<'tcx> { pub fn codegen_place_stable( &mut self, place: &Place, + loc: Location, ) -> Result { debug!(?place, "codegen_place"); - let initial_expr = self.codegen_local(place.local); + let initial_expr = self.codegen_local(place.local, loc); let initial_typ = TypeOrVariant::Type(self.local_ty_stable(place.local)); debug!(?initial_typ, ?initial_expr, "codegen_place"); let initial_projection = @@ -721,7 +722,7 @@ impl<'tcx> GotocCtx<'tcx> { let result = place .projection .iter() - .fold(initial_projection, |accum, proj| self.codegen_projection(accum, proj)); + .fold(initial_projection, |accum, proj| self.codegen_projection(accum, proj, loc)); match result { Err(data) => Err(UnimplementedData::new( &data.operation, @@ -738,10 +739,11 @@ impl<'tcx> GotocCtx<'tcx> { &mut self, initial_projection: ProjectedPlace, variant_idx: VariantIdx, + loc: Location, ) -> ProjectedPlace { debug!(?initial_projection, ?variant_idx, "codegen_variant_lvalue"); let downcast = ProjectionElem::Downcast(variant_idx); - self.codegen_projection(Ok(initial_projection), &downcast).unwrap() + self.codegen_projection(Ok(initial_projection), &downcast, loc).unwrap() } // https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.ProjectionElem.html diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index 21185624942c..31590266a7b8 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -73,7 +73,7 @@ impl<'tcx> GotocCtx<'tcx> { ret_type.nondet().as_stmt(loc).with_location(loc), ]; - Expr::statement_expression(body, ret_type).with_location(loc) + Expr::statement_expression(body, ret_type, loc) } else { // Compare data pointer. let res_ty = op.ty(left_typ, right_typ); @@ -167,12 +167,12 @@ impl<'tcx> GotocCtx<'tcx> { op_expr.array_constant(width).with_location(loc) } - fn codegen_rvalue_len(&mut self, p: &Place) -> Expr { + fn codegen_rvalue_len(&mut self, p: &Place, loc: Location) -> Expr { let pt = self.place_ty_stable(p); match pt.kind() { - TyKind::RigidTy(RigidTy::Array(_, sz)) => self.codegen_const_ty(&sz, None), + TyKind::RigidTy(RigidTy::Array(_, sz)) => self.codegen_const_ty(&sz, loc), TyKind::RigidTy(RigidTy::Slice(_)) => { - unwrap_or_return_codegen_unimplemented!(self, self.codegen_place_stable(p)) + unwrap_or_return_codegen_unimplemented!(self, self.codegen_place_stable(p, loc)) .fat_ptr_goto_expr .unwrap() .member("len", &self.symbol_table) @@ -224,6 +224,7 @@ impl<'tcx> GotocCtx<'tcx> { var.member(ARITH_OVERFLOW_RESULT_FIELD, &self.symbol_table).as_stmt(loc), ], ret_type, + loc, ) } @@ -254,7 +255,7 @@ impl<'tcx> GotocCtx<'tcx> { ], &self.symbol_table, ); - Expr::statement_expression(vec![decl, cast.as_stmt(loc)], expected_typ) + Expr::statement_expression(vec![decl, cast.as_stmt(loc)], expected_typ, loc) } /// Generate code for a binary arithmetic operation with UB / overflow checks in place. @@ -371,6 +372,7 @@ impl<'tcx> GotocCtx<'tcx> { Expr::statement_expression( vec![check, result.clone().as_stmt(loc)], result.typ().clone(), + loc, ) } BinOp::AddUnchecked | BinOp::MulUnchecked | BinOp::SubUnchecked => { @@ -384,6 +386,7 @@ impl<'tcx> GotocCtx<'tcx> { Expr::statement_expression( vec![check, result.clone().as_stmt(loc)], result.typ().clone(), + loc, ) } else { result @@ -429,6 +432,7 @@ impl<'tcx> GotocCtx<'tcx> { Expr::statement_expression( vec![bytes_overflow_check, overflow_check, res.as_stmt(loc)], ce1.typ().clone(), + loc, ) } } @@ -566,7 +570,7 @@ impl<'tcx> GotocCtx<'tcx> { // 2- Initialize the members of the temporary variant. let initial_projection = ProjectedPlace::try_from_ty(temp_var.clone(), res_ty, self).unwrap(); - let variant_proj = self.codegen_variant_lvalue(initial_projection, variant_index); + let variant_proj = self.codegen_variant_lvalue(initial_projection, variant_index, loc); let variant_expr = variant_proj.goto_expr.clone(); let layout = self.layout_of_stable(res_ty); let fields = match &layout.variants { @@ -603,7 +607,7 @@ impl<'tcx> GotocCtx<'tcx> { stmts.push(set_discriminant); // 4- Return temporary variable. stmts.push(temp_var.as_stmt(loc)); - Expr::statement_expression(stmts, typ) + Expr::statement_expression(stmts, typ, loc) } fn codegen_rvalue_aggregate( @@ -723,13 +727,14 @@ impl<'tcx> GotocCtx<'tcx> { 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); + let place_ref = self.codegen_place_ref_stable(&p, loc); if self.queries.args().ub_check.contains(&ExtraChecks::PtrToRefCast) { let place_ref_type = place_ref.typ().clone(); match self.codegen_raw_ptr_deref_validity_check(&p, &loc) { Some(ptr_validity_check_expr) => Expr::statement_expression( vec![ptr_validity_check_expr, place_ref.as_stmt(loc)], place_ref_type, + loc, ), None => place_ref, } @@ -737,7 +742,7 @@ impl<'tcx> GotocCtx<'tcx> { place_ref } } - Rvalue::Len(p) => self.codegen_rvalue_len(p), + Rvalue::Len(p) => self.codegen_rvalue_len(p, loc), // Rust has begun distinguishing "ptr -> num" and "num -> ptr" (providence-relevant casts) but we do not yet: // Should we? Tracking ticket: https://github.com/model-checking/kani/issues/1274 Rvalue::Cast( @@ -862,9 +867,11 @@ impl<'tcx> GotocCtx<'tcx> { } }, Rvalue::Discriminant(p) => { - let place = - unwrap_or_return_codegen_unimplemented!(self, self.codegen_place_stable(p)) - .goto_expr; + let place = unwrap_or_return_codegen_unimplemented!( + self, + self.codegen_place_stable(p, loc) + ) + .goto_expr; let pt = self.place_ty_stable(p); self.codegen_get_discriminant(place, pt, res_ty) } @@ -879,7 +886,7 @@ impl<'tcx> GotocCtx<'tcx> { // A CopyForDeref is equivalent to a read from a place at the codegen level. // https://github.com/rust-lang/rust/blob/1673f1450eeaf4a5452e086db0fe2ae274a0144f/compiler/rustc_middle/src/mir/syntax.rs#L1055 Rvalue::CopyForDeref(place) => { - unwrap_or_return_codegen_unimplemented!(self, self.codegen_place_stable(place)) + unwrap_or_return_codegen_unimplemented!(self, self.codegen_place_stable(place, loc)) .goto_expr } } @@ -1130,7 +1137,7 @@ impl<'tcx> GotocCtx<'tcx> { let instance = Instance::resolve(def, &args).unwrap(); // We need to handle this case in a special way because `codegen_operand_stable` compiles FnDefs to dummy structs. // (cf. the function documentation) - self.codegen_func_expr(instance, None).address_of() + self.codegen_func_expr(instance, loc).address_of() } _ => unreachable!(), }, @@ -1141,7 +1148,7 @@ impl<'tcx> GotocCtx<'tcx> { { let instance = Instance::resolve_closure(def, &args, ClosureKind::FnOnce) .expect("failed to normalize and resolve closure during codegen"); - self.codegen_func_expr(instance, None).address_of() + self.codegen_func_expr(instance, loc).address_of() } else { unreachable!("{:?} cannot be cast to a fn ptr", operand) } @@ -1173,7 +1180,7 @@ impl<'tcx> GotocCtx<'tcx> { let src_goto_expr = self.codegen_operand_stable(operand); let src_mir_type = self.operand_ty_stable(operand); let dst_mir_type = t; - self.codegen_unsized_cast(src_goto_expr, src_mir_type, dst_mir_type) + self.codegen_unsized_cast(src_goto_expr, src_mir_type, dst_mir_type, loc) } } } @@ -1191,6 +1198,7 @@ impl<'tcx> GotocCtx<'tcx> { src_goto_expr: Expr, src_mir_type: Ty, dst_mir_type: Ty, + loc: Location, ) -> Expr { // The MIR may include casting that isn't necessary. Detect this early on and return the // expression for the RHS. @@ -1204,7 +1212,7 @@ impl<'tcx> GotocCtx<'tcx> { // Handle the leaf which should always be a pointer. let (ptr_cast_info, ptr_src_expr) = path.pop().unwrap(); - let initial_expr = self.codegen_cast_to_fat_pointer(ptr_src_expr, ptr_cast_info); + let initial_expr = self.codegen_cast_to_fat_pointer(ptr_src_expr, ptr_cast_info, loc); // Iterate from the back of the path initializing each struct that requires the coercion. // This code is required for handling smart pointers. @@ -1370,7 +1378,7 @@ impl<'tcx> GotocCtx<'tcx> { // Check the size are inserting in to the vtable against two sources of // truth: (1) the compile-time rustc sizeof functions, and (2) the CBMC // __CPROVER_OBJECT_SIZE function. - fn check_vtable_size(&mut self, operand_type: Ty, vt_size: Expr) -> Stmt { + fn check_vtable_size(&mut self, operand_type: Ty, vt_size: Expr, loc: Location) -> Stmt { // Check against the size we get from the layout from the what we // get constructing a value of that type let ty = self.codegen_ty_stable(operand_type); @@ -1380,7 +1388,7 @@ impl<'tcx> GotocCtx<'tcx> { // Insert a CBMC-time size check, roughly: // local_temp = nondet(); // assert(__CPROVER_OBJECT_SIZE(&local_temp) == vt_size); - let (temp_var, decl) = self.decl_temp_variable(ty.clone(), None, Location::none()); + let (temp_var, decl) = self.decl_temp_variable(ty.clone(), None, loc); let cbmc_size = if ty.is_empty() { // CBMC errors on passing a pointer to void to __CPROVER_OBJECT_SIZE. // In practice, we have seen this with the Never type, which has size 0: @@ -1396,11 +1404,11 @@ impl<'tcx> GotocCtx<'tcx> { let check = Expr::eq(cbmc_size, vt_size); let assert_msg = format!("Correct CBMC vtable size for {ty:?} (MIR type {:?})", operand_type.kind()); - let size_assert = self.codegen_sanity(check, &assert_msg, Location::none()); - Stmt::block(vec![decl, size_assert], Location::none()) + let size_assert = self.codegen_sanity(check, &assert_msg, loc); + Stmt::block(vec![decl, size_assert], loc) } - fn codegen_vtable(&mut self, src_mir_type: Ty, dst_mir_type: Ty) -> Expr { + fn codegen_vtable(&mut self, src_mir_type: Ty, dst_mir_type: Ty, loc: Location) -> Expr { let trait_type = match dst_mir_type.kind() { // DST is pointer type TyKind::RigidTy(RigidTy::Ref(_, pointee_type, ..)) => pointee_type, @@ -1422,7 +1430,7 @@ impl<'tcx> GotocCtx<'tcx> { vtable_impl_name, true, Type::struct_tag(vtable_name), - Location::none(), + loc, |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() { @@ -1433,7 +1441,7 @@ impl<'tcx> GotocCtx<'tcx> { }; let (vt_size, vt_align) = ctx.codegen_vtable_size_and_align(src_mir_type); - let size_assert = ctx.check_vtable_size(src_mir_type, vt_size.clone()); + let size_assert = ctx.check_vtable_size(src_mir_type, vt_size.clone(), loc); let vtable_fields: Vec = vtable_entries .iter() @@ -1461,8 +1469,8 @@ impl<'tcx> GotocCtx<'tcx> { vtable_fields, &ctx.symbol_table, ); - let body = var.assign(vtable, Location::none()); - let block = Stmt::block(vec![size_assert, body], Location::none()); + let body = var.assign(vtable, loc); + let block = Stmt::block(vec![size_assert, body], loc); Some(block) }, ) @@ -1476,6 +1484,7 @@ impl<'tcx> GotocCtx<'tcx> { &mut self, src_goto_expr: Expr, coerce_info: CoerceUnsizedInfo, + loc: Location, ) -> Expr { assert_ne!(coerce_info.src_ty.kind(), coerce_info.dst_ty.kind()); @@ -1499,7 +1508,7 @@ impl<'tcx> GotocCtx<'tcx> { ) => { // Cast to a slice fat pointer. assert_eq!(src_elt_type, dst_elt_type); - let dst_goto_len = self.codegen_const_ty(&src_elt_count, None); + let dst_goto_len = self.codegen_const_ty(&src_elt_count, loc); let src_pointee_ty = pointee_type_stable(coerce_info.src_ty).unwrap(); let dst_data_expr = if src_pointee_ty.kind().is_array() { src_goto_expr.cast_to(self.codegen_ty_stable(src_elt_type).to_pointer()) @@ -1527,7 +1536,7 @@ impl<'tcx> GotocCtx<'tcx> { (_, TyKind::RigidTy(RigidTy::Dynamic(..))) => { // Generate the data and vtable pointer that will be stored in the fat pointer. let dst_data_expr = src_goto_expr.cast_to(dst_data_type); - let vtable = self.codegen_vtable(metadata_src_type, metadata_dst_type); + let vtable = self.codegen_vtable(metadata_src_type, metadata_dst_type, loc); let vtable_expr = vtable.address_of(); dynamic_fat_ptr(fat_ptr_type, dst_data_expr, vtable_expr, &self.symbol_table) } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs index d06829822274..aadb4fbebed9 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs @@ -26,10 +26,6 @@ impl<'tcx> GotocCtx<'tcx> { ) } - pub fn codegen_span_option_stable(&self, sp: Option) -> Location { - sp.map_or(Location::none(), |span| self.codegen_span_stable(span)) - } - pub fn codegen_caller_span_stable(&self, sp: SpanStable) -> Location { self.codegen_caller_span(&rustc_internal::internal(self.tcx, sp)) } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 1e81cccd0c07..c606ae13d095 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -42,14 +42,14 @@ impl<'tcx> GotocCtx<'tcx> { // where the reference is implicit. unwrap_or_return_codegen_unimplemented_stmt!( self, - self.codegen_place_stable(lhs) + self.codegen_place_stable(lhs, location) ) .goto_expr .assign(self.codegen_rvalue_stable(rhs, location).address_of(), location) } else if rty.kind().is_bool() { unwrap_or_return_codegen_unimplemented_stmt!( self, - self.codegen_place_stable(lhs) + self.codegen_place_stable(lhs, location) ) .goto_expr .assign( @@ -59,7 +59,7 @@ impl<'tcx> GotocCtx<'tcx> { } else { unwrap_or_return_codegen_unimplemented_stmt!( self, - self.codegen_place_stable(lhs) + self.codegen_place_stable(lhs, location) ) .goto_expr .assign(self.codegen_rvalue_stable(rhs, location), location) @@ -70,7 +70,7 @@ impl<'tcx> GotocCtx<'tcx> { let dest_ty = self.place_ty_stable(place); let dest_expr = unwrap_or_return_codegen_unimplemented_stmt!( self, - self.codegen_place_stable(place) + self.codegen_place_stable(place, location) ) .goto_expr; self.codegen_set_discriminant(dest_ty, dest_expr, *variant_index, location) @@ -79,14 +79,14 @@ impl<'tcx> GotocCtx<'tcx> { if self.queries.args().ignore_storage_markers { Stmt::skip(location) } else { - Stmt::decl(self.codegen_local(*var_id), None, location) + Stmt::decl(self.codegen_local(*var_id, location), None, location) } } StatementKind::StorageDead(var_id) => { if self.queries.args().ignore_storage_markers { Stmt::skip(location) } else { - Stmt::dead(self.codegen_local(*var_id), location) + Stmt::dead(self.codegen_local(*var_id, location), location) } } StatementKind::Intrinsic(NonDivergingIntrinsic::CopyNonOverlapping( @@ -156,7 +156,7 @@ impl<'tcx> GotocCtx<'tcx> { let place = Place::from(RETURN_LOCAL); let place_expr = unwrap_or_return_codegen_unimplemented_stmt!( self, - self.codegen_place_stable(&place) + self.codegen_place_stable(&place, loc) ) .goto_expr; assert_eq!(rty, self.place_ty_stable(&place), "Unexpected return type"); @@ -304,9 +304,12 @@ impl<'tcx> GotocCtx<'tcx> { // We ignore assignment for all zero size types Stmt::skip(loc) } else { - unwrap_or_return_codegen_unimplemented_stmt!(self, self.codegen_place_stable(place)) - .goto_expr - .deinit(loc) + unwrap_or_return_codegen_unimplemented_stmt!( + self, + self.codegen_place_stable(place, loc) + ) + .goto_expr + .deinit(loc) } } @@ -338,7 +341,7 @@ impl<'tcx> GotocCtx<'tcx> { } InstanceKind::Shim => { // Since the reference is used right away here, no need to inject a check for pointer validity. - let place_ref = self.codegen_place_ref_stable(place); + let place_ref = self.codegen_place_ref_stable(place, loc); match place_ty.kind() { TyKind::RigidTy(RigidTy::Dynamic(..)) => { // Virtual drop via a vtable lookup. @@ -364,7 +367,7 @@ impl<'tcx> GotocCtx<'tcx> { // Non-virtual, direct drop_in_place call assert!(!matches!(drop_instance.kind, InstanceKind::Virtual { .. })); - let func = self.codegen_func_expr(drop_instance, None); + let func = self.codegen_func_expr(drop_instance, loc); // The only argument should be a self reference let args = vec![place_ref]; @@ -572,7 +575,7 @@ impl<'tcx> GotocCtx<'tcx> { InstanceKind::Item | InstanceKind::Intrinsic | InstanceKind::Shim => { // We need to handle FnDef items in a special way because `codegen_operand` compiles them to dummy structs. // (cf. the function documentation) - let func_exp = self.codegen_func_expr(instance, None); + let func_exp = self.codegen_func_expr(instance, loc); if instance.is_foreign_item() { vec![self.codegen_foreign_call(func_exp, fargs, destination, loc)] } else { @@ -723,9 +726,12 @@ impl<'tcx> GotocCtx<'tcx> { if self.place_ty_stable(place).kind().is_unit() { expr.as_stmt(loc) } else { - unwrap_or_return_codegen_unimplemented_stmt!(self, self.codegen_place_stable(place)) - .goto_expr - .assign(expr, loc) + unwrap_or_return_codegen_unimplemented_stmt!( + self, + self.codegen_place_stable(place, loc) + ) + .goto_expr + .assign(expr, loc) } } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/static_var.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/static_var.rs index 0a0a55ad3dd4..e05da3f7f622 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/static_var.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/static_var.rs @@ -19,7 +19,7 @@ impl<'tcx> GotocCtx<'tcx> { debug!("codegen_static"); let alloc = def.eval_initializer().unwrap(); let symbol_name = Instance::from(def).mangled_name(); - self.codegen_alloc_in_memory(alloc, symbol_name); + self.codegen_alloc_in_memory(alloc, symbol_name, self.codegen_span_stable(def.span())); } /// Mutates the Goto-C symbol table to add a forward-declaration of the static variable. 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 9a81838b7fc2..7d51cff037c6 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs @@ -137,8 +137,14 @@ impl<'tcx> GotocCtx<'tcx> { } // Generate a Symbol Expression representing a function variable from the MIR - pub fn gen_function_local_variable(&mut self, c: u64, fname: &str, t: Type) -> Symbol { - self.gen_stack_variable(c, fname, "var", t, Location::none()) + pub fn gen_function_local_variable( + &mut self, + c: u64, + fname: &str, + t: Type, + loc: Location, + ) -> Symbol { + self.gen_stack_variable(c, fname, "var", t, loc) } /// Given a counter `c` a function name `fname, and a prefix `prefix`, generates a new function local variable @@ -283,13 +289,14 @@ impl<'tcx> GotocCtx<'tcx> { pub fn register_initializer(&mut self, var_name: &str, body: Stmt) -> &Symbol { let fn_name = Self::initializer_fn_name(var_name); let pretty_name = format!("{var_name}::init"); + let loc = *body.location(); self.ensure(&fn_name, |_tcx, _| { Symbol::function( &fn_name, Type::code(vec![], Type::constructor()), - Some(Stmt::block(vec![body], Location::none())), //TODO is this block needed? + Some(Stmt::block(vec![body], loc)), //TODO is this block needed? &pretty_name, - Location::none(), + loc, ) .with_is_file_local(true) }) diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index d66e16b6ce59..fc245fc6f4c9 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -168,7 +168,7 @@ impl GotocHook for Nondet { } else { let pe = unwrap_or_return_codegen_unimplemented_stmt!( gcx, - gcx.codegen_place_stable(assign_to) + gcx.codegen_place_stable(assign_to, loc) ) .goto_expr; Stmt::block( @@ -228,8 +228,10 @@ impl GotocHook for IsAllocated { let ptr = fargs.pop().unwrap().cast_to(Type::void_pointer()); let target = target.unwrap(); let loc = gcx.codegen_caller_span_stable(span); - let ret_place = - unwrap_or_return_codegen_unimplemented_stmt!(gcx, gcx.codegen_place_stable(assign_to)); + let ret_place = unwrap_or_return_codegen_unimplemented_stmt!( + gcx, + gcx.codegen_place_stable(assign_to, loc) + ); let ret_type = ret_place.goto_expr.typ().clone(); Stmt::block( @@ -262,8 +264,10 @@ impl GotocHook for PointerObject { let ptr = fargs.pop().unwrap().cast_to(Type::void_pointer()); let target = target.unwrap(); let loc = gcx.codegen_caller_span_stable(span); - let ret_place = - unwrap_or_return_codegen_unimplemented_stmt!(gcx, gcx.codegen_place_stable(assign_to)); + let ret_place = unwrap_or_return_codegen_unimplemented_stmt!( + gcx, + gcx.codegen_place_stable(assign_to, loc) + ); let ret_type = ret_place.goto_expr.typ().clone(); Stmt::block( @@ -296,8 +300,10 @@ impl GotocHook for PointerOffset { let ptr = fargs.pop().unwrap().cast_to(Type::void_pointer()); let target = target.unwrap(); let loc = gcx.codegen_caller_span_stable(span); - let ret_place = - unwrap_or_return_codegen_unimplemented_stmt!(gcx, gcx.codegen_place_stable(assign_to)); + let ret_place = unwrap_or_return_codegen_unimplemented_stmt!( + gcx, + gcx.codegen_place_stable(assign_to, loc) + ); let ret_type = ret_place.goto_expr.typ().clone(); Stmt::block( @@ -336,7 +342,7 @@ impl GotocHook for RustAlloc { vec![ unwrap_or_return_codegen_unimplemented_stmt!( gcx, - gcx.codegen_place_stable(assign_to) + gcx.codegen_place_stable(assign_to, loc) ) .goto_expr .assign( @@ -396,13 +402,14 @@ impl GotocHook for MemCmp { let is_first_ok = first_var.clone().is_nonnull(); let is_second_ok = second_var.clone().is_nonnull(); let should_skip_pointer_checks = is_count_zero.and(is_first_ok).and(is_second_ok); - let place_expr = - unwrap_or_return_codegen_unimplemented_stmt!(gcx, gcx.codegen_place_stable(assign_to)) - .goto_expr; + let place_expr = unwrap_or_return_codegen_unimplemented_stmt!( + gcx, + gcx.codegen_place_stable(assign_to, loc) + ) + .goto_expr; let rhs = should_skip_pointer_checks.ternary( Expr::int_constant(0, place_expr.typ().clone()), // zero bytes are always equal (as long as pointers are nonnull and aligned) - gcx.codegen_func_expr(instance, Some(span)) - .call(vec![first_var, second_var, count_var]), + gcx.codegen_func_expr(instance, loc).call(vec![first_var, second_var, count_var]), ); let code = place_expr.assign(rhs, loc).with_location(loc); Stmt::block( @@ -447,7 +454,7 @@ impl GotocHook for UntrackedDeref { vec![Stmt::assign( unwrap_or_return_codegen_unimplemented_stmt!( gcx, - gcx.codegen_place_stable(assign_to) + gcx.codegen_place_stable(assign_to, loc) ) .goto_expr, fargs.pop().unwrap().dereference(), diff --git a/kani-driver/src/cbmc_property_renderer.rs b/kani-driver/src/cbmc_property_renderer.rs index 7e4fffe42812..4f32028b5866 100644 --- a/kani-driver/src/cbmc_property_renderer.rs +++ b/kani-driver/src/cbmc_property_renderer.rs @@ -162,7 +162,7 @@ enum CoverageStatus { const UNSUPPORTED_CONSTRUCT_DESC: &str = "is not currently supported by Kani"; const UNWINDING_ASSERT_DESC: &str = "unwinding assertion loop"; const UNWINDING_ASSERT_REC_DESC: &str = "recursion unwinding assertion"; -const DEFAULT_ASSERTION: &str = "assertion"; +const UNDEFINED_FUNCTION_DESC: &str = "undefined function should be unreachable"; impl ParserItem { /// Determines if an item must be skipped or not. @@ -618,8 +618,7 @@ fn modify_undefined_function_checks(mut properties: Vec) -> (Vec Date: Thu, 13 Jun 2024 19:37:39 -0400 Subject: [PATCH 138/225] Add a `#[derive(Invariant)]` macro (#3250) This PR adds a `#[derive(Invariant)]` macro for structs which allows users to automatically derive the `Invariant` implementations for any struct. The derived implementation determines the invariant for the struct as the conjunction of invariants of its fields. In other words, the invariant is derived as `true && self.field1.is_safe() && self.field2.is_safe() && ..`. For example, for the struct ```rs #[derive(kani::Invariant)] struct Point { x: X, y: Y, } ``` we derive the `Invariant` implementation as ```rs impl kani::Invariant for Point { fn is_safe(&self) -> bool { true && self.x.is_safe() && self.y.is_safe() } } ``` Related #3095 --- library/kani_macros/src/derive.rs | 94 ++++++++++++++++++- library/kani_macros/src/lib.rs | 7 ++ .../empty_struct/empty_struct.rs | 37 ++++++++ .../derive-invariant/empty_struct/expected | 8 ++ .../derive-invariant/generic_struct/expected | 2 + .../generic_struct/generic_struct.rs | 20 ++++ .../derive-invariant/invariant_fail/expected | 4 + .../invariant_fail/invariant_fail.rs | 33 +++++++ .../derive-invariant/named_struct/expected | 2 + .../named_struct/named_struct.rs | 20 ++++ .../derive-invariant/unnamed_struct/expected | 2 + .../unnamed_struct/unnamed_struct.rs | 17 ++++ 12 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 tests/expected/derive-invariant/empty_struct/empty_struct.rs create mode 100644 tests/expected/derive-invariant/empty_struct/expected create mode 100644 tests/expected/derive-invariant/generic_struct/expected create mode 100644 tests/expected/derive-invariant/generic_struct/generic_struct.rs create mode 100644 tests/expected/derive-invariant/invariant_fail/expected create mode 100644 tests/expected/derive-invariant/invariant_fail/invariant_fail.rs create mode 100644 tests/expected/derive-invariant/named_struct/expected create mode 100644 tests/expected/derive-invariant/named_struct/named_struct.rs create mode 100644 tests/expected/derive-invariant/unnamed_struct/expected create mode 100644 tests/expected/derive-invariant/unnamed_struct/unnamed_struct.rs diff --git a/library/kani_macros/src/derive.rs b/library/kani_macros/src/derive.rs index 7e3dee390330..4e99590fc6a3 100644 --- a/library/kani_macros/src/derive.rs +++ b/library/kani_macros/src/derive.rs @@ -23,7 +23,7 @@ pub fn expand_derive_arbitrary(item: proc_macro::TokenStream) -> proc_macro::Tok let item_name = &derive_item.ident; // Add a bound `T: Arbitrary` to every type parameter T. - let generics = add_trait_bound(derive_item.generics); + let generics = add_trait_bound_arbitrary(derive_item.generics); // Generate an expression to sum up the heap size of each field. let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); @@ -40,7 +40,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(mut generics: Generics) -> Generics { +fn add_trait_bound_arbitrary(mut generics: Generics) -> Generics { generics.params.iter_mut().for_each(|param| { if let GenericParam::Type(type_param) = param { type_param.bounds.push(parse_quote!(kani::Arbitrary)); @@ -165,3 +165,93 @@ fn fn_any_enum(ident: &Ident, data: &DataEnum) -> TokenStream { } } } + +pub fn expand_derive_invariant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let derive_item = parse_macro_input!(item as DeriveInput); + let item_name = &derive_item.ident; + + // Add a bound `T: Invariant` to every type parameter T. + let generics = add_trait_bound_invariant(derive_item.generics); + // Generate an expression to sum up the heap size of each field. + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let body = is_safe_body(&item_name, &derive_item.data); + let expanded = quote! { + // The generated implementation. + impl #impl_generics kani::Invariant for #item_name #ty_generics #where_clause { + fn is_safe(&self) -> bool { + #body + } + } + }; + proc_macro::TokenStream::from(expanded) +} + +/// Add a bound `T: Invariant` to every type parameter T. +fn add_trait_bound_invariant(mut generics: Generics) -> Generics { + generics.params.iter_mut().for_each(|param| { + if let GenericParam::Type(type_param) = param { + type_param.bounds.push(parse_quote!(kani::Invariant)); + } + }); + generics +} + +fn is_safe_body(ident: &Ident, data: &Data) -> TokenStream { + match data { + Data::Struct(struct_data) => struct_safe_conjunction(ident, &struct_data.fields), + Data::Enum(_) => { + abort!(Span::call_site(), "Cannot derive `Invariant` for `{}` enum", ident; + note = ident.span() => + "`#[derive(Invariant)]` cannot be used for enums such as `{}`", ident + ) + } + Data::Union(_) => { + abort!(Span::call_site(), "Cannot derive `Invariant` for `{}` union", ident; + note = ident.span() => + "`#[derive(Invariant)]` cannot be used for unions such as `{}`", ident + ) + } + } +} + +/// Generates an expression that is the conjunction of `is_safe` calls for each field in the struct. +fn struct_safe_conjunction(_ident: &Ident, fields: &Fields) -> TokenStream { + match fields { + // Expands to the expression + // `true && self.field1.is_safe() && self.field2.is_safe() && ..` + Fields::Named(ref fields) => { + let safe_calls = fields.named.iter().map(|field| { + let name = &field.ident; + quote_spanned! {field.span()=> + self.#name.is_safe() + } + }); + // An initial value is required for empty structs + safe_calls.fold(quote! { true }, |acc, call| { + quote! { #acc && #call } + }) + } + Fields::Unnamed(ref fields) => { + // Expands to the expression + // `true && self.0.is_safe() && self.1.is_safe() && ..` + let safe_calls = fields.unnamed.iter().enumerate().map(|(i, field)| { + let idx = syn::Index::from(i); + quote_spanned! {field.span()=> + self.#idx.is_safe() + } + }); + // An initial value is required for empty structs + safe_calls.fold(quote! { true }, |acc, call| { + quote! { #acc && #call } + }) + } + // Expands to the expression + // `true` + Fields::Unit => { + quote! { + true + } + } + } +} diff --git a/library/kani_macros/src/lib.rs b/library/kani_macros/src/lib.rs index 0a000910174a..b10b8a74cdc5 100644 --- a/library/kani_macros/src/lib.rs +++ b/library/kani_macros/src/lib.rs @@ -107,6 +107,13 @@ pub fn derive_arbitrary(item: TokenStream) -> TokenStream { derive::expand_derive_arbitrary(item) } +/// Allow users to auto generate Invariant implementations by using `#[derive(Invariant)]` macro. +#[proc_macro_error] +#[proc_macro_derive(Invariant)] +pub fn derive_invariant(item: TokenStream) -> TokenStream { + derive::expand_derive_invariant(item) +} + /// Add a precondition to this function. /// /// This is part of the function contract API, for more general information see diff --git a/tests/expected/derive-invariant/empty_struct/empty_struct.rs b/tests/expected/derive-invariant/empty_struct/empty_struct.rs new file mode 100644 index 000000000000..4c931ce8aedc --- /dev/null +++ b/tests/expected/derive-invariant/empty_struct/empty_struct.rs @@ -0,0 +1,37 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that Kani can automatically derive `Invariant` for empty structs. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct Void; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct Void2(()); + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct VoidOfVoid(Void, Void2); + +#[kani::proof] +fn check_empty_struct_invariant_1() { + let void1: Void = kani::any(); + assert!(void1.is_safe()); +} + +#[kani::proof] +fn check_empty_struct_invariant_2() { + let void2: Void2 = kani::any(); + assert!(void2.is_safe()); +} + +#[kani::proof] +fn check_empty_struct_invariant_3() { + let void3: VoidOfVoid = kani::any(); + assert!(void3.is_safe()); +} diff --git a/tests/expected/derive-invariant/empty_struct/expected b/tests/expected/derive-invariant/empty_struct/expected new file mode 100644 index 000000000000..8fdca72b1ead --- /dev/null +++ b/tests/expected/derive-invariant/empty_struct/expected @@ -0,0 +1,8 @@ + - Status: SUCCESS\ + - Description: "assertion failed: void1.is_safe()" + + - Status: SUCCESS\ + - Description: "assertion failed: void2.is_safe()" + + - Status: SUCCESS\ + - Description: "assertion failed: void3.is_safe()" diff --git a/tests/expected/derive-invariant/generic_struct/expected b/tests/expected/derive-invariant/generic_struct/expected new file mode 100644 index 000000000000..5e5886bb3e45 --- /dev/null +++ b/tests/expected/derive-invariant/generic_struct/expected @@ -0,0 +1,2 @@ + - Status: SUCCESS\ + - Description: "assertion failed: point.is_safe()" diff --git a/tests/expected/derive-invariant/generic_struct/generic_struct.rs b/tests/expected/derive-invariant/generic_struct/generic_struct.rs new file mode 100644 index 000000000000..91c62fac8ece --- /dev/null +++ b/tests/expected/derive-invariant/generic_struct/generic_struct.rs @@ -0,0 +1,20 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that Kani can automatically derive `Invariant` for structs with generics. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct Point { + x: X, + y: Y, +} + +#[kani::proof] +fn check_generic_struct_invariant() { + let point: Point = kani::any(); + assert!(point.is_safe()); +} diff --git a/tests/expected/derive-invariant/invariant_fail/expected b/tests/expected/derive-invariant/invariant_fail/expected new file mode 100644 index 000000000000..511d5901e154 --- /dev/null +++ b/tests/expected/derive-invariant/invariant_fail/expected @@ -0,0 +1,4 @@ + - Status: FAILURE\ + - Description: "assertion failed: wrapper.is_safe()" + +Verification failed for - check_invariant_fail diff --git a/tests/expected/derive-invariant/invariant_fail/invariant_fail.rs b/tests/expected/derive-invariant/invariant_fail/invariant_fail.rs new file mode 100644 index 000000000000..b1d6f8679835 --- /dev/null +++ b/tests/expected/derive-invariant/invariant_fail/invariant_fail.rs @@ -0,0 +1,33 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that a verification failure is triggered when the derived `Invariant` +//! method is checked but not satisfied. + +extern crate kani; +use kani::Invariant; +// Note: This represents an incorrect usage of `Arbitrary` and `Invariant`. +// +// The `Arbitrary` implementation should respect the type invariant, +// but Kani does not enforce this in any way at the moment. +// +#[derive(kani::Arbitrary)] +struct NotNegative(i32); + +impl kani::Invariant for NotNegative { + fn is_safe(&self) -> bool { + self.0 >= 0 + } +} + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct NotNegativeWrapper { + x: NotNegative, +} + +#[kani::proof] +fn check_invariant_fail() { + let wrapper: NotNegativeWrapper = kani::any(); + assert!(wrapper.is_safe()); +} diff --git a/tests/expected/derive-invariant/named_struct/expected b/tests/expected/derive-invariant/named_struct/expected new file mode 100644 index 000000000000..5e5886bb3e45 --- /dev/null +++ b/tests/expected/derive-invariant/named_struct/expected @@ -0,0 +1,2 @@ + - Status: SUCCESS\ + - Description: "assertion failed: point.is_safe()" diff --git a/tests/expected/derive-invariant/named_struct/named_struct.rs b/tests/expected/derive-invariant/named_struct/named_struct.rs new file mode 100644 index 000000000000..7e27404bda11 --- /dev/null +++ b/tests/expected/derive-invariant/named_struct/named_struct.rs @@ -0,0 +1,20 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that Kani can automatically derive `Invariant` for structs with named fields. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct Point { + x: i32, + y: i32, +} + +#[kani::proof] +fn check_generic_struct_invariant() { + let point: Point = kani::any(); + assert!(point.is_safe()); +} diff --git a/tests/expected/derive-invariant/unnamed_struct/expected b/tests/expected/derive-invariant/unnamed_struct/expected new file mode 100644 index 000000000000..5e5886bb3e45 --- /dev/null +++ b/tests/expected/derive-invariant/unnamed_struct/expected @@ -0,0 +1,2 @@ + - Status: SUCCESS\ + - Description: "assertion failed: point.is_safe()" diff --git a/tests/expected/derive-invariant/unnamed_struct/unnamed_struct.rs b/tests/expected/derive-invariant/unnamed_struct/unnamed_struct.rs new file mode 100644 index 000000000000..5dee718d05a6 --- /dev/null +++ b/tests/expected/derive-invariant/unnamed_struct/unnamed_struct.rs @@ -0,0 +1,17 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that Kani can automatically derive `Invariant` for structs with unnamed fields. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct Point(i32, i32); + +#[kani::proof] +fn check_generic_struct_invariant() { + let point: Point = kani::any(); + assert!(point.is_safe()); +} From 9ee1d296de22f363b399df7a38d56c5796bfed1b Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Fri, 14 Jun 2024 20:07:11 +0200 Subject: [PATCH 139/225] Use GitHub's M1 macOS runner (#3266) An M1 (aarch64, Apple Silicon) runner is available on the free plan since January 2024 (see https://github.blog/changelog/2024-01-30-github-actions-introducing-the-new-m1-macos-runner-available-to-open-source/). --- .github/workflows/kani-m1.yml | 27 --------------------- .github/workflows/kani.yml | 2 +- .github/workflows/release.yml | 26 ++++++++++++++++++++ scripts/setup/{macos-13-xlarge => macos-14} | 0 4 files changed, 27 insertions(+), 28 deletions(-) delete mode 100644 .github/workflows/kani-m1.yml rename scripts/setup/{macos-13-xlarge => macos-14} (100%) diff --git a/.github/workflows/kani-m1.yml b/.github/workflows/kani-m1.yml deleted file mode 100644 index 0f884f2fe013..000000000000 --- a/.github/workflows/kani-m1.yml +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright Kani Contributors -# SPDX-License-Identifier: Apache-2.0 OR MIT - -# Run the regression job on Apple M1 only on commits to `main` -name: Kani CI M1 -on: - push: - branches: - - 'main' - -env: - RUST_BACKTRACE: 1 - -jobs: - regression: - runs-on: macos-13-xlarge - steps: - - name: Checkout Kani - uses: actions/checkout@v4 - - - name: Setup Kani Dependencies - uses: ./.github/actions/setup - with: - os: macos-13-xlarge - - - name: Execute Kani regression - run: ./scripts/kani-regression.sh diff --git a/.github/workflows/kani.yml b/.github/workflows/kani.yml index d98e0a2b0a5b..dd077eff25e1 100644 --- a/.github/workflows/kani.yml +++ b/.github/workflows/kani.yml @@ -17,7 +17,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-13, ubuntu-20.04, ubuntu-22.04] + os: [macos-13, ubuntu-20.04, ubuntu-22.04, macos-14] steps: - name: Checkout Kani uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4ae93c291484..75bafefd6799 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,6 +44,32 @@ jobs: os: macos-13 arch: x86_64-apple-darwin + build_bundle_macos_aarch64: + name: BuildBundle-MacOs-ARM + runs-on: macos-14 + permissions: + contents: write + outputs: + version: ${{ steps.bundle.outputs.version }} + bundle: ${{ steps.bundle.outputs.bundle }} + package: ${{ steps.bundle.outputs.package }} + crate_version: ${{ steps.bundle.outputs.crate_version }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Kani Dependencies + uses: ./.github/actions/setup + with: + os: macos-14 + + - name: Build bundle + id: bundle + uses: ./.github/actions/build-bundle + with: + os: macos-14 + arch: aarch64-apple-darwin + build_bundle_linux: name: BuildBundle-Linux runs-on: ubuntu-20.04 diff --git a/scripts/setup/macos-13-xlarge b/scripts/setup/macos-14 similarity index 100% rename from scripts/setup/macos-13-xlarge rename to scripts/setup/macos-14 From 8c7a25a88b8b5a45ed1ace40cc39400f32ec6cb0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 11:38:37 -0400 Subject: [PATCH 140/225] Automatic cargo update to 2024-06-17 (#3267) Dependency upgrade resulting from `cargo update`. Co-authored-by: tautschnig <1144736+tautschnig@users.noreply.github.com> --- Cargo.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7660ff5c83ec..8de76caa816d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,9 +140,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.6" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -150,9 +150,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.6" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", @@ -551,9 +551,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memuse" @@ -801,9 +801,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags", ] From c8a1e73ff685ac2eadb7e75274af56b6cd50153c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 15:10:12 -0400 Subject: [PATCH 141/225] Bump docker/build-push-action from 5 to 6 (#3268) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
Release notes

Sourced from docker/build-push-action's releases.

v6.0.0

[!NOTE] This major release adds support for generating Build summary and exporting build record for your build. You can disable this feature by setting DOCKER_BUILD_NO_SUMMARY: true environment variable in your workflow.

Full Changelog: https://github.com/docker/build-push-action/compare/v5.4.0...v6.0.0

v5.4.0

Full Changelog: https://github.com/docker/build-push-action/compare/v5.3.0...v5.4.0

v5.3.0

Full Changelog: https://github.com/docker/build-push-action/compare/v5.2.0...v5.3.0

v5.2.0

Full Changelog: https://github.com/docker/build-push-action/compare/v5.1.0...v5.2.0

v5.1.0

Full Changelog: https://github.com/docker/build-push-action/compare/v5.0.0...v5.1.0

Commits
  • c382f71 Merge pull request #1120 from crazy-max/build-summary
  • 5a5b70d chore: update generated content
  • dc24cf9 don't generate summary for cloud driver
  • 667cb22 DOCKER_BUILD_NO_SUMMARY env to disable summary
  • d880b19 generate build summary
  • e51051a export build record and upload artifact
  • 86c2bd0 Merge pull request #1137 from docker/dependabot/npm_and_yarn/braces-3.0.3
  • 268d2b1 Merge pull request #1138 from docker/dependabot/npm_and_yarn/docker/actions-t...
  • 2b8dc7f chore: update generated content
  • 840c12b chore(deps): Bump @​docker/actions-toolkit from 0.25.1 to 0.26.0
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=docker/build-push-action&package-manager=github_actions&previous-version=5&new-version=6)](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 75bafefd6799..fc8728eb308e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -410,7 +410,7 @@ jobs: OWNER: '${{ github.repository_owner }}' - name: Build and push - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: scripts/ci/Dockerfile.bundle-release-20-04 From 44f1878280577f5d37e2822d1a7cb0bcc80f0ca4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 19:49:19 +0000 Subject: [PATCH 142/225] Bump tests/perf/s2n-quic from `59ef366` to `7495f7b` (#3269) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `59ef366` to `7495f7b`.
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 59ef366af76e..7495f7b43c27 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 59ef366af76edfb4f89bd39137865db2a1ad041d +Subproject commit 7495f7b43c271ebf3d467409c43fc3cc864bdcc6 From fd013ff350a47b63f8bcbdcc2918ca625aebfea2 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Mon, 17 Jun 2024 16:02:21 -0700 Subject: [PATCH 143/225] Upgrade Rust toolchain to nightly-2024-06-17 (#3271) Related changes: - https://github.com/rust-lang/rust/pull/125910: Introduces a new constant propagation pass which broke Kani coverage tests. For now, disable this pass if coverage is enabled. - https://github.com/rust-lang/rust/pull/126410: Rename ConstOperands Resolves #3260 --- .../src/codegen_cprover_gotoc/codegen/operand.rs | 2 +- kani-compiler/src/kani_middle/reachability.rs | 8 ++++---- kani-compiler/src/kani_middle/stubbing/mod.rs | 6 +++--- kani-compiler/src/kani_middle/transform/body.rs | 4 ++-- .../src/kani_middle/transform/check_values.rs | 6 +++--- kani-compiler/src/kani_middle/transform/contracts.rs | 4 ++-- .../src/kani_middle/transform/kani_intrinsics.rs | 10 +++++----- kani-compiler/src/kani_middle/transform/stubs.rs | 8 ++++---- kani-driver/src/call_single_file.rs | 4 ++++ rust-toolchain.toml | 4 ++-- scripts/kani-regression.sh | 11 +++++++++-- 11 files changed, 39 insertions(+), 28 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs index 4c45b475a3a7..555fc43b8999 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs @@ -53,7 +53,7 @@ impl<'tcx> GotocCtx<'tcx> { } } Operand::Constant(constant) => { - self.codegen_const(&constant.literal, self.codegen_span_stable(constant.span)) + self.codegen_const(&constant.const_, self.codegen_span_stable(constant.span)) } } } diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 4ebff6868bf5..279dcf8cc107 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -26,7 +26,7 @@ use rustc_smir::rustc_internal; use stable_mir::mir::alloc::{AllocId, GlobalAlloc}; use stable_mir::mir::mono::{Instance, InstanceKind, MonoItem, StaticDef}; use stable_mir::mir::{ - visit::Location, Body, CastKind, Constant, MirVisitor, PointerCoercion, Rvalue, Terminator, + visit::Location, Body, CastKind, ConstOperand, MirVisitor, PointerCoercion, Rvalue, Terminator, TerminatorKind, }; use stable_mir::ty::{Allocation, ClosureKind, ConstantKind, RigidTy, Ty, TyKind}; @@ -375,9 +375,9 @@ impl<'a, 'tcx> MirVisitor for MonoItemsFnCollector<'a, 'tcx> { } /// Collect constants that are represented as static variables. - fn visit_constant(&mut self, constant: &Constant, location: Location) { - debug!(?constant, ?location, literal=?constant.literal, "visit_constant"); - let allocation = match constant.literal.kind() { + fn visit_const_operand(&mut self, constant: &ConstOperand, location: Location) { + debug!(?constant, ?location, literal=?constant.const_, "visit_constant"); + let allocation = match constant.const_.kind() { ConstantKind::Allocated(allocation) => allocation, ConstantKind::Unevaluated(_) => { unreachable!("Instance with polymorphic constant: `{constant:?}`") diff --git a/kani-compiler/src/kani_middle/stubbing/mod.rs b/kani-compiler/src/kani_middle/stubbing/mod.rs index f21a02b7681e..f145a4d105e2 100644 --- a/kani-compiler/src/kani_middle/stubbing/mod.rs +++ b/kani-compiler/src/kani_middle/stubbing/mod.rs @@ -16,7 +16,7 @@ use rustc_middle::ty::{self, EarlyBinder, ParamEnv, TyCtxt, TypeFoldable}; use rustc_smir::rustc_internal; use stable_mir::mir::mono::Instance; use stable_mir::mir::visit::{Location, MirVisitor}; -use stable_mir::mir::Constant; +use stable_mir::mir::ConstOperand; use stable_mir::ty::{FnDef, RigidTy, TyKind}; use stable_mir::{CrateDef, CrateItem}; @@ -185,8 +185,8 @@ impl<'tcx> StubConstChecker<'tcx> { impl<'tcx> MirVisitor for StubConstChecker<'tcx> { /// Collect constants that are represented as static variables. - fn visit_constant(&mut self, constant: &Constant, location: Location) { - let const_ = self.monomorphize(rustc_internal::internal(self.tcx, &constant.literal)); + fn visit_const_operand(&mut self, constant: &ConstOperand, location: Location) { + let const_ = self.monomorphize(rustc_internal::internal(self.tcx, &constant.const_)); debug!(?constant, ?location, ?const_, "visit_constant"); match const_ { Const::Val(..) | Const::Ty(..) => {} diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index e058c5aedc98..2bf424a1332d 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -81,12 +81,12 @@ impl MutableBody { pub fn new_str_operand(&mut self, msg: &str, span: Span) -> Operand { let literal = MirConst::from_str(msg); - Operand::Constant(Constant { span, user_ty: None, literal }) + Operand::Constant(ConstOperand { span, user_ty: None, const_: literal }) } pub fn new_const_operand(&mut self, val: u128, uint_ty: UintTy, span: Span) -> Operand { let literal = MirConst::try_from_uint(val, uint_ty).unwrap(); - Operand::Constant(Constant { span, user_ty: None, literal }) + Operand::Constant(ConstOperand { span, user_ty: None, const_: literal }) } /// Create a raw pointer of `*mut type` and return a new local where that value is stored. diff --git a/kani-compiler/src/kani_middle/transform/check_values.rs b/kani-compiler/src/kani_middle/transform/check_values.rs index c7c9548bd609..4ecb16fab023 100644 --- a/kani-compiler/src/kani_middle/transform/check_values.rs +++ b/kani-compiler/src/kani_middle/transform/check_values.rs @@ -24,7 +24,7 @@ use stable_mir::abi::{FieldsShape, Scalar, TagEncoding, ValueAbi, VariantsShape, use stable_mir::mir::mono::{Instance, InstanceKind}; use stable_mir::mir::visit::{Location, PlaceContext, PlaceRef}; use stable_mir::mir::{ - AggregateKind, BasicBlockIdx, BinOp, Body, CastKind, Constant, FieldIdx, Local, LocalDecl, + AggregateKind, BasicBlockIdx, BinOp, Body, CastKind, ConstOperand, FieldIdx, Local, LocalDecl, MirVisitor, Mutability, NonDivergingIntrinsic, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; @@ -118,8 +118,8 @@ impl ValidValuePass { reason: &str, ) { let span = source.span(body.blocks()); - let rvalue = Rvalue::Use(Operand::Constant(Constant { - literal: MirConst::from_bool(false), + let rvalue = Rvalue::Use(Operand::Constant(ConstOperand { + const_: MirConst::from_bool(false), span, user_ty: None, })); diff --git a/kani-compiler/src/kani_middle/transform/contracts.rs b/kani-compiler/src/kani_middle/transform/contracts.rs index 10fa6775a0d9..f5760bd4d829 100644 --- a/kani-compiler/src/kani_middle/transform/contracts.rs +++ b/kani-compiler/src/kani_middle/transform/contracts.rs @@ -10,7 +10,7 @@ use cbmc::{InternString, InternedString}; use rustc_middle::ty::TyCtxt; use rustc_smir::rustc_internal; use stable_mir::mir::mono::Instance; -use stable_mir::mir::{Body, Constant, Operand, TerminatorKind}; +use stable_mir::mir::{Body, ConstOperand, Operand, TerminatorKind}; use stable_mir::ty::{FnDef, MirConst, RigidTy, TyKind}; use stable_mir::{CrateDef, DefId}; use std::collections::HashSet; @@ -113,7 +113,7 @@ impl AnyModifiesPass { let instance = Instance::resolve(self.kani_any.unwrap(), &instance_args).unwrap(); let literal = MirConst::try_new_zero_sized(instance.ty()).unwrap(); let span = bb.terminator.span; - let new_func = Constant { span, user_ty: None, literal }; + let new_func = ConstOperand { span, user_ty: None, const_: literal }; *func = Operand::Constant(new_func); changed = true; } diff --git a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs index 053b210babb0..53bbd52086ac 100644 --- a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs +++ b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs @@ -15,7 +15,7 @@ use crate::kani_queries::QueryDb; use rustc_middle::ty::TyCtxt; use stable_mir::mir::mono::Instance; use stable_mir::mir::{ - BinOp, Body, Constant, Operand, Place, Rvalue, Statement, StatementKind, RETURN_LOCAL, + BinOp, Body, ConstOperand, Operand, Place, Rvalue, Statement, StatementKind, RETURN_LOCAL, }; use stable_mir::target::MachineInfo; use stable_mir::ty::{MirConst, RigidTy, TyKind}; @@ -79,10 +79,10 @@ impl IntrinsicGeneratorPass { let span = new_body.locals()[ret_var].span; let assign = StatementKind::Assign( Place::from(ret_var), - Rvalue::Use(Operand::Constant(Constant { + Rvalue::Use(Operand::Constant(ConstOperand { span, user_ty: None, - literal: MirConst::from_bool(true), + const_: MirConst::from_bool(true), })), ); let stmt = Statement { kind: assign, span }; @@ -115,8 +115,8 @@ impl IntrinsicGeneratorPass { } Err(msg) => { // We failed to retrieve all the valid ranges. - let rvalue = Rvalue::Use(Operand::Constant(Constant { - literal: MirConst::from_bool(false), + let rvalue = Rvalue::Use(Operand::Constant(ConstOperand { + const_: MirConst::from_bool(false), span, user_ty: None, })); diff --git a/kani-compiler/src/kani_middle/transform/stubs.rs b/kani-compiler/src/kani_middle/transform/stubs.rs index 53db34dc902f..4f249afdd45a 100644 --- a/kani-compiler/src/kani_middle/transform/stubs.rs +++ b/kani-compiler/src/kani_middle/transform/stubs.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::TyCtxt; use rustc_smir::rustc_internal; use stable_mir::mir::mono::Instance; use stable_mir::mir::visit::{Location, MirVisitor}; -use stable_mir::mir::{Body, Constant, LocalDecl, Operand, Terminator, TerminatorKind}; +use stable_mir::mir::{Body, ConstOperand, LocalDecl, Operand, Terminator, TerminatorKind}; use stable_mir::ty::{FnDef, MirConst, RigidTy, TyKind}; use stable_mir::CrateDef; use std::collections::HashMap; @@ -199,7 +199,7 @@ impl<'a> MutMirVisitor for ExternFnStubVisitor<'a> { 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 = Constant { span, user_ty: None, literal }; + let new_func = ConstOperand { span, user_ty: None, const_: literal }; *func = Operand::Constant(new_func); self.changed = true; } @@ -212,12 +212,12 @@ impl<'a> MutMirVisitor for ExternFnStubVisitor<'a> { 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(Constant { span, .. }) = operand else { + 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 = Constant { span: *span, user_ty: None, literal }; + 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/call_single_file.rs b/kani-driver/src/call_single_file.rs index 29128a02dc2a..7992543cdaa1 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -184,6 +184,10 @@ impl KaniSession { } } + if self.args.coverage { + flags.push("-Zmir-enable-passes=-SingleUseConsts".into()); + } + // This argument will select the Kani flavour of the compiler. It will be removed before // rustc driver is invoked. flags.push("--kani-compiler".into()); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index d1baa70550de..eeabc79dd980 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-06-11" -components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] +channel = "nightly-2024-06-17" +components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] diff --git a/scripts/kani-regression.sh b/scripts/kani-regression.sh index d3bb0f7efb45..b1de293d533c 100755 --- a/scripts/kani-regression.sh +++ b/scripts/kani-regression.sh @@ -33,8 +33,8 @@ check_kissat_version.sh # Formatting check ${SCRIPT_DIR}/kani-fmt.sh --check -# Build all packages in the workspace and ensure no warning is emitted. -RUSTFLAGS="-D warnings" cargo build-dev +# Build kani +cargo build-dev # Unit tests cargo test -p cprover_bindings @@ -102,6 +102,13 @@ FEATURES_MANIFEST_PATH="$KANI_DIR/tests/cargo-kani/cargo-features-flag/Cargo.tom cargo kani --manifest-path "$FEATURES_MANIFEST_PATH" --harness trivial_success cargo clean --manifest-path "$FEATURES_MANIFEST_PATH" +# Build all packages in the workspace and ensure no warning is emitted. +# Please don't replace `cargo build-dev` above with this command. +# 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 + echo echo "All Kani regression tests completed successfully." echo From bfb497a2e272f0215d17e9fa531d71e3668af144 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 09:48:59 +0200 Subject: [PATCH 144/225] Automatic toolchain upgrade to nightly-2024-06-18 (#3272) Update Rust toolchain from nightly-2024-06-17 to nightly-2024-06-18 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/d7f6ebacee13b6c03623c4b74197280454ede8de up to https://github.com/rust-lang/rust/commit/59e2c01c2217a01546222e4d9ff4e6695ee8a1db. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index eeabc79dd980..ce876507640a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-06-17" +channel = "nightly-2024-06-18" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 2971595da365d0d7ef7015565223ed15e9f74390 Mon Sep 17 00:00:00 2001 From: Matias Scharager Date: Tue, 18 Jun 2024 15:09:59 -0400 Subject: [PATCH 145/225] Contracts: History Expressions via "old" monad (#3232) Add support for History Expressions. This allows an ensures clause to evaluate expressions before the function runs, bing them to anonymous variables and use them after calculating the result to be able to compare to the state before the function evaluation. Resolves #2803 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: Matias Scharager Co-authored-by: Justus Adam Co-authored-by: Felipe R. Monteiro --- library/kani/src/contracts.rs | 23 ++ library/kani_macros/Cargo.toml | 2 +- .../src/sysroot/contracts/check.rs | 9 +- .../src/sysroot/contracts/helpers.rs | 18 ++ .../src/sysroot/contracts/initialize.rs | 88 +------- .../kani_macros/src/sysroot/contracts/mod.rs | 86 +++++++- .../src/sysroot/contracts/replace.rs | 9 +- .../src/sysroot/contracts/shared.rs | 205 +++++++++++++++++- rfc/src/rfcs/0009-function-contracts.md | 43 ++++ .../function-contract/history/block.expected | 5 + .../function-contract/history/block.rs | 17 ++ .../history/clone_pass.expected | 5 + .../function-contract/history/clone_pass.rs | 28 +++ .../history/copy_pass.expected | 5 + .../function-contract/history/copy_pass.rs | 28 +++ .../history/function_call.expected | 5 + .../history/function_call.rs | 24 ++ .../history/no_modifies.expected | 5 + .../function-contract/history/no_modifies.rs | 17 ++ .../history/old_old.expected | 2 + .../function-contract/history/old_old.rs | 18 ++ .../history/side_effect.expected | 2 + .../function-contract/history/side_effect.rs | 23 ++ .../history/simple_fail.expected | 7 + .../function-contract/history/simple_fail.rs | 16 ++ .../history/simple_pass.expected | 5 + .../function-contract/history/simple_pass.rs | 16 ++ .../history/simple_pass_twice.expected | 9 + .../history/simple_pass_twice.rs | 17 ++ .../function-contract/history/stub.expected | 9 + .../function-contract/history/stub.rs | 31 +++ .../history/ui/no_args.expected | 1 + .../function-contract/history/ui/no_args.rs | 18 ++ .../history/ui/noncopy_ignore.expected | 1 + .../history/ui/noncopy_ignore.rs | 16 ++ .../history/ui/old_result.expected | 1 + .../history/ui/old_result.rs | 16 ++ .../history/ui/statement.expected | 1 + .../function-contract/history/ui/statement.rs | 16 ++ 39 files changed, 746 insertions(+), 101 deletions(-) create mode 100644 tests/expected/function-contract/history/block.expected create mode 100644 tests/expected/function-contract/history/block.rs create mode 100644 tests/expected/function-contract/history/clone_pass.expected create mode 100644 tests/expected/function-contract/history/clone_pass.rs create mode 100644 tests/expected/function-contract/history/copy_pass.expected create mode 100644 tests/expected/function-contract/history/copy_pass.rs create mode 100644 tests/expected/function-contract/history/function_call.expected create mode 100644 tests/expected/function-contract/history/function_call.rs create mode 100644 tests/expected/function-contract/history/no_modifies.expected create mode 100644 tests/expected/function-contract/history/no_modifies.rs create mode 100644 tests/expected/function-contract/history/old_old.expected create mode 100644 tests/expected/function-contract/history/old_old.rs create mode 100644 tests/expected/function-contract/history/side_effect.expected create mode 100644 tests/expected/function-contract/history/side_effect.rs create mode 100644 tests/expected/function-contract/history/simple_fail.expected create mode 100644 tests/expected/function-contract/history/simple_fail.rs create mode 100644 tests/expected/function-contract/history/simple_pass.expected create mode 100644 tests/expected/function-contract/history/simple_pass.rs create mode 100644 tests/expected/function-contract/history/simple_pass_twice.expected create mode 100644 tests/expected/function-contract/history/simple_pass_twice.rs create mode 100644 tests/expected/function-contract/history/stub.expected create mode 100644 tests/expected/function-contract/history/stub.rs create mode 100644 tests/expected/function-contract/history/ui/no_args.expected create mode 100644 tests/expected/function-contract/history/ui/no_args.rs create mode 100644 tests/expected/function-contract/history/ui/noncopy_ignore.expected create mode 100644 tests/expected/function-contract/history/ui/noncopy_ignore.rs create mode 100644 tests/expected/function-contract/history/ui/old_result.expected create mode 100644 tests/expected/function-contract/history/ui/old_result.rs create mode 100644 tests/expected/function-contract/history/ui/statement.expected create mode 100644 tests/expected/function-contract/history/ui/statement.rs diff --git a/library/kani/src/contracts.rs b/library/kani/src/contracts.rs index 65b9ec7c01cc..beddd7cc580e 100644 --- a/library/kani/src/contracts.rs +++ b/library/kani/src/contracts.rs @@ -225,4 +225,27 @@ //! Rust pointer type (`&T`, `&mut T`, `*const T` or `*mut T`). In addition `T` //! must implement [`Arbitrary`](super::Arbitrary). This is used to assign //! `kani::any()` to the location when the function is used in a `stub_verified`. +//! +//! ## History Expressions +//! +//! Additionally, an ensures clause is allowed to refer to the state of the function arguments before function execution and perform simple computations on them +//! via an `old` monad. Any instance of `old(computation)` will evaluate the +//! computation before the function is called. It is required that this computation +//! is effect free and closed with respect to the function arguments. +//! +//! For example, the following code passes kani tests: +//! +//! ``` +//! #[kani::modifies(a)] +//! #[kani::ensures(|result| old(*a).wrapping_add(1) == *a)] +//! #[kani::ensures(|result : &u32| old(*a).wrapping_add(1) == *result)] +//! fn add1(a : &mut u32) -> u32 { +//! *a=a.wrapping_add(1); +//! *a +//! } +//! ``` +//! +//! Here, the value stored in `a` is precomputed and remembered after the function +//! is called, even though the contents of `a` changed during the function execution. +//! pub use super::{ensures, modifies, proof_for_contract, requires, stub_verified}; diff --git a/library/kani_macros/Cargo.toml b/library/kani_macros/Cargo.toml index dbe2126d0458..e0c79a39c105 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -15,7 +15,7 @@ proc-macro = true proc-macro2 = "1.0" proc-macro-error = "1.0.4" quote = "1.0.20" -syn = { version = "2.0.18", features = ["full", "visit-mut", "visit"] } +syn = { version = "2.0.18", features = ["full", "visit-mut", "visit", "extra-traits"] } [package.metadata.rust-analyzer] # This package uses rustc crates. diff --git a/library/kani_macros/src/sysroot/contracts/check.rs b/library/kani_macros/src/sysroot/contracts/check.rs index bd4c09b07796..2e46d67a5b8e 100644 --- a/library/kani_macros/src/sysroot/contracts/check.rs +++ b/library/kani_macros/src/sysroot/contracts/check.rs @@ -9,7 +9,7 @@ use syn::{Expr, FnArg, ItemFn, Token}; use super::{ helpers::*, - shared::{make_unsafe_argument_copies, try_as_result_assign_mut}, + shared::{build_ensures, try_as_result_assign_mut}, ContractConditionsData, ContractConditionsHandler, INTERNAL_RESULT_IDENT, }; @@ -33,13 +33,14 @@ impl<'a> ContractConditionsHandler<'a> { #(#inner)* ) } - ContractConditionsData::Ensures { argument_names, attr } => { - let (arg_copies, copy_clean) = make_unsafe_argument_copies(&argument_names); + ContractConditionsData::Ensures { attr } => { + let (arg_copies, copy_clean, ensures_clause) = + build_ensures(&self.annotated_fn.sig, attr); // The code that enforces the postconditions and cleans up the shallow // argument copies (with `mem::forget`). let exec_postconditions = quote!( - kani::assert(#attr, stringify!(#attr_copy)); + kani::assert(#ensures_clause, stringify!(#attr_copy)); #copy_clean ); diff --git a/library/kani_macros/src/sysroot/contracts/helpers.rs b/library/kani_macros/src/sysroot/contracts/helpers.rs index baa25b8104e8..174f0f9483d7 100644 --- a/library/kani_macros/src/sysroot/contracts/helpers.rs +++ b/library/kani_macros/src/sysroot/contracts/helpers.rs @@ -268,3 +268,21 @@ pub fn short_hash_of_token_stream(stream: &proc_macro::TokenStream) -> u64 { let long_hash = hasher.finish(); long_hash % SIX_HEX_DIGITS_MASK } + +macro_rules! assert_spanned_err { + ($condition:expr, $span_source:expr, $msg:expr, $($args:expr),+) => { + if !$condition { + $span_source.span().unwrap().error(format!($msg, $($args),*)).emit(); + assert!(false); + } + }; + ($condition:expr, $span_source:expr, $msg:expr $(,)?) => { + if !$condition { + $span_source.span().unwrap().error($msg).emit(); + assert!(false); + } + }; + ($condition:expr, $span_source:expr) => { + assert_spanned_err!($condition, $span_source, concat!("Failed assertion ", stringify!($condition))) + }; +} diff --git a/library/kani_macros/src/sysroot/contracts/initialize.rs b/library/kani_macros/src/sysroot/contracts/initialize.rs index aee0c21bc82d..73621b2aeec5 100644 --- a/library/kani_macros/src/sysroot/contracts/initialize.rs +++ b/library/kani_macros/src/sysroot/contracts/initialize.rs @@ -3,17 +3,14 @@ //! Initialization routine for the contract handler -use std::collections::{HashMap, HashSet}; - use proc_macro::{Diagnostic, TokenStream}; -use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; -use quote::quote; -use syn::{spanned::Spanned, visit::Visit, visit_mut::VisitMut, Expr, ExprClosure, ItemFn}; +use proc_macro2::{Ident, TokenStream as TokenStream2}; +use syn::{spanned::Spanned, ItemFn}; use super::{ helpers::{chunks_by, is_token_stream_2_comma, matches_path}, ContractConditionsData, ContractConditionsHandler, ContractConditionsType, - ContractFunctionState, INTERNAL_RESULT_IDENT, + ContractFunctionState, }; impl<'a> TryFrom<&'a syn::Attribute> for ContractFunctionState { @@ -82,11 +79,7 @@ impl<'a> ContractConditionsHandler<'a> { ContractConditionsData::Requires { attr: syn::parse(attr)? } } ContractConditionsType::Ensures => { - let mut data: ExprClosure = syn::parse(attr)?; - let argument_names = rename_argument_occurrences(&annotated_fn.sig, &mut data); - let result: Ident = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site()); - let app: Expr = Expr::Verbatim(quote!((#data)(&#result))); - ContractConditionsData::Ensures { argument_names, attr: app } + ContractConditionsData::Ensures { attr: syn::parse(attr)? } } ContractConditionsType::Modifies => { ContractConditionsData::new_modifies(attr, &mut output) @@ -115,76 +108,3 @@ impl ContractConditionsData { ContractConditionsData::Modifies { attr } } } - -/// A supporting function for creating shallow, unsafe copies of the arguments -/// for the postconditions. -/// -/// This function: -/// - Collects all [`Ident`]s found in the argument patterns; -/// - Creates new names for them; -/// - Replaces all occurrences of those idents in `attrs` with the new names and; -/// - Returns the mapping of old names to new names. -fn rename_argument_occurrences( - sig: &syn::Signature, - attr: &mut ExprClosure, -) -> HashMap { - let mut arg_ident_collector = ArgumentIdentCollector::new(); - arg_ident_collector.visit_signature(&sig); - - let mk_new_ident_for = |id: &Ident| Ident::new(&format!("{}_renamed", id), Span::mixed_site()); - let arg_idents = arg_ident_collector - .0 - .into_iter() - .map(|i| { - let new = mk_new_ident_for(&i); - (i, new) - }) - .collect::>(); - - let mut ident_rewriter = Renamer(&arg_idents); - ident_rewriter.visit_expr_closure_mut(attr); - arg_idents -} - -/// Collect all named identifiers used in the argument patterns of a function. -struct ArgumentIdentCollector(HashSet); - -impl ArgumentIdentCollector { - fn new() -> Self { - Self(HashSet::new()) - } -} - -impl<'ast> Visit<'ast> for ArgumentIdentCollector { - fn visit_pat_ident(&mut self, i: &'ast syn::PatIdent) { - self.0.insert(i.ident.clone()); - syn::visit::visit_pat_ident(self, i) - } - fn visit_receiver(&mut self, _: &'ast syn::Receiver) { - self.0.insert(Ident::new("self", proc_macro2::Span::call_site())); - } -} - -/// Applies the contained renaming (key renamed to value) to every ident pattern -/// and ident expr visited. -struct Renamer<'a>(&'a HashMap); - -impl<'a> VisitMut for Renamer<'a> { - fn visit_expr_path_mut(&mut self, i: &mut syn::ExprPath) { - if i.path.segments.len() == 1 { - i.path - .segments - .first_mut() - .and_then(|p| self.0.get(&p.ident).map(|new| p.ident = new.clone())); - } - } - - /// This restores shadowing. Without this we would rename all ident - /// occurrences, but not rebinding location. This is because our - /// [`Self::visit_expr_path_mut`] is scope-unaware. - fn visit_pat_ident_mut(&mut self, i: &mut syn::PatIdent) { - if let Some(new) = self.0.get(&i.ident) { - i.ident = new.clone(); - } - } -} diff --git a/library/kani_macros/src/sysroot/contracts/mod.rs b/library/kani_macros/src/sysroot/contracts/mod.rs index 7a25cc390100..c91c307fba2e 100644 --- a/library/kani_macros/src/sysroot/contracts/mod.rs +++ b/library/kani_macros/src/sysroot/contracts/mod.rs @@ -233,15 +233,92 @@ //! } //! } //! ``` +//! +//! Additionally, there is functionality that allows the referencing of +//! history values within the ensures statement. This means we can +//! precompute a value before the function is called and have access to +//! this value in the later ensures statement. This is done via the +//! `old` monad which lets you access the old state within the present +//! state. Each occurrence of `old` is lifted, so is is necessary that +//! each lifted occurrence is closed with respect to the function arguments. +//! The results of these old computations are placed into +//! `remember_kani_internal_XXX` variables of incrementing index to avoid +//! collisions of variable names. Consider the following example: +//! +//! ``` +//! #[kani::ensures(|result| old(*ptr + 1) == *ptr)] +//! #[kani::ensures(|result| old(*ptr + 1) == *ptr)] +//! #[kani::requires(*ptr < 100)] +//! #[kani::modifies(ptr)] +//! fn modify(ptr: &mut u32) { +//! *ptr += 1; +//! } +//! +//! #[kani::proof_for_contract(modify)] +//! fn main() { +//! let mut i = kani::any(); +//! modify(&mut i); +//! } +//! +//! ``` +//! +//! This expands to +//! +//! ``` +//! #[allow(dead_code, unused_variables, unused_mut)] +//! #[kanitool::is_contract_generated(check)] +//! fn modify_check_633496(ptr: &mut u32) { +//! let _wrapper_arg_1 = +//! unsafe { kani::internal::Pointer::decouple_lifetime(&ptr) }; +//! kani::assume(*ptr < 100); +//! let remember_kani_internal_1 = *ptr + 1; +//! let ptr_renamed = kani::internal::untracked_deref(&ptr); +//! let remember_kani_internal_0 = *ptr + 1; +//! let ptr_renamed = kani::internal::untracked_deref(&ptr); +//! let result_kani_internal: () = modify_wrapper_633496(ptr, _wrapper_arg_1); +//! kani::assert((|result| +//! (remember_kani_internal_0) == +//! *ptr_renamed)(&result_kani_internal), +//! "|result| old(*ptr + 1) == *ptr"); +//! std::mem::forget(ptr_renamed); +//! kani::assert((|result| +//! (remember_kani_internal_1) == +//! *ptr_renamed)(&result_kani_internal), +//! "|result| old(*ptr + 1) == *ptr"); +//! std::mem::forget(ptr_renamed); +//! result_kani_internal +//! } +//! #[allow(dead_code, unused_variables, unused_mut)] +//! #[kanitool::is_contract_generated(replace)] +//! fn modify_replace_633496(ptr: &mut u32) { +//! kani::assert(*ptr < 100, "*ptr < 100"); +//! let remember_kani_internal_1 = *ptr + 1; +//! let ptr_renamed = kani::internal::untracked_deref(&ptr); +//! let remember_kani_internal_0 = *ptr + 1; +//! let ptr_renamed = kani::internal::untracked_deref(&ptr); +//! let result_kani_internal: () = kani::any_modifies(); +//! *unsafe { kani::internal::Pointer::assignable(ptr) } = +//! kani::any_modifies(); +//! kani::assume((|result| +//! (remember_kani_internal_0) == +//! *ptr_renamed)(&result_kani_internal)); +//! std::mem::forget(ptr_renamed); +//! kani::assume((|result| +//! (remember_kani_internal_1) == +//! *ptr_renamed)(&result_kani_internal)); +//! std::mem::forget(ptr_renamed); +//! result_kani_internal +//! } +//! ``` use proc_macro::TokenStream; use proc_macro2::{Ident, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; -use std::collections::HashMap; -use syn::{parse_macro_input, Expr, ItemFn}; +use syn::{parse_macro_input, Expr, ExprClosure, ItemFn}; mod bootstrap; mod check; +#[macro_use] mod helpers; mod initialize; mod replace; @@ -352,11 +429,8 @@ enum ContractConditionsData { attr: Expr, }, Ensures { - /// Translation map from original argument names to names of the copies - /// we will be emitting. - argument_names: HashMap, /// The contents of the attribute. - attr: Expr, + attr: ExprClosure, }, Modifies { attr: Vec, diff --git a/library/kani_macros/src/sysroot/contracts/replace.rs b/library/kani_macros/src/sysroot/contracts/replace.rs index 1a84e40523c2..2290cecb5aec 100644 --- a/library/kani_macros/src/sysroot/contracts/replace.rs +++ b/library/kani_macros/src/sysroot/contracts/replace.rs @@ -8,7 +8,7 @@ use quote::quote; use super::{ helpers::*, - shared::{make_unsafe_argument_copies, try_as_result_assign}, + shared::{build_ensures, try_as_result_assign}, ContractConditionsData, ContractConditionsHandler, INTERNAL_RESULT_IDENT, }; @@ -79,14 +79,15 @@ impl<'a> ContractConditionsHandler<'a> { #result ) } - ContractConditionsData::Ensures { attr, argument_names } => { - let (arg_copies, copy_clean) = make_unsafe_argument_copies(&argument_names); + ContractConditionsData::Ensures { attr } => { + let (arg_copies, copy_clean, ensures_clause) = + build_ensures(&self.annotated_fn.sig, attr); let result = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site()); quote!( #arg_copies #(#before)* #(#after)* - kani::assume(#attr); + kani::assume(#ensures_clause); #copy_clean #result ) diff --git a/library/kani_macros/src/sysroot/contracts/shared.rs b/library/kani_macros/src/sysroot/contracts/shared.rs index bf204e6bca32..5c85d723f686 100644 --- a/library/kani_macros/src/sysroot/contracts/shared.rs +++ b/library/kani_macros/src/sysroot/contracts/shared.rs @@ -7,11 +7,15 @@ //! This is so we can keep [`super`] distraction-free as the definitions of data //! structures and the entry point for contract handling. -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; -use syn::Attribute; +use std::hash::{DefaultHasher, Hash, Hasher}; +use syn::{ + spanned::Spanned, visit::Visit, visit_mut::VisitMut, Attribute, Expr, ExprCall, ExprClosure, + ExprPath, Path, +}; use super::{ContractConditionsHandler, ContractFunctionState, INTERNAL_RESULT_IDENT}; @@ -176,3 +180,200 @@ pub fn try_as_result_assign(stmt: &syn::Stmt) -> Option<&syn::LocalInit> { pub fn try_as_result_assign_mut(stmt: &mut syn::Stmt) -> Option<&mut syn::LocalInit> { try_as_result_assign_pat!(stmt, as_mut) } + +/// When a `#[kani::ensures(|result|expr)]` is expanded, this function is called on with `build_ensures(|result|expr)`. +/// This function goes through the expr and extracts out all the `old` expressions and creates a sequence +/// of statements that instantiate these expressions as `let remember_kani_internal_x = old_expr;` +/// where x is a unique hash. This is returned as the first return parameter along with changing all the +/// variables to `_renamed`. The second parameter is the closing of all the unsafe argument copies. The third +/// return parameter is the expression formed by passing in the result variable into the input closure and +/// changing all the variables to `_renamed`. +pub fn build_ensures( + fn_sig: &syn::Signature, + data: &ExprClosure, +) -> (TokenStream2, 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 arg_names = rename_argument_occurrences(fn_sig, &mut expr); + let (start, end) = make_unsafe_argument_copies(&arg_names); + + let remembers_stmts: TokenStream2 = remembers_exprs + .iter() + .fold(start, |collect, (ident, expr)| quote!(let #ident = #expr; #collect)); + + let result: Ident = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site()); + (remembers_stmts, end, Expr::Verbatim(quote!((#expr)(&#result)))) +} + +trait OldTrigger { + /// You are provided with the expression that is the first argument of the + /// `old()` call. You may modify it as you see fit. The return value + /// indicates whether the entire `old()` call should be replaced by the + /// (potentially altered) first argument. + /// + /// The second argument is the span of the original `old` expression. + /// + /// The third argument is a collection of all the expressions that need to be lifted + /// into the past environment as new remember variables. + fn trigger(&mut self, e: &mut Expr, s: Span, output: &mut HashMap) -> bool; +} + +struct OldLifter; + +impl OldLifter { + fn new() -> Self { + Self + } +} + +struct OldDenier; + +impl OldTrigger for OldDenier { + fn trigger(&mut self, _: &mut Expr, s: Span, _: &mut HashMap) -> bool { + s.unwrap().error("Nested calls to `old` are prohibited").emit(); + false + } +} + +struct OldVisitor<'a, T> { + t: T, + remembers_exprs: &'a mut HashMap, +} + +impl syn::visit_mut::VisitMut for OldVisitor<'_, T> { + fn visit_expr_mut(&mut self, ex: &mut Expr) { + let trigger = match &*ex { + Expr::Call(call @ ExprCall { func, attrs, args, .. }) => match func.as_ref() { + Expr::Path(ExprPath { + attrs: func_attrs, + qself: None, + path: Path { leading_colon: None, segments }, + }) if segments.len() == 1 + && segments.first().map_or(false, |sgm| sgm.ident == "old") => + { + let first_segment = segments.first().unwrap(); + assert_spanned_err!(first_segment.arguments.is_empty(), first_segment); + assert_spanned_err!(attrs.is_empty(), call); + assert_spanned_err!(func_attrs.is_empty(), func); + assert_spanned_err!(args.len() == 1, call); + true + } + _ => false, + }, + _ => false, + }; + if trigger { + let span = ex.span(); + let new_expr = if let Expr::Call(ExprCall { ref mut args, .. }) = ex { + self.t + .trigger(args.iter_mut().next().unwrap(), span, self.remembers_exprs) + .then(|| args.pop().unwrap().into_value()) + } else { + unreachable!() + }; + if let Some(new) = new_expr { + let _ = std::mem::replace(ex, new); + } + } else { + syn::visit_mut::visit_expr_mut(self, ex) + } + } +} + +impl OldTrigger for OldLifter { + fn trigger( + &mut self, + e: &mut Expr, + _: Span, + remembers_exprs: &mut HashMap, + ) -> bool { + let mut denier = OldVisitor { t: OldDenier, remembers_exprs }; + // This ensures there are no nested calls to `old` + denier.visit_expr_mut(e); + let mut hasher = DefaultHasher::new(); + e.hash(&mut hasher); + let ident = + Ident::new(&format!("remember_kani_internal_{:x}", hasher.finish()), Span::call_site()); + // save the original expression to be lifted into the past remember environment + remembers_exprs.insert(ident.clone(), (*e).clone()); + // change the expression to refer to the new remember variable + let _ = std::mem::replace(e, Expr::Verbatim(quote!((#ident)))); + true + } +} + +/// A supporting function for creating shallow, unsafe copies of the arguments +/// for the postconditions. +/// +/// This function: +/// - Collects all [`Ident`]s found in the argument patterns; +/// - Creates new names for them; +/// - Replaces all occurrences of those idents in `attrs` with the new names and; +/// - Returns the mapping of old names to new names. +fn rename_argument_occurrences( + sig: &syn::Signature, + attr: &mut ExprClosure, +) -> HashMap { + let mut arg_ident_collector = ArgumentIdentCollector::new(); + arg_ident_collector.visit_signature(&sig); + + let mk_new_ident_for = |id: &Ident| Ident::new(&format!("{}_renamed", id), Span::mixed_site()); + let arg_idents = arg_ident_collector + .0 + .into_iter() + .map(|i| { + let new = mk_new_ident_for(&i); + (i, new) + }) + .collect::>(); + + let mut ident_rewriter = Renamer(&arg_idents); + ident_rewriter.visit_expr_closure_mut(attr); + arg_idents +} + +/// Collect all named identifiers used in the argument patterns of a function. +struct ArgumentIdentCollector(HashSet); + +impl ArgumentIdentCollector { + fn new() -> Self { + Self(HashSet::new()) + } +} + +impl<'ast> Visit<'ast> for ArgumentIdentCollector { + fn visit_pat_ident(&mut self, i: &'ast syn::PatIdent) { + self.0.insert(i.ident.clone()); + syn::visit::visit_pat_ident(self, i) + } + fn visit_receiver(&mut self, _: &'ast syn::Receiver) { + self.0.insert(Ident::new("self", proc_macro2::Span::call_site())); + } +} + +/// Applies the contained renaming (key renamed to value) to every ident pattern +/// and ident expr visited. +struct Renamer<'a>(&'a HashMap); + +impl<'a> VisitMut for Renamer<'a> { + fn visit_expr_path_mut(&mut self, i: &mut syn::ExprPath) { + if i.path.segments.len() == 1 { + i.path + .segments + .first_mut() + .and_then(|p| self.0.get(&p.ident).map(|new| p.ident = new.clone())); + } + } + + /// This restores shadowing. Without this we would rename all ident + /// occurrences, but not rebinding location. This is because our + /// [`Self::visit_expr_path_mut`] is scope-unaware. + fn visit_pat_ident_mut(&mut self, i: &mut syn::PatIdent) { + if let Some(new) = self.0.get(&i.ident) { + i.ident = new.clone(); + } + } +} diff --git a/rfc/src/rfcs/0009-function-contracts.md b/rfc/src/rfcs/0009-function-contracts.md index deeeca66ab7a..a8bd7bf66041 100644 --- a/rfc/src/rfcs/0009-function-contracts.md +++ b/rfc/src/rfcs/0009-function-contracts.md @@ -549,6 +549,49 @@ This is the technical portion of the RFC. Please provide high level details of t +We developed the `old` contract for history expressions via understanding it as a [modality](https://en.wikipedia.org/wiki/Monad_(functional_programming)) originating from [Moggi 1991](https://www.sciencedirect.com/science/article/pii/0890540191900524). +The `old` monad links the "language of the past" to the "language of the present". +Implementing the full generality of the monad is rather difficult, so we focus on a particular usage of the monad. + +We have an external syntax representation which is what the user inputs. We then parse this and logically manipulate it as a monad, prefixing all the `bind` operations. We then output the final compiled macro output as Rust code. + +In particular, if we have an ensures statement like +```rust +#[kani::ensures(old(*ptr)+1==*ptr)] +``` +Then we comprehend this as syntax for the statement (not within Rust) +``` +bind (*ptr : O(u32)) (|remember : u32| remember + 1 == *ptr) +``` +Here, the `O(u32)` is taking the type of the past `u32` and converting it into a type in the present `O(u32)` while the bind operation lets you use the value of the past `u32` to express a type in the present `bool`. + +This then gets compiled to (within Rust) +```rust +let remember = *ptr; +let result = ...; +kani::assert(remember + 1 == *ptr) +``` +This means that the underlying principle of the monad is there, but external syntax appears to be less like a monad because otherwise it would be too difficult to implement, and the user most likely only cares about this particular construction of prefixing all the `bind` operations. + +This construction requires that `old` expressions are closed with resprect to the input parameters. This is due to the lifting into the prefixed `bind` operations. + +A major drawback is that eta expansion fails. If we eta expand a function f, it becomes |x|f(x). Note that eta expansions guarantee that the original f and the |x|f(x) are equivalent which makes a lot of sense since you’re just calling the same function. However, we have that `old(y)` is not equivalent to `(|x|old(x))(y)`. `y` is a closed expression, so the first statement works. `x` is a bound variable, so it is an open expression, so compilation will fail. + +The reason for this restriction is that the user will most likely only want to use this particular prefixed `bind` structure for their code, so exposing the concept of monads to the user level would only confuse the user. It is also simpler from an implementation perspective to limit the monad to this particular usage. + +As for nested old, such as `old(old(*ptr)+*ptr)`, it is reasonable to interpret this as syntax representing +``` +bind (bind(*ptr)(|remember_1| remember_1 + *ptr)) (|remember_0| ...) +``` +which compiles to +```rust +let remember_1 = *ptr; +let remember_0 = remember_1 + *ptr; +let result = ...; +... +``` +so the restriction is just a matter of there not being implementation support for this kind of statement rather than the theory itself. It is not particularly useful to implement this because we claim that there should be no effectful computation within the contracts, so you can substitute the `remember_1` into the second line without worrying about the effects. Hence, we opt for simply restricting this behavior instead of implementing it. (Note: it can be implemented by changing `denier.visit_expr_mut(e);` into `self.visit_expr_mut(e);`) + + +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). + + +## Open questions + + +- **Function Contracts RFC** - CBMC has support for both `exists` and `forall`, but the + code generation is difficult. The most ergonomic and easy way to implement + quantifiers on the Rust side is as higher-order functions taking `Fn(T) -> + bool`, where `T` is some arbitrary type that can be quantified over. This + 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"? + + +## Future possibilities + + +- CBMC has an SMT backend which allows the use of quantifiers with arbitrary Boolean expressions. Kani must include an option for users to experiment with this backend. + +--- From c8746e2e942a137078d3ef7ed458d2a2234337cd Mon Sep 17 00:00:00 2001 From: Matias Scharager Date: Thu, 27 Jun 2024 13:46:57 -0400 Subject: [PATCH 161/225] Fix operand in fat pointer comparison (#3297) Fixing a typo that causes potential issues in compilation. Co-authored-by: Matias Scharager --- kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index 407ff261f6ab..e01ffbe462ba 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -57,7 +57,7 @@ impl<'tcx> GotocCtx<'tcx> { ) -> Expr { debug!(?op, ?left_op, ?right_op, "codegen_comparison_fat_ptr"); let left_typ = self.operand_ty_stable(left_op); - let right_typ = self.operand_ty_stable(left_op); + let right_typ = self.operand_ty_stable(right_op); assert_eq!(left_typ, right_typ, "Cannot compare pointers of different types"); assert!(self.is_fat_pointer_stable(left_typ)); From 3adb2620d22d6e3ca654aa0a8c86b4f30da15e00 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Thu, 27 Jun 2024 21:04:11 +0200 Subject: [PATCH 162/225] C library: declare malloc (#3296) In #1812 we removed standard library includes and instead provided forward declarations of `free`, `calloc`, and `memcpy` -- but seemingly forgot to include `malloc`, which we also use. This avoids a warning seen when dialling up `goto-cc` verbosity. --- library/kani/kani_lib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/library/kani/kani_lib.c b/library/kani/kani_lib.c index b077547b10d7..eca17a3abb0e 100644 --- a/library/kani/kani_lib.c +++ b/library/kani/kani_lib.c @@ -8,6 +8,7 @@ void free(void *ptr); 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; From 3b9665fcebe1a3d773996dbd54895ce483c34ea0 Mon Sep 17 00:00:00 2001 From: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> Date: Thu, 27 Jun 2024 14:54:06 -0600 Subject: [PATCH 163/225] Upgrade rust toolchain to 06-26 (#3299) Related change: - https://github.com/rust-lang/rust/commit/24e41f1d13 Resolves #3294 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/intrinsics.rs | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kani-compiler/src/kani_middle/intrinsics.rs b/kani-compiler/src/kani_middle/intrinsics.rs index 82a75a91f1ad..73e42be00e7b 100644 --- a/kani-compiler/src/kani_middle/intrinsics.rs +++ b/kani-compiler/src/kani_middle/intrinsics.rs @@ -77,7 +77,7 @@ impl<'tcx> ModelIntrinsics<'tcx> { let Operand::Constant(fn_def) = func else { unreachable!() }; fn_def.const_ = mirConst::from_value( ConstValue::ZeroSized, - tcx.type_of(stub_id).instantiate(tcx, &new_gen_args), + tcx.type_of(stub_id).instantiate(tcx, &*new_gen_args), ); } else { debug!(?arg_ty, "replace_simd_bitmask failed"); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 18be92ff77ee..e00fa6c1b0e3 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-06-25" +channel = "nightly-2024-06-26" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 5a6a5f30aac71b6a78cf4242348fd52df34ae0cf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2024 10:07:46 +0200 Subject: [PATCH 164/225] Automatic toolchain upgrade to nightly-2024-06-27 (#3301) Update Rust toolchain from nightly-2024-06-26 to nightly-2024-06-27 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/fda509e817abeeecb5b76bc1de844f355675c81e up to https://github.com/rust-lang/rust/commit/4bc39f028d14c24b04dd17dc425432c6ec354536. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e00fa6c1b0e3..c620752b423a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-06-26" +channel = "nightly-2024-06-27" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 0994887f481a657c80044a32eea60092499393ed Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 11:32:21 -0400 Subject: [PATCH 165/225] Automatic cargo update to 2024-07-01 (#3310) Dependency upgrade resulting from `cargo update`. --- Cargo.lock | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7a451cc8804..89e393db8b46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,9 +86,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "build-kani" @@ -140,9 +140,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" dependencies = [ "clap_builder", "clap_derive", @@ -150,9 +150,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" dependencies = [ "anstream", "anstyle", @@ -162,9 +162,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.5" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" dependencies = [ "heck", "proc-macro2", @@ -290,9 +290,9 @@ dependencies = [ [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encode_unicode" @@ -536,9 +536,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "matchers" @@ -587,9 +587,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", @@ -929,9 +929,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0" dependencies = [ "itoa", "ryu", From 6f0c0b5b97c6861a3113af85c5d94bc17ee3e306 Mon Sep 17 00:00:00 2001 From: Matias Scharager Date: Mon, 1 Jul 2024 12:39:32 -0400 Subject: [PATCH 166/225] Function Contracts: Closure Type Inference (#3307) The rust type inference for closures doesn't work in the particular use case we are using it for ensures clauses. By creating a helper function, we change the path the rust type inference takes and lets the type of the closure be identified properly. This means type annotations are no longer required within ensures clauses. Resolves #3304 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/src/internal.rs | 7 +++++++ .../kani_macros/src/sysroot/contracts/shared.rs | 2 +- .../simple_ensures_pass_no_annotation.expected | 6 ++++++ .../simple_ensures_pass_no_annotation.rs | 14 ++++++++++++++ .../mutating_ensures_error.expected | 1 + .../function-contracts/mutating_ensures_error.rs | 12 ++++++++++++ 6 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 tests/expected/function-contract/simple_ensures_pass_no_annotation.expected create mode 100644 tests/expected/function-contract/simple_ensures_pass_no_annotation.rs create mode 100644 tests/ui/function-contracts/mutating_ensures_error.expected create mode 100644 tests/ui/function-contracts/mutating_ensures_error.rs diff --git a/library/kani/src/internal.rs b/library/kani/src/internal.rs index a910c333b112..509f2cf51962 100644 --- a/library/kani/src/internal.rs +++ b/library/kani/src/internal.rs @@ -90,3 +90,10 @@ pub fn untracked_deref(_: &T) -> T { #[doc(hidden)] #[rustc_diagnostic_item = "KaniInitContracts"] pub fn init_contracts() {} + +/// This should only be used within contracts. The intent is to +/// perform type inference on a closure's argument +#[doc(hidden)] +pub fn apply_closure bool>(f: U, x: &T) -> bool { + f(x) +} diff --git a/library/kani_macros/src/sysroot/contracts/shared.rs b/library/kani_macros/src/sysroot/contracts/shared.rs index 2682c0781661..1ab791d9a117 100644 --- a/library/kani_macros/src/sysroot/contracts/shared.rs +++ b/library/kani_macros/src/sysroot/contracts/shared.rs @@ -176,7 +176,7 @@ pub fn build_ensures(data: &ExprClosure) -> (TokenStream2, Expr) { .fold(quote!(), |collect, (ident, expr)| quote!(let #ident = #expr; #collect)); let result: Ident = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site()); - (remembers_stmts, Expr::Verbatim(quote!((#expr)(&#result)))) + (remembers_stmts, Expr::Verbatim(quote!(kani::internal::apply_closure(#expr, &#result)))) } trait OldTrigger { diff --git a/tests/expected/function-contract/simple_ensures_pass_no_annotation.expected b/tests/expected/function-contract/simple_ensures_pass_no_annotation.expected new file mode 100644 index 000000000000..0779b6dc88f8 --- /dev/null +++ b/tests/expected/function-contract/simple_ensures_pass_no_annotation.expected @@ -0,0 +1,6 @@ +assertion\ +- Status: SUCCESS\ +- Description: "|result| (*result == x) | (*result == y)"\ +in function max + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/simple_ensures_pass_no_annotation.rs b/tests/expected/function-contract/simple_ensures_pass_no_annotation.rs new file mode 100644 index 000000000000..a3bf30e1c0f7 --- /dev/null +++ b/tests/expected/function-contract/simple_ensures_pass_no_annotation.rs @@ -0,0 +1,14 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +#[kani::ensures(|result| (*result == x) | (*result == y))] +fn max(x: u32, y: u32) -> u32 { + if x > y { x } else { y } +} + +#[kani::proof_for_contract(max)] +fn max_harness() { + let _ = Box::new(9_usize); + max(7, 6); +} diff --git a/tests/ui/function-contracts/mutating_ensures_error.expected b/tests/ui/function-contracts/mutating_ensures_error.expected new file mode 100644 index 000000000000..4e9bb3984298 --- /dev/null +++ b/tests/ui/function-contracts/mutating_ensures_error.expected @@ -0,0 +1 @@ +cannot assign to `*_x`, as `Fn` closures cannot mutate their captured variables diff --git a/tests/ui/function-contracts/mutating_ensures_error.rs b/tests/ui/function-contracts/mutating_ensures_error.rs new file mode 100644 index 000000000000..2fc5f3c8d702 --- /dev/null +++ b/tests/ui/function-contracts/mutating_ensures_error.rs @@ -0,0 +1,12 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +#[kani::ensures(|_| {*_x += 1; true})] +fn unit(_x: &mut u32) {} + +#[kani::proof_for_contract(id)] +fn harness() { + let mut x = kani::any(); + unit(&mut x); +} From d55f25b81c8fa6a0bed1b7e7f9af3383b397a525 Mon Sep 17 00:00:00 2001 From: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:14:44 -0400 Subject: [PATCH 167/225] Add support for f16 and f128 for toolchain upgrade to 6/28 (#3306) Adds support for f16 and f128, i.e 1. adding translation to `irep` . 2. generating arbitrary values for the new types 3. generating basic invariants (checking if safe) for new types 4. Adds sanity testing for arbitrary on the new types. Resolves #3303 --- cprover_bindings/src/goto_program/expr.rs | 26 +++++++++ cprover_bindings/src/goto_program/typ.rs | 54 ++++++++++++++++++- cprover_bindings/src/irep/irep_id.rs | 4 ++ cprover_bindings/src/irep/to_irep.rs | 43 +++++++++++++++ cprover_bindings/src/lib.rs | 3 ++ .../codegen_cprover_gotoc/codegen/operand.rs | 6 +++ .../src/codegen_cprover_gotoc/codegen/typ.rs | 7 +-- kani-compiler/src/kani_middle/attributes.rs | 3 +- kani-compiler/src/main.rs | 2 + library/kani/src/arbitrary.rs | 4 ++ library/kani/src/invariant.rs | 2 + library/kani/src/lib.rs | 2 + library/kani_core/src/arbitrary.rs | 9 ++++ library/kani_core/src/lib.rs | 2 + rust-toolchain.toml | 2 +- .../floats/non_standard_floats/expected | 31 +++++++++++ .../floats/non_standard_floats/main.rs | 27 ++++++++++ .../floats/{ => standard_floats}/expected | 2 - .../floats/{ => standard_floats}/main.rs | 11 ++-- tests/kani/FloatingPoint/main.rs | 6 +++ tests/kani/Invariant/invariant_impls.rs | 7 +++ 21 files changed, 240 insertions(+), 13 deletions(-) create mode 100644 tests/expected/arbitrary/floats/non_standard_floats/expected create mode 100644 tests/expected/arbitrary/floats/non_standard_floats/main.rs rename tests/expected/arbitrary/floats/{ => standard_floats}/expected (99%) rename tests/expected/arbitrary/floats/{ => standard_floats}/main.rs (74%) diff --git a/cprover_bindings/src/goto_program/expr.rs b/cprover_bindings/src/goto_program/expr.rs index 75eb18df0abb..16f33115e0ff 100644 --- a/cprover_bindings/src/goto_program/expr.rs +++ b/cprover_bindings/src/goto_program/expr.rs @@ -98,7 +98,11 @@ pub enum ExprValue { // {} EmptyUnion, /// `1.0f` + Float16Constant(f16), + /// `1.0f` FloatConstant(f32), + /// `Float 128 example` + Float128Constant(f128), /// `function(arguments)` FunctionCall { function: Expr, @@ -581,6 +585,28 @@ impl Expr { expr!(EmptyUnion, typ) } + /// `3.14f` + pub fn float16_constant(c: f16) -> Self { + expr!(Float16Constant(c), Type::float16()) + } + + /// `union {_Float16 f; uint16_t bp} u = {.bp = 0x1234}; >>> u.f <<<` + pub fn float16_constant_from_bitpattern(bp: u16) -> Self { + let c = f16::from_bits(bp); + Self::float16_constant(c) + } + + /// `3.14159265358979323846264338327950288L` + pub fn float128_constant(c: f128) -> Self { + expr!(Float128Constant(c), Type::float128()) + } + + /// `union {_Float128 f; __uint128_t bp} u = {.bp = 0x1234}; >>> u.f <<<` + pub fn float128_constant_from_bitpattern(bp: u128) -> Self { + let c = f128::from_bits(bp); + Self::float128_constant(c) + } + /// `1.0f` pub fn float_constant(c: f32) -> Self { expr!(FloatConstant(c), Type::float()) diff --git a/cprover_bindings/src/goto_program/typ.rs b/cprover_bindings/src/goto_program/typ.rs index da943b26ab19..d0cc8821a447 100644 --- a/cprover_bindings/src/goto_program/typ.rs +++ b/cprover_bindings/src/goto_program/typ.rs @@ -41,6 +41,10 @@ pub enum Type { FlexibleArray { typ: Box }, /// `float` Float, + /// `_Float16` + Float16, + /// `_Float128` + Float128, /// `struct x {}` IncompleteStruct { tag: InternedString }, /// `union x {}` @@ -166,6 +170,8 @@ impl DatatypeComponent { | Double | FlexibleArray { .. } | Float + | Float16 + | Float128 | Integer | Pointer { .. } | Signedbv { .. } @@ -363,6 +369,8 @@ impl Type { Double => st.machine_model().double_width, Empty => 0, FlexibleArray { .. } => 0, + Float16 => 16, + Float128 => 128, Float => st.machine_model().float_width, IncompleteStruct { .. } => unreachable!("IncompleteStruct doesn't have a sizeof"), IncompleteUnion { .. } => unreachable!("IncompleteUnion doesn't have a sizeof"), @@ -532,6 +540,22 @@ impl Type { } } + pub fn is_float_16(&self) -> bool { + let concrete = self.unwrap_typedef(); + match concrete { + Float16 => true, + _ => false, + } + } + + pub fn is_float_128(&self) -> bool { + let concrete = self.unwrap_typedef(); + match concrete { + Float128 => true, + _ => false, + } + } + pub fn is_float(&self) -> bool { let concrete = self.unwrap_typedef(); match concrete { @@ -543,7 +567,7 @@ impl Type { pub fn is_floating_point(&self) -> bool { let concrete = self.unwrap_typedef(); match concrete { - Double | Float => true, + Double | Float | Float16 | Float128 => true, _ => false, } } @@ -577,6 +601,8 @@ impl Type { | CInteger(_) | Double | Float + | Float16 + | Float128 | Integer | Pointer { .. } | Signedbv { .. } @@ -632,6 +658,8 @@ impl Type { | Double | Empty | Float + | Float16 + | Float128 | Integer | Pointer { .. } | Signedbv { .. } @@ -918,6 +946,8 @@ impl Type { | CInteger(_) | Double | Float + | Float16 + | Float128 | Integer | Pointer { .. } | Signedbv { .. } @@ -1042,6 +1072,14 @@ impl Type { FlexibleArray { typ: Box::new(self) } } + pub fn float16() -> Self { + Float16 + } + + pub fn float128() -> Self { + Float128 + } + pub fn float() -> Self { Float } @@ -1275,6 +1313,10 @@ impl Type { Expr::c_true() } else if self.is_float() { Expr::float_constant(1.0) + } else if self.is_float_16() { + Expr::float16_constant(1.0) + } else if self.is_float_128() { + Expr::float128_constant(1.0) } else if self.is_double() { Expr::double_constant(1.0) } else { @@ -1291,6 +1333,10 @@ impl Type { Expr::c_false() } else if self.is_float() { Expr::float_constant(0.0) + } else if self.is_float_16() { + Expr::float16_constant(0.0) + } else if self.is_float_128() { + Expr::float128_constant(0.0) } else if self.is_double() { Expr::double_constant(0.0) } else if self.is_pointer() { @@ -1309,6 +1355,8 @@ impl Type { | CInteger(_) | Double | Float + | Float16 + | Float128 | Integer | Pointer { .. } | Signedbv { .. } @@ -1413,6 +1461,8 @@ impl Type { 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 } => { @@ -1512,6 +1562,8 @@ mod type_tests { assert_eq!(type_def.is_unsigned(&mm), src_type.is_unsigned(&mm)); assert_eq!(type_def.is_scalar(), src_type.is_scalar()); assert_eq!(type_def.is_float(), src_type.is_float()); + assert_eq!(type_def.is_float_16(), src_type.is_float_16()); + assert_eq!(type_def.is_float_128(), src_type.is_float_128()); assert_eq!(type_def.is_floating_point(), src_type.is_floating_point()); assert_eq!(type_def.width(), src_type.width()); assert_eq!(type_def.can_be_lvalue(), src_type.can_be_lvalue()); diff --git a/cprover_bindings/src/irep/irep_id.rs b/cprover_bindings/src/irep/irep_id.rs index 119aecb8887c..9d52f4ef32fa 100644 --- a/cprover_bindings/src/irep/irep_id.rs +++ b/cprover_bindings/src/irep/irep_id.rs @@ -283,6 +283,8 @@ pub enum IrepId { Short, Long, Float, + Float16, + Float128, Double, Byte, Boolean, @@ -1157,6 +1159,8 @@ impl Display for IrepId { IrepId::Short => "short", IrepId::Long => "long", IrepId::Float => "float", + IrepId::Float16 => "float16", + IrepId::Float128 => "float128", IrepId::Double => "double", IrepId::Byte => "byte", IrepId::Boolean => "boolean", diff --git a/cprover_bindings/src/irep/to_irep.rs b/cprover_bindings/src/irep/to_irep.rs index 8716c16d88e0..4b5c86350172 100644 --- a/cprover_bindings/src/irep/to_irep.rs +++ b/cprover_bindings/src/irep/to_irep.rs @@ -254,6 +254,25 @@ impl ToIrep for ExprValue { )], } } + ExprValue::Float16Constant(i) => { + let c: u16 = i.to_bits(); + Irep { + id: IrepId::Constant, + sub: vec![], + named_sub: linear_map![(IrepId::Value, Irep::just_bitpattern_id(c, 16, false))], + } + } + ExprValue::Float128Constant(i) => { + let c: u128 = i.to_bits(); + Irep { + id: IrepId::Constant, + sub: vec![], + named_sub: linear_map![( + IrepId::Value, + Irep::just_bitpattern_id(c, 128, false) + )], + } + } ExprValue::FunctionCall { function, arguments } => side_effect_irep( IrepId::FunctionCall, vec![function.to_irep(mm), arguments_irep(arguments.iter(), mm)], @@ -695,6 +714,30 @@ impl ToIrep for Type { (IrepId::CCType, Irep::just_id(IrepId::Float)), ], }, + Type::Float16 => Irep { + id: IrepId::Floatbv, + sub: vec![], + // Fraction bits: 10 + // Exponent width bits: 5 + // Sign bit: 1 + named_sub: linear_map![ + (IrepId::F, Irep::just_int_id(10)), + (IrepId::Width, Irep::just_int_id(16)), + (IrepId::CCType, Irep::just_id(IrepId::Float16)), + ], + }, + Type::Float128 => Irep { + id: IrepId::Floatbv, + sub: vec![], + // Fraction bits: 112 + // Exponent width bits: 15 + // Sign bit: 1 + named_sub: linear_map![ + (IrepId::F, Irep::just_int_id(112)), + (IrepId::Width, Irep::just_int_id(128)), + (IrepId::CCType, Irep::just_id(IrepId::Float128)), + ], + }, Type::IncompleteStruct { tag } => Irep { id: IrepId::Struct, sub: vec![], diff --git a/cprover_bindings/src/lib.rs b/cprover_bindings/src/lib.rs index cd87dffd75a6..e77b29a24ca2 100644 --- a/cprover_bindings/src/lib.rs +++ b/cprover_bindings/src/lib.rs @@ -29,6 +29,9 @@ //! Speical [irep::Irep::id]s include: //! 1. [irep::IrepId::Empty] and [irep::IrepId::Nil] behaves like \[null\]. +#![feature(f128)] +#![feature(f16)] + mod env; pub mod goto_program; pub mod irep; diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs index 7388ca44a5b7..5549edcced25 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs @@ -184,12 +184,18 @@ impl<'tcx> GotocCtx<'tcx> { // Instead, we use integers with the right width to represent the bit pattern. { match k { + FloatTy::F16 => Some(Expr::float16_constant_from_bitpattern( + alloc.read_uint().unwrap() as u16, + )), FloatTy::F32 => Some(Expr::float_constant_from_bitpattern( alloc.read_uint().unwrap() as u32, )), FloatTy::F64 => Some(Expr::double_constant_from_bitpattern( alloc.read_uint().unwrap() as u64, )), + FloatTy::F128 => { + Some(Expr::float128_constant_from_bitpattern(alloc.read_uint().unwrap())) + } } } TyKind::RigidTy(RigidTy::RawPtr(inner_ty, _)) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index 6d00bda5bce4..6e6547295ff9 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -130,6 +130,8 @@ impl<'tcx> GotocCtx<'tcx> { Type::Empty => todo!(), Type::FlexibleArray { .. } => todo!(), Type::Float => write!(out, "f32")?, + Type::Float16 => write!(out, "f16")?, + Type::Float128 => write!(out, "f128")?, Type::IncompleteStruct { .. } => todo!(), Type::IncompleteUnion { .. } => todo!(), Type::InfiniteArray { .. } => todo!(), @@ -542,9 +544,8 @@ impl<'tcx> GotocCtx<'tcx> { ty::Float(k) => match k { FloatTy::F32 => Type::float(), FloatTy::F64 => Type::double(), - // `F16` and `F128` are not yet handled. - // Tracked here: - FloatTy::F16 | FloatTy::F128 => unimplemented!(), + FloatTy::F16 => Type::float16(), + FloatTy::F128 => Type::float128(), }, ty::Adt(def, _) if def.repr().simd() => self.codegen_vector(ty), ty::Adt(def, subst) => { diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index b7d8feeee886..84ec8d627c59 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -1034,10 +1034,9 @@ fn attr_kind(tcx: TyCtxt, attr: &Attribute) -> Option { .intersperse("::") .collect::(); KaniAttributeKind::try_from(ident_str.as_str()) - .map_err(|err| { + .inspect_err(|&err| { debug!(?err, "attr_kind_failed"); tcx.dcx().span_err(attr.span, format!("unknown attribute `{ident_str}`")); - err }) .ok() } else { diff --git a/kani-compiler/src/main.rs b/kani-compiler/src/main.rs index a5cfa347a85e..d2f8cf17e9e7 100644 --- a/kani-compiler/src/main.rs +++ b/kani-compiler/src/main.rs @@ -13,6 +13,8 @@ #![feature(more_qualified_paths)] #![feature(iter_intersperse)] #![feature(let_chains)] +#![feature(f128)] +#![feature(f16)] extern crate rustc_abi; extern crate rustc_ast; extern crate rustc_ast_pretty; diff --git a/library/kani/src/arbitrary.rs b/library/kani/src/arbitrary.rs index 3f1adc787b79..424ca2485d57 100644 --- a/library/kani/src/arbitrary.rs +++ b/library/kani/src/arbitrary.rs @@ -71,6 +71,10 @@ trivial_arbitrary!(isize); trivial_arbitrary!(f32); trivial_arbitrary!(f64); +// Similarly, we do not constraint values for non-standard floating types. +trivial_arbitrary!(f16); +trivial_arbitrary!(f128); + trivial_arbitrary!(()); impl Arbitrary for bool { diff --git a/library/kani/src/invariant.rs b/library/kani/src/invariant.rs index f118f94e995c..068cdedc277e 100644 --- a/library/kani/src/invariant.rs +++ b/library/kani/src/invariant.rs @@ -96,6 +96,8 @@ trivial_invariant!(isize); // invariant that checks for NaN, infinite, or subnormal values. trivial_invariant!(f32); trivial_invariant!(f64); +trivial_invariant!(f16); +trivial_invariant!(f128); trivial_invariant!(()); trivial_invariant!(bool); diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index acf1e08e0441..0a52a9516398 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -18,6 +18,8 @@ #![allow(internal_features)] // Required for implementing memory predicates. #![feature(ptr_metadata)] +#![feature(f16)] +#![feature(f128)] pub mod arbitrary; #[cfg(feature = "concrete_playback")] diff --git a/library/kani_core/src/arbitrary.rs b/library/kani_core/src/arbitrary.rs index d202df4ead1d..a8271ad758cf 100644 --- a/library/kani_core/src/arbitrary.rs +++ b/library/kani_core/src/arbitrary.rs @@ -88,6 +88,15 @@ macro_rules! generate_arbitrary { trivial_arbitrary!(i128); trivial_arbitrary!(isize); + // We do not constrain floating points values per type spec. Users must add assumptions to their + // verification code if they want to eliminate NaN, infinite, or subnormal. + trivial_arbitrary!(f32); + trivial_arbitrary!(f64); + + // Similarly, we do not constraint values for non-standard floating types. + trivial_arbitrary!(f16); + trivial_arbitrary!(f128); + nonzero_arbitrary!(NonZeroU8, u8); nonzero_arbitrary!(NonZeroU16, u16); nonzero_arbitrary!(NonZeroU32, u32); diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index de808ffaf918..143fbb7ef825 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -17,6 +17,8 @@ #![feature(no_core)] #![no_core] +#![feature(f16)] +#![feature(f128)] mod arbitrary; mod mem; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c620752b423a..5f02b82a51d2 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-06-27" +channel = "nightly-2024-06-28" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/expected/arbitrary/floats/non_standard_floats/expected b/tests/expected/arbitrary/floats/non_standard_floats/expected new file mode 100644 index 000000000000..26db615201a6 --- /dev/null +++ b/tests/expected/arbitrary/floats/non_standard_floats/expected @@ -0,0 +1,31 @@ +Checking harness check_f128... + +Status: SATISFIED\ +Description: "This may be true"\ +in function check_f128 + +Status: SATISFIED\ +Description: "This may also be true"\ +in function check_f128 + +Status: SATISFIED\ +Description: "NaN should be valid float"\ +in function check_f128 + +Checking harness check_f16... + +Status: SATISFIED\ +Description: "This may be true"\ +in function check_f16 + +Status: SATISFIED\ +Description: "This may also be true"\ +in function check_f16 + +Status: SATISFIED\ +Description: "NaN should be valid float"\ +in function check_f16 + +VERIFICATION:- SUCCESSFUL + +Complete - 2 successfully verified harnesses, 0 failures, 2 total. diff --git a/tests/expected/arbitrary/floats/non_standard_floats/main.rs b/tests/expected/arbitrary/floats/non_standard_floats/main.rs new file mode 100644 index 000000000000..ebea6535b3d5 --- /dev/null +++ b/tests/expected/arbitrary/floats/non_standard_floats/main.rs @@ -0,0 +1,27 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Ensure that kani::any and kani::any_raw can be used with non-standard floats i.e f16 and f128. + +#![feature(f16)] +#![feature(f128)] + +macro_rules! test_non_standard_floats { + ( $type: ty ) => {{ + let v1 = kani::any::<$type>(); + let v2 = kani::any::<$type>(); + kani::cover!(v1 == v2, "This may be true"); + kani::cover!(v1 != v2, "This may also be true"); + kani::cover!(v1.is_nan(), "NaN should be valid float"); + }}; +} + +#[kani::proof] +fn check_f16() { + test_non_standard_floats!(f16); +} + +#[kani::proof] +fn check_f128() { + test_non_standard_floats!(f128); +} diff --git a/tests/expected/arbitrary/floats/expected b/tests/expected/arbitrary/floats/standard_floats/expected similarity index 99% rename from tests/expected/arbitrary/floats/expected rename to tests/expected/arbitrary/floats/standard_floats/expected index 4bb2fadacd7f..de3b67f28578 100644 --- a/tests/expected/arbitrary/floats/expected +++ b/tests/expected/arbitrary/floats/standard_floats/expected @@ -51,10 +51,8 @@ Status: SATISFIED\ Description: "Non-finite numbers are valid float"\ in function check_f32 - ** 6 of 6 cover properties satisfied - VERIFICATION:- SUCCESSFUL Complete - 2 successfully verified harnesses, 0 failures, 2 total. diff --git a/tests/expected/arbitrary/floats/main.rs b/tests/expected/arbitrary/floats/standard_floats/main.rs similarity index 74% rename from tests/expected/arbitrary/floats/main.rs rename to tests/expected/arbitrary/floats/standard_floats/main.rs index 1ad4de3ef3f7..c204643e9ab8 100644 --- a/tests/expected/arbitrary/floats/main.rs +++ b/tests/expected/arbitrary/floats/standard_floats/main.rs @@ -1,9 +1,12 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // -// Ensure that kani::any and kani::any_raw can be used with floats. +// Ensure that kani::any and kani::any_raw can be used with standard floats i.e f32 and f64. -macro_rules! test { +#![feature(f16)] +#![feature(f128)] + +macro_rules! test_standard_floats { ( $type: ty ) => {{ let v1 = kani::any::<$type>(); let v2 = kani::any::<$type>(); @@ -18,10 +21,10 @@ macro_rules! test { #[kani::proof] fn check_f32() { - test!(f32); + test_standard_floats!(f32); } #[kani::proof] fn check_f64() { - test!(f64); + test_standard_floats!(f64); } diff --git a/tests/kani/FloatingPoint/main.rs b/tests/kani/FloatingPoint/main.rs index c04bd9b305f8..f8ebccdac02a 100644 --- a/tests/kani/FloatingPoint/main.rs +++ b/tests/kani/FloatingPoint/main.rs @@ -1,5 +1,9 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT + +#![feature(f16)] +#![feature(f128)] + macro_rules! test_floats { ($ty:ty) => { let a: $ty = kani::any(); @@ -26,6 +30,8 @@ fn main() { assert!(1.1 == 1.1 * 1.0); assert!(1.1 != 1.11 / 1.0); + test_floats!(f16); test_floats!(f32); test_floats!(f64); + test_floats!(f128); } diff --git a/tests/kani/Invariant/invariant_impls.rs b/tests/kani/Invariant/invariant_impls.rs index 4f00f4134956..146c9731370d 100644 --- a/tests/kani/Invariant/invariant_impls.rs +++ b/tests/kani/Invariant/invariant_impls.rs @@ -3,6 +3,10 @@ //! Check the `Invariant` implementations that we include in the Kani library //! with respect to the underlying type invariants. + +#![feature(f16)] +#![feature(f128)] + extern crate kani; use kani::Invariant; @@ -29,6 +33,9 @@ fn check_safe_impls() { check_safe_type!(i128); check_safe_type!(isize); + check_safe_type!(f16); + check_safe_type!(f128); + check_safe_type!(f32); check_safe_type!(f64); From 67d6ac9adb2731d6942c26bbe42ab19e54bef3b3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 10:56:05 +0200 Subject: [PATCH 168/225] Automatic toolchain upgrade to nightly-2024-06-29 (#3315) Update Rust toolchain from nightly-2024-06-28 to nightly-2024-06-29 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/9c3bc805dd9cb84019c124b9a50fdff1e62a7ec9 up to https://github.com/rust-lang/rust/commit/e9e6e2e444c30c23a9c878a88fbc3978c2acad95. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5f02b82a51d2..be39f8068af9 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-06-28" +channel = "nightly-2024-06-29" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 2fb97d28b457bf7eb86086ea44a3c5db23e41e38 Mon Sep 17 00:00:00 2001 From: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Date: Tue, 2 Jul 2024 05:28:14 -0400 Subject: [PATCH 169/225] Upgrade toolchain to `nightly-2024-07-01` (#3314) Upgrade toolchain to `nightly-2024-07-01` so we're up to date. No other changes are required. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index be39f8068af9..dfe3febbb927 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-06-29" +channel = "nightly-2024-07-01" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From d926482f7a724770d8f6818f312e8dda7b17738b Mon Sep 17 00:00:00 2001 From: Artem Agvanian Date: Tue, 2 Jul 2024 12:07:47 -0700 Subject: [PATCH 170/225] Towards Proving Memory Initialization (#3264) This PR enables automatic memory initialization proofs for raw pointers in Kani. This is done without any extra instrumentation from the user. Currently, due to high memory consumption and only partial support of pointee types for which memory initialization proofs work, this feature is gated behind `-Z uninit-checks` flag. Note that because it uses shadow memory under the hood, programs using this feature need to pass `-Z ghost-state` flag as well. This PR is a part of working towards #3300. 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 --- kani-compiler/src/args.rs | 2 + .../codegen_cprover_gotoc/codegen/contract.rs | 22 + .../src/kani_middle/transform/body.rs | 178 ++++- .../kani_middle/transform/check_uninit/mod.rs | 428 +++++++++++ .../transform/check_uninit/ty_layout.rs | 334 ++++++++ .../transform/check_uninit/uninit_visitor.rs | 713 ++++++++++++++++++ .../src/kani_middle/transform/check_values.rs | 80 +- .../src/kani_middle/transform/contracts.rs | 2 +- .../kani_middle/transform/kani_intrinsics.rs | 188 ++++- .../src/kani_middle/transform/mod.rs | 24 +- .../src/kani_middle/transform/stubs.rs | 4 +- kani-driver/src/call_single_file.rs | 4 + kani_metadata/src/unstable.rs | 2 + library/kani/src/lib.rs | 1 + library/kani/src/mem.rs | 10 +- library/kani/src/mem_init.rs | 122 +++ library/kani_core/src/mem.rs | 12 +- .../access-padding-uninit.rs | 16 + .../uninit/access-padding-uninit/expected | 5 + .../access-padding-via-cast.rs | 20 + .../uninit/access-padding-via-cast/expected | 5 + .../uninit/alloc-to-slice/alloc-to-slice.rs | 20 + tests/expected/uninit/alloc-to-slice/expected | 5 + .../expected/uninit/vec-read-bad-len/expected | 5 + .../vec-read-bad-len/vec-read-bad-len.rs | 15 + .../uninit/vec-read-semi-init/expected | 5 + .../vec-read-semi-init/vec-read-semi-init.rs | 11 + .../expected/uninit/vec-read-uninit/expected | 5 + .../uninit/vec-read-uninit/vec-read-uninit.rs | 10 + .../access-padding-enum-diverging-variants.rs | 34 + .../access-padding-enum-multiple-variants.rs | 49 ++ .../access-padding-enum-single-field.rs | 34 + .../access-padding-enum-single-variant.rs | 32 + tests/perf/uninit/Cargo.toml | 14 + tests/perf/uninit/expected | 1 + tests/perf/uninit/src/lib.rs | 68 ++ tests/std-checks/core/mem.expected | 6 +- tests/std-checks/core/slice.expected | 1 + tests/std-checks/core/src/lib.rs | 1 + tests/std-checks/core/src/mem.rs | 35 + tests/std-checks/core/src/slice.rs | 30 + 41 files changed, 2500 insertions(+), 53 deletions(-) create mode 100644 kani-compiler/src/kani_middle/transform/check_uninit/mod.rs create mode 100644 kani-compiler/src/kani_middle/transform/check_uninit/ty_layout.rs create mode 100644 kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs create mode 100644 library/kani/src/mem_init.rs create mode 100644 tests/expected/uninit/access-padding-uninit/access-padding-uninit.rs create mode 100644 tests/expected/uninit/access-padding-uninit/expected create mode 100644 tests/expected/uninit/access-padding-via-cast/access-padding-via-cast.rs create mode 100644 tests/expected/uninit/access-padding-via-cast/expected create mode 100644 tests/expected/uninit/alloc-to-slice/alloc-to-slice.rs create mode 100644 tests/expected/uninit/alloc-to-slice/expected create mode 100644 tests/expected/uninit/vec-read-bad-len/expected create mode 100644 tests/expected/uninit/vec-read-bad-len/vec-read-bad-len.rs create mode 100644 tests/expected/uninit/vec-read-semi-init/expected create mode 100644 tests/expected/uninit/vec-read-semi-init/vec-read-semi-init.rs create mode 100644 tests/expected/uninit/vec-read-uninit/expected create mode 100644 tests/expected/uninit/vec-read-uninit/vec-read-uninit.rs create mode 100644 tests/kani/Uninit/access-padding-enum-diverging-variants.rs create mode 100644 tests/kani/Uninit/access-padding-enum-multiple-variants.rs create mode 100644 tests/kani/Uninit/access-padding-enum-single-field.rs create mode 100644 tests/kani/Uninit/access-padding-enum-single-variant.rs create mode 100644 tests/perf/uninit/Cargo.toml create mode 100644 tests/perf/uninit/expected create mode 100644 tests/perf/uninit/src/lib.rs create mode 100644 tests/std-checks/core/slice.expected create mode 100644 tests/std-checks/core/src/slice.rs diff --git a/kani-compiler/src/args.rs b/kani-compiler/src/args.rs index b4d4eb3718d8..e4b7a4435b0f 100644 --- a/kani-compiler/src/args.rs +++ b/kani-compiler/src/args.rs @@ -88,4 +88,6 @@ pub enum ExtraChecks { /// Check pointer validity when casting pointers to references. /// See https://github.com/model-checking/kani/issues/2975. PtrToRefCast, + /// Check for using uninitialized memory. + Uninit, } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs index 0f27d3f119f7..d35015aa040d 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs @@ -118,6 +118,27 @@ impl<'tcx> GotocCtx<'tcx> { .typ .clone(); + let shadow_memory_assign = self + .tcx + .all_diagnostic_items(()) + .name_to_id + .get(&rustc_span::symbol::Symbol::intern("KaniMemInitShadowMem")) + .map(|attr_id| { + self.tcx + .symbol_name(rustc_middle::ty::Instance::mono(self.tcx, *attr_id)) + .name + .to_string() + }) + .and_then(|shadow_memory_table| self.symbol_table.lookup(&shadow_memory_table).cloned()) + .map(|shadow_memory_symbol| { + vec![Lambda::as_contract_for( + &goto_annotated_fn_typ, + None, + shadow_memory_symbol.to_expr(), + )] + }) + .unwrap_or_default(); + let assigns = modified_places .into_iter() .map(|local| { @@ -127,6 +148,7 @@ impl<'tcx> GotocCtx<'tcx> { self.codegen_place_stable(&local.into(), loc).unwrap().goto_expr.dereference(), ) }) + .chain(shadow_memory_assign) .collect(); FunctionContract::new(assigns) diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index 2bf424a1332d..22895bd8d20d 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -37,6 +37,13 @@ pub struct MutableBody { span: Span, } +/// Denotes whether instrumentation should be inserted before or after the statement. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum InsertPosition { + Before, + After, +} + impl MutableBody { /// Get the basic blocks of this builder. pub fn blocks(&self) -> &[BasicBlock] { @@ -95,12 +102,13 @@ impl MutableBody { from: Operand, pointee_ty: Ty, mutability: Mutability, - before: &mut SourceInstruction, + source: &mut SourceInstruction, + position: InsertPosition, ) -> Local { assert!(from.ty(self.locals()).unwrap().kind().is_raw_ptr()); let target_ty = Ty::new_ptr(pointee_ty, mutability); let rvalue = Rvalue::Cast(CastKind::PtrToPtr, from, target_ty); - self.new_assignment(rvalue, before) + self.new_assignment(rvalue, source, position) } /// Add a new assignment for the given binary operation. @@ -111,21 +119,27 @@ impl MutableBody { bin_op: BinOp, lhs: Operand, rhs: Operand, - before: &mut SourceInstruction, + source: &mut SourceInstruction, + position: InsertPosition, ) -> Local { let rvalue = Rvalue::BinaryOp(bin_op, lhs, rhs); - self.new_assignment(rvalue, before) + self.new_assignment(rvalue, source, position) } /// Add a new assignment. /// /// Return local where the result is saved. - pub fn new_assignment(&mut self, rvalue: Rvalue, before: &mut SourceInstruction) -> Local { - let span = before.span(&self.blocks); + pub fn new_assignment( + &mut self, + rvalue: Rvalue, + source: &mut SourceInstruction, + position: InsertPosition, + ) -> Local { + let span = source.span(&self.blocks); let ret_ty = rvalue.ty(&self.locals).unwrap(); let result = self.new_local(ret_ty, span, Mutability::Not); let stmt = Statement { kind: StatementKind::Assign(Place::from(result), rvalue), span }; - self.insert_stmt(stmt, before); + self.insert_stmt(stmt, source, position); result } @@ -139,6 +153,7 @@ impl MutableBody { tcx: TyCtxt, check_type: &CheckType, source: &mut SourceInstruction, + position: InsertPosition, value: Local, msg: &str, ) { @@ -168,7 +183,7 @@ impl MutableBody { unwind: UnwindAction::Terminate, }; let terminator = Terminator { kind, span }; - self.split_bb(source, terminator); + self.split_bb(source, position, terminator); } CheckType::Panic | CheckType::NoCore => { tcx.sess @@ -182,11 +197,55 @@ impl MutableBody { } } - /// Split a basic block right before the source location and use the new terminator - /// in the basic block that was split. + /// Add a new call to the basic block indicated by the given index. + /// + /// The new call will have the same span as the source instruction, and the basic block + /// will be split. The source instruction will be adjusted to point to the first instruction in + /// the new basic block. + pub fn add_call( + &mut self, + callee: &Instance, + source: &mut SourceInstruction, + position: InsertPosition, + args: Vec, + destination: Place, + ) { + let new_bb = self.blocks.len(); + let span = source.span(&self.blocks); + let callee_op = + Operand::Copy(Place::from(self.new_local(callee.ty(), span, Mutability::Not))); + let kind = TerminatorKind::Call { + func: callee_op, + args, + destination, + target: Some(new_bb), + unwind: UnwindAction::Terminate, + }; + let terminator = Terminator { kind, span }; + self.split_bb(source, position, terminator); + } + + /// Split a basic block and use the new terminator in the basic block that was split. /// /// The source is updated to point to the same instruction which is now in the new basic block. - pub fn split_bb(&mut self, source: &mut SourceInstruction, new_term: Terminator) { + pub fn split_bb( + &mut self, + source: &mut SourceInstruction, + position: InsertPosition, + new_term: Terminator, + ) { + match position { + InsertPosition::Before => { + self.split_bb_before(source, new_term); + } + InsertPosition::After => { + self.split_bb_after(source, new_term); + } + } + } + + /// Split a basic block right before the source location. + fn split_bb_before(&mut self, source: &mut SourceInstruction, new_term: Terminator) { let new_bb_idx = self.blocks.len(); let (idx, bb) = match source { SourceInstruction::Statement { idx, bb } => { @@ -196,9 +255,9 @@ impl MutableBody { (orig_idx, orig_bb) } SourceInstruction::Terminator { bb } => { - let orig_bb = *bb; + let (orig_idx, orig_bb) = (self.blocks[*bb].statements.len(), *bb); *bb = new_bb_idx; - (self.blocks[orig_bb].statements.len(), orig_bb) + (orig_idx, orig_bb) } }; let old_term = mem::replace(&mut self.blocks[bb].terminator, new_term); @@ -208,16 +267,95 @@ impl MutableBody { self.blocks.push(new_bb); } - /// Insert statement before the source instruction and update the source as needed. - pub fn insert_stmt(&mut self, new_stmt: Statement, before: &mut SourceInstruction) { - match before { + /// Split a basic block right after the source location. + fn split_bb_after(&mut self, source: &mut SourceInstruction, mut new_term: Terminator) { + let new_bb_idx = self.blocks.len(); + match source { + // Split the current block after the statement located at `source` + // and move the remaining statements into the new one. SourceInstruction::Statement { idx, bb } => { - self.blocks[*bb].statements.insert(*idx, new_stmt); - *idx += 1; + let (orig_idx, orig_bb) = (*idx, *bb); + *idx = 0; + *bb = new_bb_idx; + let old_term = mem::replace(&mut self.blocks[orig_bb].terminator, new_term); + let bb_stmts = &mut self.blocks[orig_bb].statements; + let remaining = bb_stmts.split_off(orig_idx + 1); + let new_bb = BasicBlock { statements: remaining, terminator: old_term }; + self.blocks.push(new_bb); } + // Make the terminator at `source` point at the new block, + // the terminator of which is a simple Goto instruction. SourceInstruction::Terminator { bb } => { - // Append statements at the end of the basic block. - self.blocks[*bb].statements.push(new_stmt); + let current_terminator = &mut self.blocks.get_mut(*bb).unwrap().terminator; + // Kani can only instrument function calls like this. + match (&mut current_terminator.kind, &mut new_term.kind) { + ( + TerminatorKind::Call { target: Some(target_bb), .. }, + TerminatorKind::Call { target: Some(new_target_bb), .. }, + ) => { + // Set the new terminator to point where previous terminator pointed. + *new_target_bb = *target_bb; + // Point the current terminator to the new terminator's basic block. + *target_bb = new_bb_idx; + // Update the current poisition. + *bb = new_bb_idx; + self.blocks.push(BasicBlock { statements: vec![], terminator: new_term }); + } + _ => unimplemented!("Kani can only split blocks after calls."), + }; + } + }; + } + + /// Insert statement before or after the source instruction and update the source as needed. + pub fn insert_stmt( + &mut self, + new_stmt: Statement, + source: &mut SourceInstruction, + position: InsertPosition, + ) { + match position { + InsertPosition::Before => { + match source { + SourceInstruction::Statement { idx, bb } => { + self.blocks[*bb].statements.insert(*idx, new_stmt); + *idx += 1; + } + SourceInstruction::Terminator { bb } => { + // Append statements at the end of the basic block. + self.blocks[*bb].statements.push(new_stmt); + } + } + } + InsertPosition::After => { + let new_bb_idx = self.blocks.len(); + let span = source.span(&self.blocks); + match source { + SourceInstruction::Statement { idx, bb } => { + self.blocks[*bb].statements.insert(*idx + 1, new_stmt); + *idx += 1; + } + SourceInstruction::Terminator { bb } => { + // Create a new basic block, as we need to append a statement after the terminator. + let current_terminator = &mut self.blocks.get_mut(*bb).unwrap().terminator; + // Kani can only instrument function calls in this way. + match &mut current_terminator.kind { + TerminatorKind::Call { target: Some(target_bb), .. } => { + *source = SourceInstruction::Statement { idx: 0, bb: new_bb_idx }; + let new_bb = BasicBlock { + statements: vec![new_stmt], + terminator: Terminator { + kind: TerminatorKind::Goto { target: *target_bb }, + span, + }, + }; + *target_bb = new_bb_idx; + self.blocks.push(new_bb); + } + _ => unimplemented!("Kani can only insert statements after calls."), + }; + } + } } } } diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs b/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs new file mode 100644 index 000000000000..6665ab697287 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs @@ -0,0 +1,428 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +//! Implement a transformation pass that instruments the code to detect possible UB due to +//! the accesses to uninitialized memory. + +use crate::args::ExtraChecks; +use crate::kani_middle::find_fn_def; +use crate::kani_middle::transform::body::{ + CheckType, InsertPosition, MutableBody, SourceInstruction, +}; +use crate::kani_middle::transform::{TransformPass, TransformationType}; +use crate::kani_queries::QueryDb; +use rustc_middle::ty::TyCtxt; +use rustc_smir::rustc_internal; +use stable_mir::mir::mono::Instance; +use stable_mir::mir::{AggregateKind, Body, ConstOperand, Mutability, Operand, Place, Rvalue}; +use stable_mir::ty::{ + FnDef, GenericArgKind, GenericArgs, MirConst, RigidTy, Ty, TyConst, TyKind, UintTy, +}; +use stable_mir::CrateDef; +use std::collections::{HashMap, HashSet}; +use std::fmt::Debug; +use tracing::{debug, trace}; + +mod ty_layout; +mod uninit_visitor; + +pub use ty_layout::{PointeeInfo, PointeeLayout}; +use uninit_visitor::{CheckUninitVisitor, InitRelevantInstruction, MemoryInitOp}; + +const SKIPPED_DIAGNOSTIC_ITEMS: &[&str] = + &["KaniIsUnitPtrInitialized", "KaniSetUnitPtrInitialized"]; + +/// Instrument the code with checks for uninitialized memory. +#[derive(Debug)] +pub struct UninitPass { + pub check_type: CheckType, + /// Used to cache FnDef lookups of injected memory initialization functions. + pub mem_init_fn_cache: HashMap<&'static str, FnDef>, +} + +impl TransformPass for UninitPass { + fn transformation_type() -> TransformationType + where + Self: Sized, + { + TransformationType::Instrumentation + } + + fn is_enabled(&self, query_db: &QueryDb) -> bool + where + Self: Sized, + { + let args = query_db.args(); + args.ub_check.contains(&ExtraChecks::Uninit) + } + + fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + trace!(function=?instance.name(), "transform"); + + // Need to break infinite recursion when shadow memory checks are inserted, + // so the internal function responsible for shadow memory checks are skipped. + if tcx + .get_diagnostic_name(rustc_internal::internal(tcx, instance.def.def_id())) + .map(|diagnostic_name| { + SKIPPED_DIAGNOSTIC_ITEMS.contains(&diagnostic_name.to_ident_string().as_str()) + }) + .unwrap_or(false) + { + return (false, body); + } + + let mut new_body = MutableBody::from(body); + let orig_len = new_body.blocks().len(); + + // Set of basic block indices for which analyzing first statement should be skipped. + // + // This is necessary because some checks are inserted before the source instruction, which, in + // turn, gets moved to the next basic block. Hence, we would not need to look at the + // instruction again as a part of new basic block. However, if the check is inserted after the + // source instruction, we still need to look at the first statement of the new basic block, so + // we need to keep track of which basic blocks were created as a part of injecting checks after + // the source instruction. + let mut skip_first = HashSet::new(); + + // Do not cache body.blocks().len() since it will change as we add new checks. + let mut bb_idx = 0; + while bb_idx < new_body.blocks().len() { + if let Some(candidate) = + CheckUninitVisitor::find_next(&new_body, bb_idx, skip_first.contains(&bb_idx)) + { + self.build_check_for_instruction(tcx, &mut new_body, candidate, &mut skip_first); + bb_idx += 1 + } else { + bb_idx += 1; + }; + } + (orig_len != new_body.blocks().len(), new_body.into()) + } +} + +impl UninitPass { + /// Inject memory initialization checks for each operation in an instruction. + fn build_check_for_instruction( + &mut self, + tcx: TyCtxt, + body: &mut MutableBody, + instruction: InitRelevantInstruction, + skip_first: &mut HashSet, + ) { + debug!(?instruction, "build_check"); + let mut source = instruction.source; + for operation in instruction.before_instruction { + self.build_check_for_operation(tcx, body, &mut source, operation, skip_first); + } + for operation in instruction.after_instruction { + self.build_check_for_operation(tcx, body, &mut source, operation, skip_first); + } + } + + /// Inject memory initialization check for an operation. + fn build_check_for_operation( + &mut self, + tcx: TyCtxt, + body: &mut MutableBody, + source: &mut SourceInstruction, + operation: MemoryInitOp, + skip_first: &mut HashSet, + ) { + if let MemoryInitOp::Unsupported { reason } = &operation { + collect_skipped(&operation, body, skip_first); + self.unsupported_check(tcx, body, source, operation.position(), reason); + return; + }; + + let pointee_ty_info = { + let ptr_operand = operation.mk_operand(body, source); + let ptr_operand_ty = ptr_operand.ty(body.locals()).unwrap(); + let pointee_ty = match ptr_operand_ty.kind() { + TyKind::RigidTy(RigidTy::RawPtr(pointee_ty, _)) => pointee_ty, + _ => { + unreachable!( + "Should only build checks for raw pointers, `{ptr_operand_ty}` encountered." + ) + } + }; + match PointeeInfo::from_ty(pointee_ty) { + Ok(type_info) => type_info, + Err(_) => { + let reason = format!( + "Kani currently doesn't support checking memory initialization for pointers to `{pointee_ty}.", + ); + collect_skipped(&operation, body, skip_first); + self.unsupported_check(tcx, body, source, operation.position(), &reason); + return; + } + } + }; + + match operation { + MemoryInitOp::Check { .. } => { + self.build_get_and_check(tcx, body, source, operation, pointee_ty_info, skip_first) + } + MemoryInitOp::Set { .. } | MemoryInitOp::SetRef { .. } => { + self.build_set(tcx, body, source, operation, pointee_ty_info, skip_first) + } + MemoryInitOp::Unsupported { .. } => { + unreachable!() + } + } + } + + /// Inject a load from shadow memory tracking memory initialization and an assertion that all + /// non-padding bytes are initialized. + fn build_get_and_check( + &mut self, + tcx: TyCtxt, + body: &mut MutableBody, + source: &mut SourceInstruction, + operation: MemoryInitOp, + pointee_info: PointeeInfo, + skip_first: &mut HashSet, + ) { + let ret_place = Place { + local: body.new_local(Ty::bool_ty(), source.span(body.blocks()), Mutability::Not), + projection: vec![], + }; + let ptr_operand = operation.mk_operand(body, source); + match pointee_info.layout() { + PointeeLayout::Sized { layout } => { + let is_ptr_initialized_instance = resolve_mem_init_fn( + get_mem_init_fn_def(tcx, "KaniIsPtrInitialized", &mut self.mem_init_fn_cache), + layout.len(), + *pointee_info.ty(), + ); + let layout_operand = mk_layout_operand(body, source, operation.position(), &layout); + collect_skipped(&operation, body, skip_first); + body.add_call( + &is_ptr_initialized_instance, + source, + operation.position(), + vec![ptr_operand.clone(), layout_operand, operation.expect_count()], + ret_place.clone(), + ); + } + PointeeLayout::Slice { element_layout } => { + // Since `str`` is a separate type, need to differentiate between [T] and str. + let (slicee_ty, diagnostic) = match pointee_info.ty().kind() { + TyKind::RigidTy(RigidTy::Slice(slicee_ty)) => { + (slicee_ty, "KaniIsSlicePtrInitialized") + } + TyKind::RigidTy(RigidTy::Str) => { + (Ty::unsigned_ty(UintTy::U8), "KaniIsStrPtrInitialized") + } + _ => unreachable!(), + }; + let is_ptr_initialized_instance = resolve_mem_init_fn( + get_mem_init_fn_def(tcx, diagnostic, &mut self.mem_init_fn_cache), + element_layout.len(), + slicee_ty, + ); + let layout_operand = + mk_layout_operand(body, source, operation.position(), &element_layout); + collect_skipped(&operation, body, skip_first); + body.add_call( + &is_ptr_initialized_instance, + source, + operation.position(), + vec![ptr_operand.clone(), layout_operand], + ret_place.clone(), + ); + } + PointeeLayout::TraitObject => { + collect_skipped(&operation, body, skip_first); + let reason = "Kani does not support reasoning about memory initialization of pointers to trait objects."; + self.unsupported_check(tcx, body, source, operation.position(), reason); + return; + } + }; + + // Make sure all non-padding bytes are initialized. + collect_skipped(&operation, body, skip_first); + let ptr_operand_ty = ptr_operand.ty(body.locals()).unwrap(); + body.add_check( + tcx, + &self.check_type, + source, + operation.position(), + ret_place.local, + &format!("Undefined Behavior: Reading from an uninitialized pointer of type `{ptr_operand_ty}`"), + ) + } + + /// Inject a store into shadow memory tracking memory initialization to initialize or + /// deinitialize all non-padding bytes. + fn build_set( + &mut self, + tcx: TyCtxt, + body: &mut MutableBody, + source: &mut SourceInstruction, + operation: MemoryInitOp, + pointee_info: PointeeInfo, + skip_first: &mut HashSet, + ) { + let ret_place = Place { + local: body.new_local(Ty::new_tuple(&[]), source.span(body.blocks()), Mutability::Not), + projection: vec![], + }; + let ptr_operand = operation.mk_operand(body, source); + let value = operation.expect_value(); + + match pointee_info.layout() { + PointeeLayout::Sized { layout } => { + let set_ptr_initialized_instance = resolve_mem_init_fn( + get_mem_init_fn_def(tcx, "KaniSetPtrInitialized", &mut self.mem_init_fn_cache), + layout.len(), + *pointee_info.ty(), + ); + let layout_operand = mk_layout_operand(body, source, operation.position(), &layout); + collect_skipped(&operation, body, skip_first); + body.add_call( + &set_ptr_initialized_instance, + source, + operation.position(), + vec![ + ptr_operand, + layout_operand, + operation.expect_count(), + Operand::Constant(ConstOperand { + span: source.span(body.blocks()), + user_ty: None, + const_: MirConst::from_bool(value), + }), + ], + ret_place, + ); + } + PointeeLayout::Slice { element_layout } => { + // Since `str`` is a separate type, need to differentiate between [T] and str. + let (slicee_ty, diagnostic) = match pointee_info.ty().kind() { + TyKind::RigidTy(RigidTy::Slice(slicee_ty)) => { + (slicee_ty, "KaniSetSlicePtrInitialized") + } + TyKind::RigidTy(RigidTy::Str) => { + (Ty::unsigned_ty(UintTy::U8), "KaniSetStrPtrInitialized") + } + _ => unreachable!(), + }; + let set_ptr_initialized_instance = resolve_mem_init_fn( + get_mem_init_fn_def(tcx, diagnostic, &mut self.mem_init_fn_cache), + element_layout.len(), + slicee_ty, + ); + let layout_operand = + mk_layout_operand(body, source, operation.position(), &element_layout); + collect_skipped(&operation, body, skip_first); + body.add_call( + &set_ptr_initialized_instance, + source, + operation.position(), + vec![ + ptr_operand, + layout_operand, + Operand::Constant(ConstOperand { + span: source.span(body.blocks()), + user_ty: None, + const_: MirConst::from_bool(value), + }), + ], + ret_place, + ); + } + PointeeLayout::TraitObject => { + unreachable!("Cannot change the initialization state of a trait object directly."); + } + }; + } + + fn unsupported_check( + &self, + tcx: TyCtxt, + body: &mut MutableBody, + source: &mut SourceInstruction, + position: InsertPosition, + 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.new_assignment(rvalue, source, position); + body.add_check(tcx, &self.check_type, source, position, result, reason); + } +} + +/// Create an operand from a bit array that represents a byte mask for a type layout where padding +/// bytes are marked as `false` and data bytes are marked as `true`. +/// +/// For example, the layout for: +/// ``` +/// [repr(C)] +/// struct { +/// a: u16, +/// b: u8 +/// } +/// ``` +/// will have the following byte mask `[true, true, true, false]`. +pub fn mk_layout_operand( + body: &mut MutableBody, + source: &mut SourceInstruction, + position: InsertPosition, + layout_byte_mask: &[bool], +) -> Operand { + Operand::Move(Place { + local: body.new_assignment( + Rvalue::Aggregate( + AggregateKind::Array(Ty::bool_ty()), + layout_byte_mask + .iter() + .map(|byte| { + Operand::Constant(ConstOperand { + span: source.span(body.blocks()), + user_ty: None, + const_: MirConst::from_bool(*byte), + }) + }) + .collect(), + ), + source, + position, + ), + projection: vec![], + }) +} + +/// If injecting a new call to the function before the current statement, need to skip the original +/// statement when analyzing it as a part of the new basic block. +fn collect_skipped(operation: &MemoryInitOp, body: &MutableBody, skip_first: &mut HashSet) { + if operation.position() == InsertPosition::Before { + let new_bb_idx = body.blocks().len(); + skip_first.insert(new_bb_idx); + } +} + +/// Retrieve a function definition by diagnostic string, caching the result. +pub fn get_mem_init_fn_def( + tcx: TyCtxt, + diagnostic: &'static str, + cache: &mut HashMap<&'static str, FnDef>, +) -> FnDef { + let entry = cache.entry(diagnostic).or_insert_with(|| find_fn_def(tcx, diagnostic).unwrap()); + *entry +} + +/// Resolves a given memory initialization function with passed type parameters. +pub fn resolve_mem_init_fn(fn_def: FnDef, layout_size: usize, associated_type: Ty) -> Instance { + Instance::resolve( + fn_def, + &GenericArgs(vec![ + GenericArgKind::Const(TyConst::try_from_target_usize(layout_size as u64).unwrap()), + GenericArgKind::Type(associated_type), + ]), + ) + .unwrap() +} 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 new file mode 100644 index 000000000000..09116230af80 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/check_uninit/ty_layout.rs @@ -0,0 +1,334 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +//! Utility functions that help calculate type layout. + +use stable_mir::abi::{FieldsShape, Scalar, TagEncoding, ValueAbi, VariantsShape}; +use stable_mir::target::{MachineInfo, MachineSize}; +use stable_mir::ty::{AdtKind, IndexedVal, RigidTy, Ty, TyKind, UintTy}; +use stable_mir::CrateDef; + +/// Represents a chunk of data bytes in a data structure. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +struct DataBytes { + /// Offset in bytes. + offset: usize, + /// Size of this data chunk. + size: MachineSize, +} + +/// Bytewise mask, representing which bytes of a type are data and which are padding. Here, `false` +/// represents padding bytes and `true` represents data bytes. +type Layout = Vec; + +/// Create a byte-wise mask from known chunks of data bytes. +fn generate_byte_mask(size_in_bytes: usize, data_chunks: Vec) -> Vec { + let mut layout_mask = vec![false; size_in_bytes]; + for data_bytes in data_chunks.iter() { + for layout_item in + layout_mask.iter_mut().skip(data_bytes.offset).take(data_bytes.size.bytes()) + { + *layout_item = true; + } + } + layout_mask +} + +// Depending on whether the type is statically or dynamically sized, +// the layout of the element or the layout of the actual type is returned. +pub enum PointeeLayout { + /// Layout of sized objects. + Sized { layout: Layout }, + /// Layout of slices, *const/mut str is included in this case and treated as *const/mut [u8]. + Slice { element_layout: Layout }, + /// Trait objects have an arbitrary layout. + TraitObject, +} + +pub struct PointeeInfo { + pointee_ty: Ty, + layout: PointeeLayout, +} + +impl PointeeInfo { + pub fn from_ty(ty: Ty) -> Result { + match ty.kind() { + TyKind::RigidTy(rigid_ty) => match rigid_ty { + RigidTy::Str => { + let slicee_ty = Ty::unsigned_ty(UintTy::U8); + let size_in_bytes = slicee_ty.layout().unwrap().shape().size.bytes(); + let data_chunks = data_bytes_for_ty(&MachineInfo::target(), slicee_ty, 0)?; + let layout = PointeeLayout::Slice { + element_layout: generate_byte_mask(size_in_bytes, data_chunks), + }; + Ok(PointeeInfo { pointee_ty: ty, layout }) + } + RigidTy::Slice(slicee_ty) => { + let size_in_bytes = slicee_ty.layout().unwrap().shape().size.bytes(); + let data_chunks = data_bytes_for_ty(&MachineInfo::target(), slicee_ty, 0)?; + let layout = PointeeLayout::Slice { + element_layout: generate_byte_mask(size_in_bytes, data_chunks), + }; + Ok(PointeeInfo { pointee_ty: ty, layout }) + } + RigidTy::Dynamic(..) => { + Ok(PointeeInfo { pointee_ty: ty, layout: PointeeLayout::TraitObject }) + } + _ => { + if ty.layout().unwrap().shape().is_sized() { + let size_in_bytes = ty.layout().unwrap().shape().size.bytes(); + let data_chunks = data_bytes_for_ty(&MachineInfo::target(), ty, 0)?; + let layout = PointeeLayout::Sized { + layout: generate_byte_mask(size_in_bytes, data_chunks), + }; + Ok(PointeeInfo { pointee_ty: ty, layout }) + } else { + Err(format!("Cannot determine type layout for type `{ty}`")) + } + } + }, + TyKind::Alias(..) | TyKind::Param(..) | TyKind::Bound(..) => { + unreachable!("Should only encounter monomorphized types at this point.") + } + } + } + + pub fn ty(&self) -> &Ty { + &self.pointee_ty + } + + pub fn layout(&self) -> &PointeeLayout { + &self.layout + } +} + +/// Retrieve a set of data bytes with offsets for a type. +fn data_bytes_for_ty( + machine_info: &MachineInfo, + ty: Ty, + current_offset: usize, +) -> Result, String> { + let layout = ty.layout().unwrap().shape(); + + match layout.fields { + FieldsShape::Primitive => Ok(vec![match layout.abi { + ValueAbi::Scalar(Scalar::Initialized { value, .. }) => { + DataBytes { offset: current_offset, size: value.size(machine_info) } + } + _ => unreachable!("FieldsShape::Primitive with a different ABI than ValueAbi::Scalar"), + }]), + FieldsShape::Array { stride, count } if count > 0 => { + let TyKind::RigidTy(RigidTy::Array(elem_ty, _)) = ty.kind() else { unreachable!() }; + let elem_data_bytes = data_bytes_for_ty(machine_info, elem_ty, current_offset)?; + let mut result = vec![]; + if !elem_data_bytes.is_empty() { + for idx in 0..count { + let idx: usize = idx.try_into().unwrap(); + let elem_offset = idx * stride.bytes(); + let mut next_data_bytes = elem_data_bytes + .iter() + .cloned() + .map(|mut req| { + req.offset += elem_offset; + req + }) + .collect::>(); + result.append(&mut next_data_bytes) + } + } + Ok(result) + } + FieldsShape::Arbitrary { ref offsets } => { + match ty.kind().rigid().expect(&format!("unexpected type: {ty:?}")) { + RigidTy::Adt(def, args) => { + match def.kind() { + AdtKind::Enum => { + // Support basic enumeration forms + let ty_variants = def.variants(); + match layout.variants { + VariantsShape::Single { index } => { + // Only one variant is reachable. This behaves like a struct. + let fields = ty_variants[index.to_index()].fields(); + 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); + fields_data_bytes.append(&mut data_bytes_for_ty( + machine_info, + field_ty, + field_offset + current_offset, + )?); + } + Ok(fields_data_bytes) + } + VariantsShape::Multiple { + tag_encoding: TagEncoding::Niche { .. }, + .. + } => { + Err(format!("Unsupported Enum `{}` check", def.trimmed_name()))? + } + VariantsShape::Multiple { variants, tag, .. } => { + // Retrieve data bytes for the tag. + let tag_size = match tag { + Scalar::Initialized { value, .. } => { + value.size(&machine_info) + } + Scalar::Union { .. } => { + unreachable!("Enum tag should not be a union.") + } + }; + // For enums, tag is the only field and should have offset of 0. + assert!(offsets.len() == 1 && offsets[0].bytes() == 0); + let tag_data_bytes = + vec![DataBytes { offset: current_offset, size: tag_size }]; + + // Retrieve data bytes for the fields. + let mut fields_data_bytes = vec![]; + // Iterate over all variants for the enum. + for (index, variant) in variants.iter().enumerate() { + let mut field_data_bytes_for_variant = vec![]; + let fields = ty_variants[index].fields(); + // Get offsets of all fields in a variant. + let FieldsShape::Arbitrary { offsets: field_offsets } = + variant.fields.clone() + else { + unreachable!() + }; + 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); + field_data_bytes_for_variant.append( + &mut data_bytes_for_ty( + machine_info, + field_ty, + field_offset + current_offset, + )?, + ); + } + fields_data_bytes.push(field_data_bytes_for_variant); + } + + if fields_data_bytes.is_empty() { + // If there are no fields, return the tag data bytes. + Ok(tag_data_bytes) + } else if fields_data_bytes.iter().all( + |data_bytes_for_variant| { + // Byte layout for variant N. + let byte_mask_for_variant = generate_byte_mask( + layout.size.bytes(), + data_bytes_for_variant.clone(), + ); + // Byte layout for variant 0. + let byte_mask_for_first = generate_byte_mask( + layout.size.bytes(), + fields_data_bytes.first().unwrap().clone(), + ); + byte_mask_for_variant == byte_mask_for_first + }, + ) { + // If all fields have the same layout, return fields data + // bytes. + let mut total_data_bytes = tag_data_bytes; + let mut field_data_bytes = + fields_data_bytes.first().unwrap().clone(); + total_data_bytes.append(&mut field_data_bytes); + Ok(total_data_bytes) + } else { + // Struct has multiple padding variants, Kani cannot + // differentiate between them. + Err(format!( + "Unsupported Enum `{}` check", + def.trimmed_name() + )) + } + } + } + } + AdtKind::Union => unreachable!(), + AdtKind::Struct => { + let mut struct_data_bytes = vec![]; + 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); + struct_data_bytes.append(&mut data_bytes_for_ty( + machine_info, + field_ty, + field_offset + current_offset, + )?); + } + Ok(struct_data_bytes) + } + } + } + RigidTy::Pat(base_ty, ..) => { + // This is similar to a structure with one field and with niche defined. + let mut pat_data_bytes = vec![]; + pat_data_bytes.append(&mut data_bytes_for_ty(machine_info, *base_ty, 0)?); + Ok(pat_data_bytes) + } + RigidTy::Tuple(tys) => { + let mut tuple_data_bytes = vec![]; + for idx in layout.fields.fields_by_offset_order() { + let field_offset = offsets[idx].bytes(); + let field_ty = tys[idx]; + tuple_data_bytes.append(&mut data_bytes_for_ty( + machine_info, + field_ty, + field_offset + current_offset, + )?); + } + Ok(tuple_data_bytes) + } + RigidTy::Bool + | RigidTy::Char + | RigidTy::Int(_) + | RigidTy::Uint(_) + | RigidTy::Float(_) + | RigidTy::Never => { + unreachable!("Expected primitive layout for {ty:?}") + } + RigidTy::Str | RigidTy::Slice(_) | RigidTy::Array(_, _) => { + unreachable!("Expected array layout for {ty:?}") + } + RigidTy::RawPtr(_, _) | RigidTy::Ref(_, _, _) => Ok(match layout.abi { + ValueAbi::Scalar(Scalar::Initialized { value, .. }) => { + // Thin pointer, ABI is a single scalar. + vec![DataBytes { offset: current_offset, size: value.size(machine_info) }] + } + ValueAbi::ScalarPair( + Scalar::Initialized { value: value_first, .. }, + Scalar::Initialized { value: value_second, .. }, + ) => { + // Fat pointer, ABI is a scalar pair. + let FieldsShape::Arbitrary { offsets } = layout.fields else { + unreachable!() + }; + // Since this is a scalar pair, only 2 elements are in the offsets vec. + assert!(offsets.len() == 2); + vec![ + DataBytes { + offset: current_offset + offsets[0].bytes(), + size: value_first.size(machine_info), + }, + DataBytes { + offset: current_offset + offsets[1].bytes(), + size: value_second.size(machine_info), + }, + ] + } + _ => unreachable!("RigidTy::RawPtr | RigidTy::Ref with a non-scalar ABI."), + }), + RigidTy::FnDef(_, _) + | RigidTy::FnPtr(_) + | RigidTy::Closure(_, _) + | RigidTy::Coroutine(_, _, _) + | RigidTy::CoroutineWitness(_, _) + | RigidTy::Foreign(_) + | RigidTy::Dynamic(_, _, _) => Err(format!("Unsupported {ty:?}")), + } + } + FieldsShape::Union(_) => Err(format!("Unsupported {ty:?}")), + FieldsShape::Array { .. } => Ok(vec![]), + } +} diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs b/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs new file mode 100644 index 000000000000..19b13c6ab8b6 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs @@ -0,0 +1,713 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +//! Visitor that collects all instructions relevant to uninitialized memory access. + +use crate::kani_middle::transform::body::{InsertPosition, MutableBody, SourceInstruction}; +use stable_mir::mir::alloc::GlobalAlloc; +use stable_mir::mir::mono::{Instance, InstanceKind}; +use stable_mir::mir::visit::{Location, PlaceContext}; +use stable_mir::mir::{ + BasicBlockIdx, CastKind, ConstOperand, LocalDecl, MirVisitor, Mutability, + NonDivergingIntrinsic, Operand, Place, PointerCoercion, ProjectionElem, Rvalue, Statement, + StatementKind, Terminator, TerminatorKind, +}; +use stable_mir::ty::{ConstantKind, MirConst, RigidTy, Span, TyKind, UintTy}; +use strum_macros::AsRefStr; + +/// Memory initialization operations: set or get memory initialization state for a given pointer. +#[derive(AsRefStr, Clone, Debug)] +pub enum MemoryInitOp { + /// Check memory initialization of data bytes in a memory region starting from the pointer + /// `operand` and of length `count * sizeof(operand)` bytes. + Check { operand: Operand, count: Operand }, + /// Set memory initialization state of data bytes in a memory region starting from the pointer + /// `operand` and of length `count * sizeof(operand)` bytes. + Set { operand: Operand, count: Operand, value: bool, position: InsertPosition }, + /// Set memory initialization of data bytes in a memory region starting from the reference to + /// `operand` and of length `count * sizeof(operand)` bytes. + SetRef { operand: Operand, count: Operand, value: bool, position: InsertPosition }, + /// Unsupported memory initialization operation. + Unsupported { reason: String }, +} + +impl MemoryInitOp { + /// Produce an operand for the relevant memory initialization related operation. This is mostly + /// required so that the analysis can create a new local to take a reference in + /// `MemoryInitOp::SetRef`. + pub fn mk_operand(&self, body: &mut MutableBody, source: &mut SourceInstruction) -> Operand { + match self { + MemoryInitOp::Check { operand, .. } | MemoryInitOp::Set { operand, .. } => { + operand.clone() + } + MemoryInitOp::SetRef { operand, .. } => Operand::Copy(Place { + local: { + let place = match operand { + Operand::Copy(place) | Operand::Move(place) => place, + Operand::Constant(_) => unreachable!(), + }; + body.new_assignment( + Rvalue::AddressOf(Mutability::Not, place.clone()), + source, + self.position(), + ) + }, + projection: vec![], + }), + MemoryInitOp::Unsupported { .. } => unreachable!(), + } + } + + pub fn expect_count(&self) -> Operand { + match self { + MemoryInitOp::Check { count, .. } + | MemoryInitOp::Set { count, .. } + | MemoryInitOp::SetRef { count, .. } => count.clone(), + MemoryInitOp::Unsupported { .. } => unreachable!(), + } + } + + pub fn expect_value(&self) -> bool { + match self { + MemoryInitOp::Set { value, .. } | MemoryInitOp::SetRef { value, .. } => *value, + MemoryInitOp::Check { .. } | MemoryInitOp::Unsupported { .. } => unreachable!(), + } + } + + pub fn position(&self) -> InsertPosition { + match self { + MemoryInitOp::Set { position, .. } | MemoryInitOp::SetRef { position, .. } => *position, + MemoryInitOp::Check { .. } | MemoryInitOp::Unsupported { .. } => InsertPosition::Before, + } + } +} + +/// Represents an instruction in the source code together with all memory initialization checks/sets +/// that are connected to the memory used in this instruction and whether they should be inserted +/// before or after the instruction. +#[derive(Clone, Debug)] +pub struct InitRelevantInstruction { + /// The instruction that affects the state of the memory. + pub source: SourceInstruction, + /// All memory-related operations that should happen after the instruction. + pub before_instruction: Vec, + /// All memory-related operations that should happen after the instruction. + pub after_instruction: Vec, +} + +impl InitRelevantInstruction { + pub fn push_operation(&mut self, source_op: MemoryInitOp) { + match source_op.position() { + InsertPosition::Before => self.before_instruction.push(source_op), + InsertPosition::After => self.after_instruction.push(source_op), + } + } +} + +pub struct CheckUninitVisitor<'a> { + locals: &'a [LocalDecl], + /// Whether we should skip the next instruction, since it might've been instrumented already. + /// When we instrument an instruction, we partition the basic block, and the instruction that + /// may trigger UB becomes the first instruction of the basic block, which we need to skip + /// later. + skip_next: bool, + /// The instruction being visited at a given point. + current: SourceInstruction, + /// The target instruction that should be verified. + pub target: Option, + /// The basic block being visited. + bb: BasicBlockIdx, +} + +impl<'a> CheckUninitVisitor<'a> { + pub fn find_next( + body: &'a MutableBody, + bb: BasicBlockIdx, + skip_first: bool, + ) -> Option { + let mut visitor = CheckUninitVisitor { + locals: body.locals(), + skip_next: skip_first, + current: SourceInstruction::Statement { idx: 0, bb }, + target: None, + bb, + }; + visitor.visit_basic_block(&body.blocks()[bb]); + visitor.target + } + + fn push_target(&mut self, source_op: MemoryInitOp) { + let target = self.target.get_or_insert_with(|| InitRelevantInstruction { + source: self.current, + after_instruction: vec![], + before_instruction: vec![], + }); + target.push_operation(source_op); + } +} + +impl<'a> MirVisitor for CheckUninitVisitor<'a> { + fn visit_statement(&mut self, stmt: &Statement, location: Location) { + if self.skip_next { + self.skip_next = false; + } else if self.target.is_none() { + // Leave it as an exhaustive match to be notified when a new kind is added. + match &stmt.kind { + StatementKind::Intrinsic(NonDivergingIntrinsic::CopyNonOverlapping(copy)) => { + self.super_statement(stmt, location); + // Source is a *const T and it must be initialized. + self.push_target(MemoryInitOp::Check { + operand: copy.src.clone(), + count: copy.count.clone(), + }); + // Destimation is a *mut T so it gets initialized. + self.push_target(MemoryInitOp::Set { + operand: copy.dst.clone(), + count: copy.count.clone(), + value: true, + position: InsertPosition::After, + }); + } + StatementKind::Assign(place, rvalue) => { + // First check rvalue. + self.visit_rvalue(rvalue, location); + // Check whether we are assigning into a dereference (*ptr = _). + if let Some(place_without_deref) = try_remove_topmost_deref(place) { + // First, check that we are not dereferencing extra pointers along the way + // (e.g., **ptr = _). If yes, check whether these pointers are initialized. + let mut place_to_add_projections = + Place { local: place_without_deref.local, projection: vec![] }; + 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(..)) = + place_to_add_projections.ty(&self.locals).unwrap().kind() + { + self.push_target(MemoryInitOp::Check { + operand: Operand::Copy(place_to_add_projections.clone()), + count: mk_const_operand(1, location.span()), + }); + }; + } + place_to_add_projections.projection.push(projection_elem.clone()); + } + if place_without_deref.ty(&self.locals).unwrap().kind().is_raw_ptr() { + self.push_target(MemoryInitOp::Set { + operand: Operand::Copy(place_without_deref), + count: mk_const_operand(1, location.span()), + value: true, + position: InsertPosition::After, + }); + } + } + // 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()), + count: mk_const_operand(1, location.span()), + value: true, + position: InsertPosition::After, + }); + } + } + } + StatementKind::Deinit(place) => { + self.super_statement(stmt, location); + self.push_target(MemoryInitOp::Set { + operand: Operand::Copy(place.clone()), + count: mk_const_operand(1, location.span()), + value: false, + position: InsertPosition::After, + }); + } + StatementKind::FakeRead(_, _) + | StatementKind::SetDiscriminant { .. } + | StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Retag(_, _) + | StatementKind::PlaceMention(_) + | StatementKind::AscribeUserType { .. } + | StatementKind::Coverage(_) + | StatementKind::ConstEvalCounter + | StatementKind::Intrinsic(NonDivergingIntrinsic::Assume(_)) + | StatementKind::Nop => self.super_statement(stmt, location), + } + } + let SourceInstruction::Statement { idx, bb } = self.current else { unreachable!() }; + self.current = SourceInstruction::Statement { idx: idx + 1, bb }; + } + + fn visit_terminator(&mut self, term: &Terminator, location: Location) { + if !(self.skip_next || self.target.is_some()) { + self.current = SourceInstruction::Terminator { bb: self.bb }; + // Leave it as an exhaustive match to be notified when a new kind is added. + match &term.kind { + TerminatorKind::Call { func, args, destination, .. } => { + self.super_terminator(term, location); + let instance = match try_resolve_instance(self.locals, func) { + Ok(instance) => instance, + Err(reason) => { + self.super_terminator(term, location); + self.push_target(MemoryInitOp::Unsupported { reason }); + return; + } + }; + match instance.kind { + InstanceKind::Intrinsic => { + match instance.intrinsic_name().unwrap().as_str() { + intrinsic_name if can_skip_intrinsic(intrinsic_name) => { + /* Intrinsics that can be safely skipped */ + } + name if name.starts_with("atomic") => { + let num_args = + if name.starts_with("atomic_cxchg") { 3 } else { 2 }; + assert_eq!( + args.len(), + num_args, + "Unexpected number of arguments for `{name}`" + ); + assert!(matches!( + args[0].ty(self.locals).unwrap().kind(), + TyKind::RigidTy(RigidTy::RawPtr(..)) + )); + self.push_target(MemoryInitOp::Check { + operand: args[0].clone(), + count: mk_const_operand(1, location.span()), + }); + } + "compare_bytes" => { + assert_eq!( + args.len(), + 3, + "Unexpected number of arguments for `compare_bytes`" + ); + assert!(matches!( + args[0].ty(self.locals).unwrap().kind(), + TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Not)) + )); + assert!(matches!( + args[1].ty(self.locals).unwrap().kind(), + TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Not)) + )); + self.push_target(MemoryInitOp::Check { + operand: args[0].clone(), + count: args[2].clone(), + }); + self.push_target(MemoryInitOp::Check { + operand: args[1].clone(), + count: args[2].clone(), + }); + } + "copy" + | "volatile_copy_memory" + | "volatile_copy_nonoverlapping_memory" => { + assert_eq!( + args.len(), + 3, + "Unexpected number of arguments for `copy`" + ); + assert!(matches!( + args[0].ty(self.locals).unwrap().kind(), + TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Not)) + )); + assert!(matches!( + args[1].ty(self.locals).unwrap().kind(), + TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Mut)) + )); + self.push_target(MemoryInitOp::Check { + operand: args[0].clone(), + count: args[2].clone(), + }); + self.push_target(MemoryInitOp::Set { + operand: args[1].clone(), + count: args[2].clone(), + value: true, + position: InsertPosition::After, + }); + } + "typed_swap" => { + assert_eq!( + args.len(), + 2, + "Unexpected number of arguments for `typed_swap`" + ); + assert!(matches!( + args[0].ty(self.locals).unwrap().kind(), + TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Mut)) + )); + assert!(matches!( + args[1].ty(self.locals).unwrap().kind(), + TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Mut)) + )); + self.push_target(MemoryInitOp::Check { + operand: args[0].clone(), + count: mk_const_operand(1, location.span()), + }); + self.push_target(MemoryInitOp::Check { + operand: args[1].clone(), + count: mk_const_operand(1, location.span()), + }); + } + "unaligned_volatile_load" => { + assert_eq!( + args.len(), + 1, + "Unexpected number of arguments for `unaligned_volatile_load`" + ); + assert!(matches!( + args[0].ty(self.locals).unwrap().kind(), + TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Not)) + )); + self.push_target(MemoryInitOp::Check { + operand: args[0].clone(), + count: mk_const_operand(1, location.span()), + }); + } + "volatile_load" => { + assert_eq!( + args.len(), + 1, + "Unexpected number of arguments for `volatile_load`" + ); + assert!(matches!( + args[0].ty(self.locals).unwrap().kind(), + TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Not)) + )); + self.push_target(MemoryInitOp::Check { + operand: args[0].clone(), + count: mk_const_operand(1, location.span()), + }); + } + "volatile_store" => { + assert_eq!( + args.len(), + 2, + "Unexpected number of arguments for `volatile_store`" + ); + assert!(matches!( + args[0].ty(self.locals).unwrap().kind(), + TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Mut)) + )); + self.push_target(MemoryInitOp::Set { + operand: args[0].clone(), + count: mk_const_operand(1, location.span()), + value: true, + position: InsertPosition::After, + }); + } + "write_bytes" => { + assert_eq!( + args.len(), + 3, + "Unexpected number of arguments for `write_bytes`" + ); + assert!(matches!( + args[0].ty(self.locals).unwrap().kind(), + TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Mut)) + )); + self.push_target(MemoryInitOp::Set { + operand: args[0].clone(), + count: args[2].clone(), + value: true, + position: InsertPosition::After, + }) + } + intrinsic => { + self.push_target(MemoryInitOp::Unsupported { + reason: format!("Kani does not support reasoning about memory initialization of intrinsic `{intrinsic}`."), + }); + } + } + } + InstanceKind::Item => { + if instance.is_foreign_item() { + match instance.name().as_str() { + "alloc::alloc::__rust_alloc" + | "alloc::alloc::__rust_realloc" => { + /* Memory is uninitialized, nothing to do here. */ + } + "alloc::alloc::__rust_alloc_zeroed" => { + /* Memory is initialized here, need to update shadow memory. */ + self.push_target(MemoryInitOp::Set { + operand: Operand::Copy(destination.clone()), + count: args[0].clone(), + value: true, + position: InsertPosition::After, + }); + } + "alloc::alloc::__rust_dealloc" => { + /* Memory is uninitialized here, need to update shadow memory. */ + self.push_target(MemoryInitOp::Set { + operand: args[0].clone(), + count: args[1].clone(), + value: false, + position: InsertPosition::After, + }); + } + _ => {} + } + } + } + _ => {} + } + } + TerminatorKind::Drop { place, .. } => { + self.super_terminator(term, location); + let place_ty = place.ty(&self.locals).unwrap(); + // When drop is codegen'ed, a reference is taken to the place which is later implicitly coerced to a pointer. + // Hence, we need to bless this pointer as initialized. + self.push_target(MemoryInitOp::SetRef { + operand: Operand::Copy(place.clone()), + count: mk_const_operand(1, location.span()), + value: true, + position: InsertPosition::Before, + }); + if place_ty.kind().is_raw_ptr() { + self.push_target(MemoryInitOp::Set { + operand: Operand::Copy(place.clone()), + count: mk_const_operand(1, location.span()), + value: false, + position: InsertPosition::After, + }); + } + } + TerminatorKind::Goto { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Assert { .. } + | TerminatorKind::InlineAsm { .. } => self.super_terminator(term, location), + } + } + } + + fn visit_place(&mut self, place: &Place, ptx: PlaceContext, location: Location) { + for (idx, elem) in place.projection.iter().enumerate() { + let intermediate_place = + Place { local: place.local, projection: place.projection[..idx].to_vec() }; + match elem { + ProjectionElem::Deref => { + let ptr_ty = intermediate_place.ty(self.locals).unwrap(); + if ptr_ty.kind().is_raw_ptr() { + self.push_target(MemoryInitOp::Check { + operand: Operand::Copy(intermediate_place.clone()), + count: mk_const_operand(1, location.span()), + }); + } + } + ProjectionElem::Field(idx, target_ty) => { + if target_ty.kind().is_union() + && (!ptx.is_mutating() || place.projection.len() > idx + 1) + { + self.push_target(MemoryInitOp::Unsupported { + reason: "Kani does not support reasoning about memory initialization of unions.".to_string(), + }); + } + } + ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } => { + /* For a slice to be indexed, it should be valid first. */ + } + ProjectionElem::Downcast(_) => {} + ProjectionElem::OpaqueCast(_) => {} + ProjectionElem::Subtype(_) => {} + } + } + self.super_place(place, ptx, location) + } + + 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) { + self.push_target(MemoryInitOp::Set { + operand: Operand::Constant(constant.clone()), + count: mk_const_operand(1, location.span()), + value: true, + position: InsertPosition::Before, + }); + }; + } + } + } + self.super_operand(operand, location); + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue, location: Location) { + if let Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), _, ty) = rvalue { + if let TyKind::RigidTy(RigidTy::RawPtr(pointee_ty, _)) = ty.kind() { + if pointee_ty.kind().is_trait() { + self.push_target(MemoryInitOp::Unsupported { + reason: "Kani does not support reasoning about memory initialization of unsized pointers.".to_string(), + }); + } + } + }; + self.super_rvalue(rvalue, location); + } +} + +/// Determines if the intrinsic has no memory initialization related function and hence can be +/// safely skipped. +fn can_skip_intrinsic(intrinsic_name: &str) -> bool { + match intrinsic_name { + "add_with_overflow" + | "arith_offset" + | "assert_inhabited" + | "assert_mem_uninitialized_valid" + | "assert_zero_valid" + | "assume" + | "bitreverse" + | "black_box" + | "breakpoint" + | "bswap" + | "caller_location" + | "ceilf32" + | "ceilf64" + | "copysignf32" + | "copysignf64" + | "cosf32" + | "cosf64" + | "ctlz" + | "ctlz_nonzero" + | "ctpop" + | "cttz" + | "cttz_nonzero" + | "discriminant_value" + | "exact_div" + | "exp2f32" + | "exp2f64" + | "expf32" + | "expf64" + | "fabsf32" + | "fabsf64" + | "fadd_fast" + | "fdiv_fast" + | "floorf32" + | "floorf64" + | "fmaf32" + | "fmaf64" + | "fmul_fast" + | "forget" + | "fsub_fast" + | "is_val_statically_known" + | "likely" + | "log10f32" + | "log10f64" + | "log2f32" + | "log2f64" + | "logf32" + | "logf64" + | "maxnumf32" + | "maxnumf64" + | "min_align_of" + | "min_align_of_val" + | "minnumf32" + | "minnumf64" + | "mul_with_overflow" + | "nearbyintf32" + | "nearbyintf64" + | "needs_drop" + | "powf32" + | "powf64" + | "powif32" + | "powif64" + | "pref_align_of" + | "raw_eq" + | "rintf32" + | "rintf64" + | "rotate_left" + | "rotate_right" + | "roundf32" + | "roundf64" + | "saturating_add" + | "saturating_sub" + | "sinf32" + | "sinf64" + | "sqrtf32" + | "sqrtf64" + | "sub_with_overflow" + | "truncf32" + | "truncf64" + | "type_id" + | "type_name" + | "unchecked_div" + | "unchecked_rem" + | "unlikely" + | "vtable_size" + | "vtable_align" + | "wrapping_add" + | "wrapping_mul" + | "wrapping_sub" => { + /* Intrinsics that do not interact with memory initialization. */ + true + } + "ptr_guaranteed_cmp" | "ptr_offset_from" | "ptr_offset_from_unsigned" | "size_of_val" => { + /* AFAICS from the documentation, none of those require the pointer arguments to be actually initialized. */ + true + } + name if name.starts_with("simd") => { + /* SIMD operations */ + true + } + "copy_nonoverlapping" => unreachable!( + "Expected `core::intrinsics::unreachable` to be handled by `StatementKind::CopyNonOverlapping`" + ), + "offset" => unreachable!( + "Expected `core::intrinsics::unreachable` to be handled by `BinOp::OffSet`" + ), + "unreachable" => unreachable!( + "Expected `std::intrinsics::unreachable` to be handled by `TerminatorKind::Unreachable`" + ), + "transmute" | "transmute_copy" | "unchecked_add" | "unchecked_mul" | "unchecked_shl" + | "size_of" | "unchecked_shr" | "unchecked_sub" => { + unreachable!("Expected intrinsic to be lowered before codegen") + } + "catch_unwind" => { + unimplemented!("") + } + "retag_box_to_raw" => { + unreachable!("This was removed in the latest Rust version.") + } + _ => { + /* Everything else */ + false + } + } +} + +/// Create a constant operand with a given value and span. +fn mk_const_operand(value: usize, span: Span) -> Operand { + Operand::Constant(ConstOperand { + span, + user_ty: None, + const_: MirConst::try_from_uint(value as u128, UintTy::Usize).unwrap(), + }) +} + +/// Try removing a topmost deref projection from a place if it exists, returning a place without it. +fn try_remove_topmost_deref(place: &Place) -> Option { + let mut new_place = place.clone(); + if let Some(ProjectionElem::Deref) = new_place.projection.pop() { + Some(new_place) + } else { + None + } +} + +/// Try retrieving instance for the given function operand. +fn try_resolve_instance(locals: &[LocalDecl], func: &Operand) -> Result { + let ty = func.ty(locals).unwrap(); + match ty.kind() { + TyKind::RigidTy(RigidTy::FnDef(def, args)) => Ok(Instance::resolve(def, &args).unwrap()), + _ => Err(format!( + "Kani does not support reasoning about memory initialization of arguments to `{ty:?}`." + )), + } +} diff --git a/kani-compiler/src/kani_middle/transform/check_values.rs b/kani-compiler/src/kani_middle/transform/check_values.rs index 4ecb16fab023..a7d0f14d270f 100644 --- a/kani-compiler/src/kani_middle/transform/check_values.rs +++ b/kani-compiler/src/kani_middle/transform/check_values.rs @@ -14,7 +14,9 @@ //! 1. We could merge the invalid values by the offset. //! 2. We could avoid checking places that have been checked before. use crate::args::ExtraChecks; -use crate::kani_middle::transform::body::{CheckType, MutableBody, SourceInstruction}; +use crate::kani_middle::transform::body::{ + CheckType, InsertPosition, MutableBody, SourceInstruction, +}; use crate::kani_middle::transform::check_values::SourceOp::UnsupportedCheck; use crate::kani_middle::transform::{TransformPass, TransformationType}; use crate::kani_queries::QueryDb; @@ -59,7 +61,7 @@ impl TransformPass for ValidValuePass { /// Transform the function body by inserting checks one-by-one. /// For every unsafe dereference or a transmute operation, we check all values are valid. - fn transform(&self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { trace!(function=?instance.name(), "transform"); let mut new_body = MutableBody::from(body); let orig_len = new_body.blocks().len(); @@ -83,13 +85,20 @@ impl ValidValuePass { for operation in instruction.operations { match operation { SourceOp::BytesValidity { ranges, target_ty, rvalue } => { - let value = body.new_assignment(rvalue, &mut source); + let value = body.new_assignment(rvalue, &mut source, InsertPosition::Before); let rvalue_ptr = Rvalue::AddressOf(Mutability::Not, Place::from(value)); for range in ranges { let result = build_limits(body, &range, rvalue_ptr.clone(), &mut source); let msg = format!("Undefined Behavior: Invalid value of type `{target_ty}`",); - body.add_check(tcx, &self.check_type, &mut source, result, &msg); + body.add_check( + tcx, + &self.check_type, + &mut source, + InsertPosition::Before, + result, + &msg, + ); } } SourceOp::DerefValidity { pointee_ty, rvalue, ranges } => { @@ -97,7 +106,14 @@ impl ValidValuePass { let result = build_limits(body, &range, rvalue.clone(), &mut source); let msg = format!("Undefined Behavior: Invalid value of type `{pointee_ty}`",); - body.add_check(tcx, &self.check_type, &mut source, result, &msg); + body.add_check( + tcx, + &self.check_type, + &mut source, + InsertPosition::Before, + result, + &msg, + ); } } SourceOp::UnsupportedCheck { check, ty } => { @@ -123,8 +139,8 @@ impl ValidValuePass { span, user_ty: None, })); - let result = body.new_assignment(rvalue, source); - body.add_check(tcx, &self.check_type, source, result, reason); + let result = body.new_assignment(rvalue, source, InsertPosition::Before); + body.add_check(tcx, &self.check_type, source, InsertPosition::Before, result, reason); } } @@ -758,30 +774,60 @@ pub fn build_limits( let start_const = body.new_const_operand(req.valid_range.start, primitive_ty, span); let end_const = body.new_const_operand(req.valid_range.end, primitive_ty, span); let orig_ptr = if req.offset != 0 { - let start_ptr = move_local(body.new_assignment(rvalue_ptr, source)); + let start_ptr = move_local(body.new_assignment(rvalue_ptr, source, InsertPosition::Before)); let byte_ptr = move_local(body.new_cast_ptr( start_ptr, Ty::unsigned_ty(UintTy::U8), Mutability::Not, source, + InsertPosition::Before, )); let offset_const = body.new_const_operand(req.offset as _, UintTy::Usize, span); - let offset = move_local(body.new_assignment(Rvalue::Use(offset_const), source)); - move_local(body.new_binary_op(BinOp::Offset, byte_ptr, offset, source)) + let offset = move_local(body.new_assignment( + Rvalue::Use(offset_const), + source, + InsertPosition::Before, + )); + move_local(body.new_binary_op( + BinOp::Offset, + byte_ptr, + offset, + source, + InsertPosition::Before, + )) } else { - move_local(body.new_assignment(rvalue_ptr, source)) + move_local(body.new_assignment(rvalue_ptr, source, InsertPosition::Before)) }; - let value_ptr = - body.new_cast_ptr(orig_ptr, Ty::unsigned_ty(primitive_ty), Mutability::Not, source); + let value_ptr = body.new_cast_ptr( + orig_ptr, + Ty::unsigned_ty(primitive_ty), + Mutability::Not, + source, + InsertPosition::Before, + ); let value = Operand::Copy(Place { local: value_ptr, projection: vec![ProjectionElem::Deref] }); - let start_result = body.new_binary_op(BinOp::Ge, value.clone(), start_const, source); - let end_result = body.new_binary_op(BinOp::Le, value, end_const, source); + let start_result = + body.new_binary_op(BinOp::Ge, value.clone(), start_const, source, InsertPosition::Before); + let end_result = + body.new_binary_op(BinOp::Le, value, end_const, source, InsertPosition::Before); if req.valid_range.wraps_around() { // valid >= start || valid <= end - body.new_binary_op(BinOp::BitOr, move_local(start_result), move_local(end_result), source) + body.new_binary_op( + BinOp::BitOr, + move_local(start_result), + move_local(end_result), + source, + InsertPosition::Before, + ) } else { // valid >= start && valid <= end - body.new_binary_op(BinOp::BitAnd, move_local(start_result), move_local(end_result), source) + body.new_binary_op( + BinOp::BitAnd, + move_local(start_result), + move_local(end_result), + source, + InsertPosition::Before, + ) } } diff --git a/kani-compiler/src/kani_middle/transform/contracts.rs b/kani-compiler/src/kani_middle/transform/contracts.rs index f5760bd4d829..eb5266e0a0eb 100644 --- a/kani-compiler/src/kani_middle/transform/contracts.rs +++ b/kani-compiler/src/kani_middle/transform/contracts.rs @@ -47,7 +47,7 @@ impl TransformPass for AnyModifiesPass { } /// Transform the function body by replacing it with the stub body. - fn transform(&self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { trace!(function=?instance.name(), "AnyModifiesPass::transform"); if instance.def.def_id() == self.kani_any.unwrap().def_id() { diff --git a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs index 53bbd52086ac..ea7bf8625228 100644 --- a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs +++ b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs @@ -7,8 +7,12 @@ //! information; thus, they are implemented as a transformation pass where their body get generated //! by the transformation. +use crate::args::{Arguments, ExtraChecks}; use crate::kani_middle::attributes::matches_diagnostic; -use crate::kani_middle::transform::body::{CheckType, MutableBody, SourceInstruction}; +use crate::kani_middle::transform::body::{ + CheckType, InsertPosition, MutableBody, SourceInstruction, +}; +use crate::kani_middle::transform::check_uninit::PointeeInfo; use crate::kani_middle::transform::check_values::{build_limits, ty_validity_per_offset}; use crate::kani_middle::transform::{TransformPass, TransformationType}; use crate::kani_queries::QueryDb; @@ -18,15 +22,24 @@ use stable_mir::mir::{ BinOp, Body, ConstOperand, Operand, Place, Rvalue, Statement, StatementKind, RETURN_LOCAL, }; use stable_mir::target::MachineInfo; -use stable_mir::ty::{MirConst, RigidTy, TyKind}; +use stable_mir::ty::{FnDef, MirConst, RigidTy, Ty, TyKind, UintTy}; +use std::collections::HashMap; use std::fmt::Debug; use strum_macros::AsRefStr; use tracing::trace; +use super::check_uninit::{ + get_mem_init_fn_def, mk_layout_operand, resolve_mem_init_fn, PointeeLayout, +}; + /// Generate the body for a few Kani intrinsics. #[derive(Debug)] pub struct IntrinsicGeneratorPass { pub check_type: CheckType, + /// Used to cache FnDef lookups of injected memory initialization functions. + pub mem_init_fn_cache: HashMap<&'static str, FnDef>, + /// Used to enable intrinsics depending on the flags passed. + pub arguments: Arguments, } impl TransformPass for IntrinsicGeneratorPass { @@ -46,10 +59,12 @@ impl TransformPass for IntrinsicGeneratorPass { /// Transform the function body by inserting checks one-by-one. /// For every unsafe dereference or a transmute operation, we check all values are valid. - fn transform(&self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { trace!(function=?instance.name(), "transform"); if matches_diagnostic(tcx, instance.def, Intrinsics::KaniValidValue.as_ref()) { (true, self.valid_value_body(tcx, body)) + } else if matches_diagnostic(tcx, instance.def, Intrinsics::KaniIsInitialized.as_ref()) { + (true, self.is_initialized_body(tcx, body)) } else { (false, body) } @@ -86,7 +101,7 @@ impl IntrinsicGeneratorPass { })), ); let stmt = Statement { kind: assign, span }; - new_body.insert_stmt(stmt, &mut terminator); + new_body.insert_stmt(stmt, &mut terminator, InsertPosition::Before); let machine_info = MachineInfo::target(); // The first and only argument type. @@ -110,7 +125,7 @@ impl IntrinsicGeneratorPass { ); let assign = StatementKind::Assign(Place::from(ret_var), rvalue); let stmt = Statement { kind: assign, span }; - new_body.insert_stmt(stmt, &mut terminator); + new_body.insert_stmt(stmt, &mut terminator, InsertPosition::Before); } } Err(msg) => { @@ -120,11 +135,169 @@ impl IntrinsicGeneratorPass { span, user_ty: None, })); - let result = new_body.new_assignment(rvalue, &mut terminator); + let result = + new_body.new_assignment(rvalue, &mut terminator, InsertPosition::Before); let reason = format!( "Kani currently doesn't support checking validity of `{target_ty}`. {msg}" ); - new_body.add_check(tcx, &self.check_type, &mut terminator, result, &reason); + new_body.add_check( + tcx, + &self.check_type, + &mut terminator, + InsertPosition::Before, + result, + &reason, + ); + } + } + new_body.into() + } + + /// Generate the body for `is_initialized`, which looks like the following + /// + /// ``` + /// pub fn is_initialized(ptr: *const T, len: usize) -> bool { + /// let layout = ... // Byte mask representing the layout of T. + /// __kani_mem_init_sm_get(ptr, layout, len) + /// } + /// ``` + fn is_initialized_body(&mut self, tcx: TyCtxt, body: Body) -> Body { + let mut new_body = MutableBody::from(body); + new_body.clear_body(); + + // Initialize return variable with True. + let ret_var = RETURN_LOCAL; + let mut terminator = SourceInstruction::Terminator { bb: 0 }; + let span = new_body.locals()[ret_var].span; + let assign = StatementKind::Assign( + Place::from(ret_var), + Rvalue::Use(Operand::Constant(ConstOperand { + span, + user_ty: None, + const_: MirConst::from_bool(true), + })), + ); + let stmt = Statement { kind: assign, span }; + new_body.insert_stmt(stmt, &mut terminator, InsertPosition::Before); + + if !self.arguments.ub_check.contains(&ExtraChecks::Uninit) { + // Short-circut if uninitialized memory checks are not enabled. + return new_body.into(); + } + + // The first argument type. + let arg_ty = new_body.locals()[1].ty; + let TyKind::RigidTy(RigidTy::RawPtr(target_ty, _)) = arg_ty.kind() else { unreachable!() }; + let pointee_info = PointeeInfo::from_ty(target_ty); + match pointee_info { + Ok(pointee_info) => { + match pointee_info.layout() { + PointeeLayout::Sized { layout } => { + if layout.is_empty() { + // Encountered a ZST, so we can short-circut here. + return new_body.into(); + } + let is_ptr_initialized_instance = resolve_mem_init_fn( + get_mem_init_fn_def( + tcx, + "KaniIsPtrInitialized", + &mut self.mem_init_fn_cache, + ), + layout.len(), + *pointee_info.ty(), + ); + let layout_operand = mk_layout_operand( + &mut new_body, + &mut terminator, + InsertPosition::Before, + &layout, + ); + new_body.add_call( + &is_ptr_initialized_instance, + &mut terminator, + InsertPosition::Before, + vec![ + Operand::Copy(Place::from(1)), + layout_operand, + Operand::Copy(Place::from(2)), + ], + Place::from(ret_var), + ); + } + PointeeLayout::Slice { element_layout } => { + // Since `str`` is a separate type, need to differentiate between [T] and str. + let (slicee_ty, diagnostic) = match pointee_info.ty().kind() { + TyKind::RigidTy(RigidTy::Slice(slicee_ty)) => { + (slicee_ty, "KaniIsSlicePtrInitialized") + } + TyKind::RigidTy(RigidTy::Str) => { + (Ty::unsigned_ty(UintTy::U8), "KaniIsStrPtrInitialized") + } + _ => unreachable!(), + }; + let is_ptr_initialized_instance = resolve_mem_init_fn( + get_mem_init_fn_def(tcx, diagnostic, &mut self.mem_init_fn_cache), + element_layout.len(), + slicee_ty, + ); + let layout_operand = mk_layout_operand( + &mut new_body, + &mut terminator, + InsertPosition::Before, + &element_layout, + ); + new_body.add_call( + &is_ptr_initialized_instance, + &mut terminator, + InsertPosition::Before, + vec![Operand::Copy(Place::from(1)), layout_operand], + Place::from(ret_var), + ); + } + PointeeLayout::TraitObject => { + let rvalue = Rvalue::Use(Operand::Constant(ConstOperand { + const_: MirConst::from_bool(false), + span, + user_ty: None, + })); + let result = new_body.new_assignment( + rvalue, + &mut terminator, + InsertPosition::Before, + ); + let reason: &str = "Kani does not support reasoning about memory initialization of pointers to trait objects."; + + new_body.add_check( + tcx, + &self.check_type, + &mut terminator, + InsertPosition::Before, + result, + &reason, + ); + } + }; + } + Err(msg) => { + // We failed to retrieve the type layout. + let rvalue = Rvalue::Use(Operand::Constant(ConstOperand { + const_: MirConst::from_bool(false), + span, + user_ty: None, + })); + let result = + new_body.new_assignment(rvalue, &mut terminator, InsertPosition::Before); + let reason = format!( + "Kani currently doesn't support checking memory initialization of `{target_ty}`. {msg}" + ); + new_body.add_check( + tcx, + &self.check_type, + &mut terminator, + InsertPosition::Before, + result, + &reason, + ); } } new_body.into() @@ -135,4 +308,5 @@ impl IntrinsicGeneratorPass { #[strum(serialize_all = "PascalCase")] enum Intrinsics { KaniValidValue, + KaniIsInitialized, } diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index 8d5c61f55c92..56ab0be493c4 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -18,6 +18,7 @@ //! case is added. use crate::kani_middle::codegen_units::CodegenUnit; use crate::kani_middle::transform::body::CheckType; +use crate::kani_middle::transform::check_uninit::UninitPass; use crate::kani_middle::transform::check_values::ValidValuePass; use crate::kani_middle::transform::contracts::AnyModifiesPass; use crate::kani_middle::transform::kani_intrinsics::IntrinsicGeneratorPass; @@ -30,6 +31,7 @@ use std::collections::HashMap; use std::fmt::Debug; pub(crate) mod body; +mod check_uninit; mod check_values; mod contracts; mod kani_intrinsics; @@ -64,7 +66,23 @@ impl BodyTransformation { // This has to come after stubs since we want this to replace the stubbed body. transformer.add_pass(queries, AnyModifiesPass::new(tcx, &unit)); transformer.add_pass(queries, ValidValuePass { check_type: check_type.clone() }); - transformer.add_pass(queries, IntrinsicGeneratorPass { check_type }); + // 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 + // 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 { check_type: check_type.clone(), mem_init_fn_cache: HashMap::new() }, + ); + transformer.add_pass( + queries, + IntrinsicGeneratorPass { + check_type, + mem_init_fn_cache: HashMap::new(), + arguments: queries.args().clone(), + }, + ); transformer } @@ -79,7 +97,7 @@ impl BodyTransformation { None => { let mut body = instance.body().unwrap(); let mut modified = false; - for pass in self.stub_passes.iter().chain(self.inst_passes.iter()) { + for pass in self.stub_passes.iter_mut().chain(self.inst_passes.iter_mut()) { let result = pass.transform(tcx, body, instance); modified |= result.0; body = result.1; @@ -127,7 +145,7 @@ pub(crate) trait TransformPass: Debug { Self: Sized; /// Run a transformation pass in the function body. - fn transform(&self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body); + fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body); } /// The transformation result. diff --git a/kani-compiler/src/kani_middle/transform/stubs.rs b/kani-compiler/src/kani_middle/transform/stubs.rs index 4f249afdd45a..3dbd667c3943 100644 --- a/kani-compiler/src/kani_middle/transform/stubs.rs +++ b/kani-compiler/src/kani_middle/transform/stubs.rs @@ -43,7 +43,7 @@ impl TransformPass for FnStubPass { } /// Transform the function body by replacing it with the stub body. - fn transform(&self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + 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, mut args)) = ty.kind() { @@ -103,7 +103,7 @@ impl TransformPass for ExternFnStubPass { /// /// We need to find function calls and function pointers. /// We should replace this with a visitor once StableMIR includes a mutable one. - fn transform(&self, _tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + fn transform(&mut self, _tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { trace!(function=?instance.name(), "transform"); let mut new_body = MutableBody::from(body); let changed = false; diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index 18ca11b0a30f..7cbe150427e9 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -139,6 +139,10 @@ impl KaniSession { flags.push("--ub-check=ptr_to_ref_cast".into()) } + if self.args.common_args.unstable_features.contains(UnstableFeature::UninitChecks) { + flags.push("--ub-check=uninit".into()) + } + if self.args.ignore_locals_lifetime { flags.push("--ignore-storage-markers".into()) } diff --git a/kani_metadata/src/unstable.rs b/kani_metadata/src/unstable.rs index 9f82e4b02a64..68e4fba28819 100644 --- a/kani_metadata/src/unstable.rs +++ b/kani_metadata/src/unstable.rs @@ -91,6 +91,8 @@ pub enum UnstableFeature { GhostState, /// Automatically check that pointers are valid when casting them to references. PtrToRefCastChecks, + /// Automatically check that uninitialized memory is not used. + UninitChecks, /// Enable an unstable option or subcommand. UnstableOptions, } diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index 0a52a9516398..6eab2a331811 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -35,6 +35,7 @@ pub mod vec; #[doc(hidden)] pub mod internal; +mod mem_init; mod models; pub use arbitrary::Arbitrary; diff --git a/library/kani/src/mem.rs b/library/kani/src/mem.rs index c40a1aa696e3..0b390e74288d 100644 --- a/library/kani/src/mem.rs +++ b/library/kani/src/mem.rs @@ -120,6 +120,7 @@ where let (thin_ptr, metadata) = ptr.to_raw_parts(); metadata.is_ptr_aligned(thin_ptr, Internal) && is_inbounds(&metadata, thin_ptr) + && is_initialized(ptr, 1) && unsafe { has_valid_value(ptr) } } @@ -147,7 +148,7 @@ where ::Metadata: PtrProperties, { let (thin_ptr, metadata) = ptr.to_raw_parts(); - is_inbounds(&metadata, thin_ptr) && unsafe { has_valid_value(ptr) } + is_inbounds(&metadata, thin_ptr) && is_initialized(ptr, 1) && unsafe { has_valid_value(ptr) } } /// Checks that `data_ptr` points to an allocation that can hold data of size calculated from `T`. @@ -290,6 +291,13 @@ unsafe fn has_valid_value(_ptr: *const T) -> bool { kani_intrinsic() } +/// Check whether `len * size_of::()` bytes are initialized starting from `ptr`. +#[rustc_diagnostic_item = "KaniIsInitialized"] +#[inline(never)] +pub fn is_initialized(_ptr: *const T, _len: usize) -> bool { + kani_intrinsic() +} + /// Get the object ID of the given pointer. #[rustc_diagnostic_item = "KaniPointerObject"] #[inline(never)] diff --git a/library/kani/src/mem_init.rs b/library/kani/src/mem_init.rs new file mode 100644 index 000000000000..37832ea32604 --- /dev/null +++ b/library/kani/src/mem_init.rs @@ -0,0 +1,122 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! This module uses shadow memory API to track 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. 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. Padding bytes are always considered +//! uninitialized: type layout is determined at compile time and statically injected into the +//! program (see `Layout`). + +// Definitions in this module are not meant to be visible to the end user, only the compiler. +#![allow(dead_code)] + +use crate::shadow::ShadowMem; + +/// Bytewise mask, representing which bytes of a type are data and which are padding. +/// For example, for a type like this: +/// ``` +/// #[repr(C)] +/// struct Foo { +/// a: u16, +/// b: u8, +/// } +/// ``` +/// the layout would be [true, true, true, false]; +type Layout = [bool; N]; + +/// Global shadow memory object for tracking memory initialization. +#[rustc_diagnostic_item = "KaniMemInitShadowMem"] +static mut MEM_INIT_SHADOW_MEM: ShadowMem = ShadowMem::new(false); + +/// Get initialization state of `len` items laid out according to the `layout` starting at address `ptr`. +#[rustc_diagnostic_item = "KaniIsUnitPtrInitialized"] +fn is_unit_ptr_initialized(ptr: *const (), layout: Layout, len: usize) -> bool { + let mut count: usize = 0; + while count < len { + let mut offset: usize = 0; + while offset < N { + unsafe { + if layout[offset] + && !MEM_INIT_SHADOW_MEM.get((ptr as *const u8).add(count * N + offset)) + { + return false; + } + offset += 1; + } + } + count += 1; + } + true +} + +/// Set initialization state to `value` for `len` items laid out according to the `layout` starting at address `ptr`. +#[rustc_diagnostic_item = "KaniSetUnitPtrInitialized"] +fn set_unit_ptr_initialized( + ptr: *const (), + layout: Layout, + len: usize, + value: bool, +) { + let mut count: usize = 0; + while count < len { + let mut offset: usize = 0; + while offset < N { + unsafe { + MEM_INIT_SHADOW_MEM + .set((ptr as *const u8).add(count * N + offset), value && layout[offset]); + } + offset += 1; + } + count += 1; + } +} + +/// Get initialization state of `len` items laid out according to the `layout` starting at address `ptr`. +#[rustc_diagnostic_item = "KaniIsPtrInitialized"] +fn is_ptr_initialized(ptr: *const T, layout: Layout, len: usize) -> bool { + let (ptr, _) = ptr.to_raw_parts(); + is_unit_ptr_initialized(ptr, layout, len) +} + +/// Set initialization state to `value` for `len` items laid out according to the `layout` starting at address `ptr`. +#[rustc_diagnostic_item = "KaniSetPtrInitialized"] +fn set_ptr_initialized( + ptr: *const T, + layout: Layout, + len: usize, + value: bool, +) { + let (ptr, _) = ptr.to_raw_parts(); + set_unit_ptr_initialized(ptr, layout, len, value); +} + +/// Get initialization state of the slice, items of which are laid out according to the `layout` starting at address `ptr`. +#[rustc_diagnostic_item = "KaniIsSlicePtrInitialized"] +fn is_slice_ptr_initialized(ptr: *const [T], layout: Layout) -> bool { + let (ptr, len) = ptr.to_raw_parts(); + is_unit_ptr_initialized(ptr, layout, len) +} + +/// Set initialization state of the slice, items of which are laid out according to the `layout` starting at address `ptr` to `value`. +#[rustc_diagnostic_item = "KaniSetSlicePtrInitialized"] +fn set_slice_ptr_initialized(ptr: *const [T], layout: Layout, value: bool) { + let (ptr, len) = ptr.to_raw_parts(); + set_unit_ptr_initialized(ptr, layout, len, value); +} + +/// Get initialization state of the string slice, items of which are laid out according to the `layout` starting at address `ptr`. +#[rustc_diagnostic_item = "KaniIsStrPtrInitialized"] +fn is_str_ptr_initialized(ptr: *const str, layout: Layout) -> bool { + let (ptr, len) = ptr.to_raw_parts(); + is_unit_ptr_initialized(ptr, layout, len) +} + +/// Set initialization state of the string slice, items of which are laid out according to the `layout` starting at address `ptr` to `value`. +#[rustc_diagnostic_item = "KaniSetStrPtrInitialized"] +fn set_str_ptr_initialized(ptr: *const str, layout: Layout, value: bool) { + let (ptr, len) = ptr.to_raw_parts(); + set_unit_ptr_initialized(ptr, layout, len, value); +} diff --git a/library/kani_core/src/mem.rs b/library/kani_core/src/mem.rs index 8dd4a27cb03a..3b10856765a5 100644 --- a/library/kani_core/src/mem.rs +++ b/library/kani_core/src/mem.rs @@ -125,6 +125,7 @@ macro_rules! kani_mem { let (thin_ptr, metadata) = ptr.to_raw_parts(); metadata.is_ptr_aligned(thin_ptr, Internal) && is_inbounds(&metadata, thin_ptr) + && is_initialized(ptr, 1) && unsafe { has_valid_value(ptr) } } @@ -153,7 +154,9 @@ macro_rules! kani_mem { ::Metadata: PtrProperties, { let (thin_ptr, metadata) = ptr.to_raw_parts(); - is_inbounds(&metadata, thin_ptr) && unsafe { has_valid_value(ptr) } + is_inbounds(&metadata, thin_ptr) + && is_initialized(ptr, 1) + && unsafe { has_valid_value(ptr) } } /// Checks that `data_ptr` points to an allocation that can hold data of size calculated from `T`. @@ -296,6 +299,13 @@ macro_rules! kani_mem { kani_intrinsic() } + /// Check whether `len * size_of::()` bytes are initialized starting from `ptr`. + #[rustc_diagnostic_item = "KaniIsInitialized"] + #[inline(never)] + pub fn is_initialized(_ptr: *const T, _len: usize) -> bool { + kani_intrinsic() + } + /// Get the object ID of the given pointer. #[rustc_diagnostic_item = "KaniPointerObject"] #[inline(never)] diff --git a/tests/expected/uninit/access-padding-uninit/access-padding-uninit.rs b/tests/expected/uninit/access-padding-uninit/access-padding-uninit.rs new file mode 100644 index 000000000000..d6e735e219ad --- /dev/null +++ b/tests/expected/uninit/access-padding-uninit/access-padding-uninit.rs @@ -0,0 +1,16 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z ghost-state -Z uninit-checks + +use std::ptr::addr_of; + +#[repr(C)] +struct S(u32, u8); + +/// Checks that Kani catches an attempt to access padding of a struct using raw pointers. +#[kani::proof] +fn check_uninit_padding() { + let s = S(0, 0); + let ptr: *const u8 = addr_of!(s) as *const u8; + let padding = unsafe { *(ptr.add(5)) }; // ~ERROR: padding bytes are uninitialized, so reading them is UB. +} diff --git a/tests/expected/uninit/access-padding-uninit/expected b/tests/expected/uninit/access-padding-uninit/expected new file mode 100644 index 000000000000..da8d15b2dbb9 --- /dev/null +++ b/tests/expected/uninit/access-padding-uninit/expected @@ -0,0 +1,5 @@ +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const u8` + +VERIFICATION:- FAILED + +Complete - 0 successfully verified harnesses, 1 failures, 1 total. \ No newline at end of file diff --git a/tests/expected/uninit/access-padding-via-cast/access-padding-via-cast.rs b/tests/expected/uninit/access-padding-via-cast/access-padding-via-cast.rs new file mode 100644 index 000000000000..b73bebc827bc --- /dev/null +++ b/tests/expected/uninit/access-padding-via-cast/access-padding-via-cast.rs @@ -0,0 +1,20 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z ghost-state -Z uninit-checks + +use std::ptr; +#[repr(C)] +struct S(u8, u16); + +/// Checks that Kani catches an attempt to access padding of a struct using casting to different types. +#[kani::proof] +fn check_uninit_padding_after_cast() { + unsafe { + let mut s = S(0, 0); + let sptr = ptr::addr_of_mut!(s); + let sptr2 = sptr as *mut [u8; 4]; + *sptr2 = [0; 4]; + *sptr = S(0, 0); // should reset the padding + let val = *sptr2; // ~ERROR: encountered uninitialized memory + } +} diff --git a/tests/expected/uninit/access-padding-via-cast/expected b/tests/expected/uninit/access-padding-via-cast/expected new file mode 100644 index 000000000000..12c5c0a4a439 --- /dev/null +++ b/tests/expected/uninit/access-padding-via-cast/expected @@ -0,0 +1,5 @@ +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*mut [u8; 4]` + +VERIFICATION:- FAILED + +Complete - 0 successfully verified harnesses, 1 failures, 1 total. \ No newline at end of file diff --git a/tests/expected/uninit/alloc-to-slice/alloc-to-slice.rs b/tests/expected/uninit/alloc-to-slice/alloc-to-slice.rs new file mode 100644 index 000000000000..3c4420f5791f --- /dev/null +++ b/tests/expected/uninit/alloc-to-slice/alloc-to-slice.rs @@ -0,0 +1,20 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z ghost-state -Z uninit-checks + +use std::alloc::{alloc, dealloc, Layout}; +use std::slice::from_raw_parts; + +/// Checks that Kani catches an attempt to form a slice from uninitialized memory. +#[kani::proof] +fn check_uninit_slice() { + let layout = Layout::from_size_align(16, 8).unwrap(); + unsafe { + let ptr = alloc(layout); + *ptr = 0x41; + *ptr.add(1) = 0x42; + *ptr.add(2) = 0x43; + *ptr.add(3) = 0x44; + let uninit_slice = from_raw_parts(ptr, 16); // ~ERROR: forming a slice from unitialized memory is UB. + } +} diff --git a/tests/expected/uninit/alloc-to-slice/expected b/tests/expected/uninit/alloc-to-slice/expected new file mode 100644 index 000000000000..f8347a591edf --- /dev/null +++ b/tests/expected/uninit/alloc-to-slice/expected @@ -0,0 +1,5 @@ +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const [u8]` + +VERIFICATION:- FAILED + +Complete - 0 successfully verified harnesses, 1 failures, 1 total. \ No newline at end of file diff --git a/tests/expected/uninit/vec-read-bad-len/expected b/tests/expected/uninit/vec-read-bad-len/expected new file mode 100644 index 000000000000..f8347a591edf --- /dev/null +++ b/tests/expected/uninit/vec-read-bad-len/expected @@ -0,0 +1,5 @@ +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const [u8]` + +VERIFICATION:- FAILED + +Complete - 0 successfully verified harnesses, 1 failures, 1 total. \ No newline at end of file diff --git a/tests/expected/uninit/vec-read-bad-len/vec-read-bad-len.rs b/tests/expected/uninit/vec-read-bad-len/vec-read-bad-len.rs new file mode 100644 index 000000000000..9778bb11a277 --- /dev/null +++ b/tests/expected/uninit/vec-read-bad-len/vec-read-bad-len.rs @@ -0,0 +1,15 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z ghost-state -Z uninit-checks + +use std::ops::Index; + +/// Checks that Kani catches an attempt to read uninitialized memory from a vector with bad length. +#[kani::proof] +fn check_vec_read_bad_len() { + let mut v: Vec = Vec::with_capacity(10); + unsafe { + v.set_len(5); // even though length is now 5, vector is still uninitialized + } + let uninit = v.index(0); // ~ERROR: reading from unitialized memory is UB. +} diff --git a/tests/expected/uninit/vec-read-semi-init/expected b/tests/expected/uninit/vec-read-semi-init/expected new file mode 100644 index 000000000000..da8d15b2dbb9 --- /dev/null +++ b/tests/expected/uninit/vec-read-semi-init/expected @@ -0,0 +1,5 @@ +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const u8` + +VERIFICATION:- FAILED + +Complete - 0 successfully verified harnesses, 1 failures, 1 total. \ No newline at end of file diff --git a/tests/expected/uninit/vec-read-semi-init/vec-read-semi-init.rs b/tests/expected/uninit/vec-read-semi-init/vec-read-semi-init.rs new file mode 100644 index 000000000000..4330f5f53023 --- /dev/null +++ b/tests/expected/uninit/vec-read-semi-init/vec-read-semi-init.rs @@ -0,0 +1,11 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z ghost-state -Z uninit-checks + +/// Checks that Kani catches an attempt to read uninitialized memory from a semi-initialized vector. +#[kani::proof] +fn check_vec_read_semi_init() { + let mut v: Vec = Vec::with_capacity(10); + unsafe { *v.as_mut_ptr().add(4) = 0x42 }; + let uninit = unsafe { *v.as_ptr().add(5) }; // ~ERROR: reading from unitialized memory is UB. +} diff --git a/tests/expected/uninit/vec-read-uninit/expected b/tests/expected/uninit/vec-read-uninit/expected new file mode 100644 index 000000000000..da8d15b2dbb9 --- /dev/null +++ b/tests/expected/uninit/vec-read-uninit/expected @@ -0,0 +1,5 @@ +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const u8` + +VERIFICATION:- FAILED + +Complete - 0 successfully verified harnesses, 1 failures, 1 total. \ No newline at end of file diff --git a/tests/expected/uninit/vec-read-uninit/vec-read-uninit.rs b/tests/expected/uninit/vec-read-uninit/vec-read-uninit.rs new file mode 100644 index 000000000000..c322b4d33bb2 --- /dev/null +++ b/tests/expected/uninit/vec-read-uninit/vec-read-uninit.rs @@ -0,0 +1,10 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z ghost-state -Z uninit-checks + +/// Checks that Kani catches an attempt to read uninitialized memory from an uninitialized vector. +#[kani::proof] +fn check_vec_read_uninit() { + let v: Vec = Vec::with_capacity(10); + let uninit = unsafe { *v.as_ptr().add(5) }; // ~ERROR: reading from unitialized memory is UB. +} diff --git a/tests/kani/Uninit/access-padding-enum-diverging-variants.rs b/tests/kani/Uninit/access-padding-enum-diverging-variants.rs new file mode 100644 index 000000000000..7feb493a5b3f --- /dev/null +++ b/tests/kani/Uninit/access-padding-enum-diverging-variants.rs @@ -0,0 +1,34 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z ghost-state -Z uninit-checks + +use std::ptr; +use std::ptr::addr_of; + +/// The layout of this enum is variable, so Kani cannot check memory initialization statically. +#[repr(C)] +enum E1 { + A(u16, u8), + B(u16), +} + +/// The layout of this enum is variable, but both of the arms have the same padding, so Kani should +/// support that. +#[repr(C)] +enum E2 { + A(u16), + B(u8, u8), +} + +#[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; +} + +#[kani::proof] +fn access_padding_supported() { + let s = E2::A(0); + let ptr: *const u8 = addr_of!(s) as *const u8; +} diff --git a/tests/kani/Uninit/access-padding-enum-multiple-variants.rs b/tests/kani/Uninit/access-padding-enum-multiple-variants.rs new file mode 100644 index 000000000000..fb8fae06ca59 --- /dev/null +++ b/tests/kani/Uninit/access-padding-enum-multiple-variants.rs @@ -0,0 +1,49 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z ghost-state -Z uninit-checks + +use std::ptr; +use std::ptr::addr_of; + +/// The layout of this enum is the following (D = data, P = padding): +/// 0 1 2 3 4 5 6 7 +/// [D, D, D, D, D, D, D, P] +/// ---------- ------- +/// \_ tag (i32) \_ A|B(u16, u8) +#[repr(C)] +enum E { + A(u16, u8), + B(u16, u8), +} + +#[kani::proof] +fn access_padding_init_a() { + let s = E::A(0, 0); + let ptr: *const u8 = addr_of!(s) as *const u8; + let at_0 = unsafe { *(ptr.add(0)) }; + let at_4 = unsafe { *(ptr.add(4)) }; +} + +#[kani::proof] +fn access_padding_init_b() { + let s = E::A(0, 0); + let ptr: *const u8 = addr_of!(s) as *const u8; + let at_0 = unsafe { *(ptr.add(0)) }; + let at_4 = unsafe { *(ptr.add(4)) }; +} + +#[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; + let at_7 = unsafe { *(ptr.add(7)) }; +} + +#[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; + let at_7 = unsafe { *(ptr.add(7)) }; +} diff --git a/tests/kani/Uninit/access-padding-enum-single-field.rs b/tests/kani/Uninit/access-padding-enum-single-field.rs new file mode 100644 index 000000000000..c283b603f705 --- /dev/null +++ b/tests/kani/Uninit/access-padding-enum-single-field.rs @@ -0,0 +1,34 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z ghost-state -Z uninit-checks + +use std::ptr; +use std::ptr::addr_of; + +/// The layout of this enum is the following (D = data, P = padding): +/// 0 1 2 3 4 5 6 7 8 9 A B C D E F +/// [D, D, D, D, P, P, P, P, D, D, D, D, D, D, D, D] +/// ---------- ---------------------- +/// \_ tag (i32) \_ A(u64) +#[repr(C)] +enum E { + A(u64), +} + +#[kani::proof] +fn access_padding_init() { + let s = E::A(0); + let ptr: *const u8 = addr_of!(s) as *const u8; + let at_0 = unsafe { *(ptr.add(0)) }; + let at_3 = unsafe { *(ptr.add(3)) }; + let at_9 = unsafe { *(ptr.add(9)) }; +} + +#[kani::proof] +#[kani::should_panic] +fn access_padding_uninit() { + let s = E::A(0); + let ptr: *const u8 = addr_of!(s) as *const u8; + let at_4 = unsafe { *(ptr.add(4)) }; + let at_7 = unsafe { *(ptr.add(7)) }; +} diff --git a/tests/kani/Uninit/access-padding-enum-single-variant.rs b/tests/kani/Uninit/access-padding-enum-single-variant.rs new file mode 100644 index 000000000000..f33cfe7ce6fb --- /dev/null +++ b/tests/kani/Uninit/access-padding-enum-single-variant.rs @@ -0,0 +1,32 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z ghost-state -Z uninit-checks + +use std::ptr; +use std::ptr::addr_of; + +/// The layout of this enum is the following (D = data, P = padding): +/// 0 1 2 3 4 5 6 7 +/// [D, D, D, D, D, D, D, P] +/// ---------- ------- +/// \_ tag (i32) \_ A(u16, u8) +#[repr(C)] +enum E { + A(u16, u8), +} + +#[kani::proof] +fn access_padding_init() { + let s = E::A(0, 0); + let ptr: *const u8 = addr_of!(s) as *const u8; + let at_0 = unsafe { *(ptr.add(0)) }; + let at_4 = unsafe { *(ptr.add(4)) }; +} + +#[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; + let at_7 = unsafe { *(ptr.add(7)) }; +} diff --git a/tests/perf/uninit/Cargo.toml b/tests/perf/uninit/Cargo.toml new file mode 100644 index 000000000000..9f44cb3fe103 --- /dev/null +++ b/tests/perf/uninit/Cargo.toml @@ -0,0 +1,14 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +[package] +name = "uninit" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[package.metadata.kani] +unstable = { ghost-state = true, uninit-checks = true } diff --git a/tests/perf/uninit/expected b/tests/perf/uninit/expected new file mode 100644 index 000000000000..f7b4fd303a77 --- /dev/null +++ b/tests/perf/uninit/expected @@ -0,0 +1 @@ +Complete - 5 successfully verified harnesses, 0 failures, 5 total. \ No newline at end of file diff --git a/tests/perf/uninit/src/lib.rs b/tests/perf/uninit/src/lib.rs new file mode 100644 index 000000000000..86b101c0e5d8 --- /dev/null +++ b/tests/perf/uninit/src/lib.rs @@ -0,0 +1,68 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use std::alloc::{alloc, alloc_zeroed, Layout}; +use std::ptr; +use std::ptr::addr_of; +use std::slice::from_raw_parts; + +#[repr(C)] +struct S(u32, u8); + +#[kani::proof] +fn access_padding_init() { + let s = S(0, 0); + let ptr: *const u8 = addr_of!(s) as *const u8; + let data = unsafe { *(ptr.add(3)) }; // Accessing data bytes is valid. +} + +#[kani::proof] +fn alloc_to_slice() { + let layout = Layout::from_size_align(32, 8).unwrap(); + unsafe { + let ptr = alloc(layout); + *ptr = 0x41; + *ptr.add(1) = 0x42; + *ptr.add(2) = 0x43; + *ptr.add(3) = 0x44; + *ptr.add(16) = 0x00; + let val = *(ptr.add(2)); // Accessing previously initialized byte is valid. + } +} + +#[kani::proof] +fn alloc_zeroed_to_slice() { + let layout = Layout::from_size_align(32, 8).unwrap(); + unsafe { + // This returns initialized memory, so any further accesses are valid. + let ptr = alloc_zeroed(layout); + *ptr = 0x41; + *ptr.add(1) = 0x42; + *ptr.add(2) = 0x43; + *ptr.add(3) = 0x44; + *ptr.add(16) = 0x00; + let slice1 = from_raw_parts(ptr, 16); + let slice2 = from_raw_parts(ptr.add(16), 16); + } +} + +#[kani::proof] +fn struct_padding_and_arr_init() { + unsafe { + let mut s = S(0, 0); + let sptr = ptr::addr_of_mut!(s); + let sptr2 = sptr as *mut [u8; 4]; + *sptr2 = [0; 4]; + *sptr = S(0, 0); + // Both S(u32, u8) and [u8; 4] have the same layout, so the memory is initialized. + let val = *sptr2; + } +} + +#[kani::proof] +fn vec_read_init() { + let mut v: Vec = Vec::with_capacity(10); + unsafe { *v.as_mut_ptr().add(5) = 0x42 }; + let def = unsafe { *v.as_ptr().add(5) }; // Accessing previously initialized byte is valid. + let x = def + 1; +} diff --git a/tests/std-checks/core/mem.expected b/tests/std-checks/core/mem.expected index 8a3b89a8f66a..1484c83901fc 100644 --- a/tests/std-checks/core/mem.expected +++ b/tests/std-checks/core/mem.expected @@ -1,3 +1,7 @@ +Checking harness mem::verify::check_swap_unit... + +Failed Checks: ptr NULL or writable up to size + Summary: Verification failed for - mem::verify::check_swap_unit -Complete - 3 successfully verified harnesses, 1 failures, 4 total. +Complete - 6 successfully verified harnesses, 1 failures, 7 total. diff --git a/tests/std-checks/core/slice.expected b/tests/std-checks/core/slice.expected new file mode 100644 index 000000000000..01a90d50b557 --- /dev/null +++ b/tests/std-checks/core/slice.expected @@ -0,0 +1 @@ +Complete - 1 successfully verified harnesses, 0 failures, 1 total. diff --git a/tests/std-checks/core/src/lib.rs b/tests/std-checks/core/src/lib.rs index f0e5b480a2d2..b0a4fbc6154f 100644 --- a/tests/std-checks/core/src/lib.rs +++ b/tests/std-checks/core/src/lib.rs @@ -7,3 +7,4 @@ extern crate kani; pub mod mem; pub mod ptr; +pub mod slice; diff --git a/tests/std-checks/core/src/mem.rs b/tests/std-checks/core/src/mem.rs index 4f41d176a73a..b0400d0a75f5 100644 --- a/tests/std-checks/core/src/mem.rs +++ b/tests/std-checks/core/src/mem.rs @@ -18,6 +18,11 @@ pub mod contracts { pub fn swap(x: &mut T, y: &mut T) { std::mem::swap(x, y) } + + #[kani::modifies(dest)] + pub fn replace(dest: &mut T, src: T) -> T { + std::mem::replace(dest, src) + } } #[cfg(kani)] @@ -70,4 +75,34 @@ mod verify { std::mem::forget(x); std::mem::forget(y); } + + #[kani::proof_for_contract(contracts::replace)] + pub fn check_replace_primitive() { + let mut x: u8 = kani::any(); + let x_before = x; + + let y: u8 = kani::any(); + let x_returned = contracts::replace(&mut x, y); + + kani::assert(x_before == x_returned, "x_before == x_returned"); + } + + #[kani::proof_for_contract(contracts::replace)] + pub fn check_replace_adt_no_drop() { + let mut x: CannotDrop = kani::any(); + let y: CannotDrop = kani::any(); + let new_x = contracts::replace(&mut x, y); + std::mem::forget(x); + std::mem::forget(new_x); + } + + /// Memory replace logic is optimized according to the size and alignment of a type. + #[kani::proof_for_contract(contracts::replace)] + pub fn check_replace_large_adt_no_drop() { + let mut x: CannotDrop<[u128; 4]> = kani::any(); + let y: CannotDrop<[u128; 4]> = kani::any(); + let new_x = contracts::replace(&mut x, y); + std::mem::forget(x); + std::mem::forget(new_x); + } } diff --git a/tests/std-checks/core/src/slice.rs b/tests/std-checks/core/src/slice.rs new file mode 100644 index 000000000000..044f4bd38586 --- /dev/null +++ b/tests/std-checks/core/src/slice.rs @@ -0,0 +1,30 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +/// Create wrapper functions to standard library functions that contains their contract. +pub mod contracts { + use kani::{mem::*, requires}; + + #[requires(can_dereference(std::ptr::slice_from_raw_parts(data, len)))] + #[requires(is_initialized(data, len))] + pub unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { + std::slice::from_raw_parts(data, len) + } +} + +#[cfg(kani)] +mod verify { + use super::*; + + const MAX_LEN: usize = 2; + + #[kani::proof_for_contract(contracts::from_raw_parts)] + #[kani::unwind(25)] + pub fn check_from_raw_parts_primitive() { + let len: usize = kani::any(); + kani::assume(len < MAX_LEN); + + let arr = vec![0u8; len]; + let _slice = unsafe { contracts::from_raw_parts(arr.as_ptr(), len) }; + } +} From f6d33854ca95e25cbd52d52f3ca842b7eb07793f Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Tue, 2 Jul 2024 22:44:22 -0700 Subject: [PATCH 171/225] Remove deprecated `--enable-stubbing` (#3309) Part of #2279 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 | 41 ++++++++----------- tests/cargo-ui/stubbing-flag/src/main.rs | 3 +- .../function-stubbing-no-harness/main.rs | 2 +- .../expected/stubbing-ambiguous-path/main.rs | 2 +- tests/ui/function-stubbing-error/main.rs | 2 +- .../deprecated-enable-stable/deprecated.rs | 16 -------- .../deprecated-enable-stable/expected | 2 - tests/ui/stubbing/invalid-path/invalid.rs | 2 +- tests/ui/stubbing/stubbing-flag/main.rs | 4 +- .../trait_mismatch.rs | 2 +- .../stubbing-type-validation/type_mismatch.rs | 2 +- 11 files changed, 28 insertions(+), 50 deletions(-) delete mode 100644 tests/ui/stubbing/deprecated-enable-stable/deprecated.rs delete mode 100644 tests/ui/stubbing/deprecated-enable-stable/expected diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index 4c0fe98cc65d..d043035bcab2 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -282,17 +282,6 @@ pub struct VerificationArgs { #[arg(long)] pub randomize_layout: Option>, - /// Enable the stubbing of functions and methods. - // TODO: Stubbing should in principle work with concrete playback. - // - #[arg( - long, - hide_short_help = true, - requires("enable_unstable"), - conflicts_with("concrete_playback") - )] - enable_stubbing: bool, - /// Enable Kani coverage output alongside verification result #[arg(long, hide_short_help = true)] pub coverage: bool, @@ -345,8 +334,7 @@ impl VerificationArgs { /// Is experimental stubbing enabled? pub fn is_stubbing_enabled(&self) -> bool { - self.enable_stubbing - || self.common_args.unstable_features.contains(UnstableFeature::Stubbing) + self.common_args.unstable_features.contains(UnstableFeature::Stubbing) || self.is_function_contracts_enabled() } } @@ -579,6 +567,13 @@ impl ValidateArgs for VerificationArgs { --output-format=old.", )); } + if self.concrete_playback.is_some() && self.is_stubbing_enabled() { + // Concrete playback currently does not work with contracts or stubbing. + return Err(Error::raw( + ErrorKind::ArgumentConflict, + "Conflicting options: --concrete-playback isn't compatible with stubbing.", + )); + } if self.concrete_playback.is_some() && self.jobs() != Some(1) { // Concrete playback currently embeds a lot of assumptions about the order in which harnesses get called. return Err(Error::raw( @@ -606,10 +601,6 @@ impl ValidateArgs for VerificationArgs { } } - if self.enable_stubbing { - print_deprecated(&self.common_args, "--enable-stubbing", "-Z stubbing"); - } - if self.concrete_playback.is_some() && !self.common_args.unstable_features.contains(UnstableFeature::ConcretePlayback) { @@ -880,14 +871,18 @@ mod tests { #[test] fn check_enable_stubbing() { - check_unstable_flag!("--enable-stubbing --harness foo", enable_stubbing); + let res = parse_unstable_disabled("--harness foo").unwrap(); + assert!(!res.verify_opts.is_stubbing_enabled()); - check_unstable_flag!("--enable-stubbing", enable_stubbing); + let res = parse_unstable_disabled("--harness foo -Z stubbing").unwrap(); + assert!(res.verify_opts.is_stubbing_enabled()); - // `--enable-stubbing` cannot be called with `--concrete-playback` - let err = - parse_unstable_enabled("--enable-stubbing --harness foo --concrete-playback=print") - .unwrap_err(); + // `-Z stubbing` cannot be called with `--concrete-playback` + let res = parse_unstable_disabled( + "--harness foo --concrete-playback=print -Z concrete-playback -Z stubbing", + ) + .unwrap(); + let err = res.validate().unwrap_err(); assert_eq!(err.kind(), ErrorKind::ArgumentConflict); } diff --git a/tests/cargo-ui/stubbing-flag/src/main.rs b/tests/cargo-ui/stubbing-flag/src/main.rs index db26e366005e..1ef312b852ff 100644 --- a/tests/cargo-ui/stubbing-flag/src/main.rs +++ b/tests/cargo-ui/stubbing-flag/src/main.rs @@ -1,7 +1,8 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // -//! This tests that the `--enable-stubbing` and `--harness` arguments flow from `kani-driver` to `kani-compiler`. +//! This tests that enabling stubbing and using `--harness` arguments flow from +//! `kani-driver` to `kani-compiler`. #[kani::proof] fn main() {} diff --git a/tests/expected/function-stubbing-no-harness/main.rs b/tests/expected/function-stubbing-no-harness/main.rs index 1819c8dce862..d69192ae2180 100644 --- a/tests/expected/function-stubbing-no-harness/main.rs +++ b/tests/expected/function-stubbing-no-harness/main.rs @@ -1,7 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // -// kani-flags: --harness foo --enable-unstable --enable-stubbing +// kani-flags: --harness foo -Z stubbing // //! This tests whether we detect missing harnesses during stubbing. diff --git a/tests/expected/stubbing-ambiguous-path/main.rs b/tests/expected/stubbing-ambiguous-path/main.rs index 7a406d51d227..9ab25b501a44 100644 --- a/tests/expected/stubbing-ambiguous-path/main.rs +++ b/tests/expected/stubbing-ambiguous-path/main.rs @@ -1,7 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // -// kani-flags: --harness main --enable-unstable --enable-stubbing +// kani-flags: --harness main -Z stubbing // //! This tests that we raise an error if a path in a `kani::stub` attribute can //! resolve to multiple functions. diff --git a/tests/ui/function-stubbing-error/main.rs b/tests/ui/function-stubbing-error/main.rs index 2f5a10ff18cb..c433e352e740 100644 --- a/tests/ui/function-stubbing-error/main.rs +++ b/tests/ui/function-stubbing-error/main.rs @@ -1,7 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // -// kani-flags: --harness main --enable-unstable --enable-stubbing +// kani-flags: --harness main -Z stubbing // //! This tests whether we detect syntactically misformed `kani::stub` annotations. diff --git a/tests/ui/stubbing/deprecated-enable-stable/deprecated.rs b/tests/ui/stubbing/deprecated-enable-stable/deprecated.rs deleted file mode 100644 index a5686af20a35..000000000000 --- a/tests/ui/stubbing/deprecated-enable-stable/deprecated.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: --enable-unstable --enable-stubbing -//! Checks that the `kani::stub` attribute is accepted - -fn foo() { - unreachable!(); -} - -fn bar() {} - -#[kani::proof] -#[kani::stub(foo, bar)] -fn main() { - foo(); -} diff --git a/tests/ui/stubbing/deprecated-enable-stable/expected b/tests/ui/stubbing/deprecated-enable-stable/expected deleted file mode 100644 index eb05b8cf9812..000000000000 --- a/tests/ui/stubbing/deprecated-enable-stable/expected +++ /dev/null @@ -1,2 +0,0 @@ -warning: The `--enable-stubbing` option is deprecated. This option will be removed soon. Consider using `-Z stubbing` instead -VERIFICATION:- SUCCESSFUL diff --git a/tests/ui/stubbing/invalid-path/invalid.rs b/tests/ui/stubbing/invalid-path/invalid.rs index d24c757fd0cd..04d3c786b0d7 100644 --- a/tests/ui/stubbing/invalid-path/invalid.rs +++ b/tests/ui/stubbing/invalid-path/invalid.rs @@ -1,7 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // -// kani-flags: --harness invalid_stub --enable-unstable --enable-stubbing +// kani-flags: --harness invalid_stub -Z stubbing pub mod mod_a { use crate::mod_b::noop; diff --git a/tests/ui/stubbing/stubbing-flag/main.rs b/tests/ui/stubbing/stubbing-flag/main.rs index f9b788e94d82..a5172ad9d0cd 100644 --- a/tests/ui/stubbing/stubbing-flag/main.rs +++ b/tests/ui/stubbing/stubbing-flag/main.rs @@ -1,9 +1,9 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // -// kani-flags: --harness main --enable-unstable --enable-stubbing +// kani-flags: --harness main -Z stubbing // -//! This tests that the `--enable-stubbing` and `--harness` arguments flow from `kani-driver` to `kani-compiler`. +//! This tests that enabling stubbing and `--harness` argument flow from `kani-driver` to `kani-compiler`. #[kani::proof] fn main() {} diff --git a/tests/ui/stubbing/stubbing-trait-validation/trait_mismatch.rs b/tests/ui/stubbing/stubbing-trait-validation/trait_mismatch.rs index e934141664bf..c49552d942e2 100644 --- a/tests/ui/stubbing/stubbing-trait-validation/trait_mismatch.rs +++ b/tests/ui/stubbing/stubbing-trait-validation/trait_mismatch.rs @@ -1,7 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // -// kani-flags: --harness harness --enable-unstable --enable-stubbing +// kani-flags: --harness harness -Z stubbing // //! This tests that we catch trait mismatches between the stub and the original //! function/method. In particular, this tests the case when the program is diff --git a/tests/ui/stubbing/stubbing-type-validation/type_mismatch.rs b/tests/ui/stubbing/stubbing-type-validation/type_mismatch.rs index 9f98ae302a22..bc8343658f9e 100644 --- a/tests/ui/stubbing/stubbing-type-validation/type_mismatch.rs +++ b/tests/ui/stubbing/stubbing-type-validation/type_mismatch.rs @@ -1,7 +1,7 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // -// kani-flags: --harness harness --enable-unstable --enable-stubbing +// kani-flags: --harness harness -Z stubbing // //! This tests that we catch type mismatches between the stub and the original //! function/method. From fcc9d8b6a0fed99065f0d204d8962600001f8f8d Mon Sep 17 00:00:00 2001 From: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Date: Wed, 3 Jul 2024 10:46:26 -0400 Subject: [PATCH 172/225] Bump Kani version to 0.53.0 (#3317) Bump Kani version to 0.53.0 and add notes for the upcoming release. --- CHANGELOG.md | 38 ++++++++++++++++++++++++++++++++++ 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, 58 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b4acbb284aa..47ae290853a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,44 @@ 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.53.0] + +### Major Changes +* The `--visualize` option is being deprecated and will be removed in a future release. Consider using the `--concrete-playback` option instead. +* The `-Z ptr-to-ref-cast-checks` option is being introduced to check pointer validity when casting raw pointers to references. The feature is currently behind an unstable flag but is expected to be stabilized in a future release once remaining performance issues have been resolved. +* The `-Z uninit-checks` option is being introduced to check memory initialization. The feature is currently behind an unstable flag and also requires the `-Z ghost-state` option. + +### Breaking Changes +* Remove support for the unstable argument `--function` by @celinval in https://github.com/model-checking/kani/pull/3278 +* Remove deprecated `--enable-stubbing` by @celinval in https://github.com/model-checking/kani/pull/3309 + +### What's Changed + +* Change ensures into closures by @pi314mm in https://github.com/model-checking/kani/pull/3207 +* (Re)introduce `Invariant` trait by @adpaco-aws in https://github.com/model-checking/kani/pull/3190 +* Remove empty box creation from contracts impl by @celinval in https://github.com/model-checking/kani/pull/3233 +* Add a new verify-std subcommand to Kani by @celinval in https://github.com/model-checking/kani/pull/3231 +* Inject pointer validity check when casting raw pointers to references by @artemagvanian in https://github.com/model-checking/kani/pull/3221 +* Do not turn trivially diverging loops into assume(false) by @tautschnig in https://github.com/model-checking/kani/pull/3223 +* Fix "unused mut" warnings created by generated code. by @jsalzbergedu in https://github.com/model-checking/kani/pull/3247 +* Refactor stubbing so Kani compiler only invoke rustc once per crate by @celinval in https://github.com/model-checking/kani/pull/3245 +* Use cfg=kani_host for host crates by @tautschnig in https://github.com/model-checking/kani/pull/3244 +* Add intrinsics and Arbitrary support for no_core by @jaisnan in https://github.com/model-checking/kani/pull/3230 +* Contracts: Avoid attribute duplication and `const` function generation for constant function by @celinval in https://github.com/model-checking/kani/pull/3255 +* Fix contract of constant fn with effect feature by @celinval in https://github.com/model-checking/kani/pull/3259 +* Fix typed_swap for ZSTs by @tautschnig in https://github.com/model-checking/kani/pull/3256 +* Add a `#[derive(Invariant)]` macro by @adpaco-aws in https://github.com/model-checking/kani/pull/3250 +* Contracts: History Expressions via "old" monad by @pi314mm in https://github.com/model-checking/kani/pull/3232 +* Function Contracts: remove instances of _renamed by @pi314mm in https://github.com/model-checking/kani/pull/3274 +* Deprecate `--visualize` in favor of concrete playback by @celinval in https://github.com/model-checking/kani/pull/3281 +* Fix operand in fat pointer comparison by @pi314mm in https://github.com/model-checking/kani/pull/3297 +* Function Contracts: Closure Type Inference by @pi314mm in https://github.com/model-checking/kani/pull/3307 +* Add support for f16 and f128 for toolchain upgrade to 6/28 by @jaisnan in https://github.com/model-checking/kani/pull/3306 +* Towards Proving Memory Initialization by @artemagvanian in https://github.com/model-checking/kani/pull/3264 +* Rust toolchain upgraded to `nightly-2024-07-01` by @tautschnig @celinval @jaisnan @adpaco-aws + +**Full Changelog**: https://github.com/model-checking/kani/compare/kani-0.52.0...kani-0.53.0 + ## [0.52.0] ## What's Changed diff --git a/Cargo.lock b/Cargo.lock index 89e393db8b46..cf47e75b0562 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,7 +92,7 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "build-kani" -version = "0.52.0" +version = "0.53.0" dependencies = [ "anyhow", "cargo_metadata", @@ -228,7 +228,7 @@ dependencies = [ [[package]] name = "cprover_bindings" -version = "0.52.0" +version = "0.53.0" dependencies = [ "lazy_static", "linear-map", @@ -405,14 +405,14 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "kani" -version = "0.52.0" +version = "0.53.0" dependencies = [ "kani_macros", ] [[package]] name = "kani-compiler" -version = "0.52.0" +version = "0.53.0" dependencies = [ "clap", "cprover_bindings", @@ -433,7 +433,7 @@ dependencies = [ [[package]] name = "kani-driver" -version = "0.52.0" +version = "0.53.0" dependencies = [ "anyhow", "cargo_metadata", @@ -461,7 +461,7 @@ dependencies = [ [[package]] name = "kani-verifier" -version = "0.52.0" +version = "0.53.0" dependencies = [ "anyhow", "home", @@ -470,14 +470,14 @@ dependencies = [ [[package]] name = "kani_core" -version = "0.52.0" +version = "0.53.0" dependencies = [ "kani_macros", ] [[package]] name = "kani_macros" -version = "0.52.0" +version = "0.53.0" dependencies = [ "proc-macro-error", "proc-macro2", @@ -487,7 +487,7 @@ dependencies = [ [[package]] name = "kani_metadata" -version = "0.52.0" +version = "0.53.0" dependencies = [ "clap", "cprover_bindings", @@ -992,7 +992,7 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "std" -version = "0.52.0" +version = "0.53.0" dependencies = [ "kani", ] diff --git a/Cargo.toml b/Cargo.toml index 641cbaf961be..1b4933c5bdcf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-verifier" -version = "0.52.0" +version = "0.53.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 63dfbf6781cd..aced9e5b9b65 100644 --- a/cprover_bindings/Cargo.toml +++ b/cprover_bindings/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "cprover_bindings" -version = "0.52.0" +version = "0.53.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-compiler/Cargo.toml b/kani-compiler/Cargo.toml index 05002ed1eb27..23389c156302 100644 --- a/kani-compiler/Cargo.toml +++ b/kani-compiler/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-compiler" -version = "0.52.0" +version = "0.53.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/kani-driver/Cargo.toml b/kani-driver/Cargo.toml index 2263e39792e2..d58d686d7d43 100644 --- a/kani-driver/Cargo.toml +++ b/kani-driver/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani-driver" -version = "0.52.0" +version = "0.53.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 f9d701018367..816752a58e03 100644 --- a/kani_metadata/Cargo.toml +++ b/kani_metadata/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_metadata" -version = "0.52.0" +version = "0.53.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/kani/Cargo.toml b/library/kani/Cargo.toml index c0f783e65315..91fee3dabf30 100644 --- a/library/kani/Cargo.toml +++ b/library/kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani" -version = "0.52.0" +version = "0.53.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 6f5230c6d19b..ec12209f0e08 100644 --- a/library/kani_core/Cargo.toml +++ b/library/kani_core/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_core" -version = "0.52.0" +version = "0.53.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 e0c79a39c105..5917c322729e 100644 --- a/library/kani_macros/Cargo.toml +++ b/library/kani_macros/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "kani_macros" -version = "0.52.0" +version = "0.53.0" edition = "2021" license = "MIT OR Apache-2.0" publish = false diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index fcc2739dc606..ae70767f6781 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.52.0" +version = "0.53.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 30c2807aa323..7c8e6eef122a 100644 --- a/tools/build-kani/Cargo.toml +++ b/tools/build-kani/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "build-kani" -version = "0.52.0" +version = "0.53.0" edition = "2021" description = "Builds Kani, Sysroot and release bundle." license = "MIT OR Apache-2.0" From 107daa7c36fdf2341ac972fd7bfba39f26ff786b Mon Sep 17 00:00:00 2001 From: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Date: Mon, 8 Jul 2024 17:02:54 -0400 Subject: [PATCH 173/225] CI: Allow use of `node16` for release jobs (#3322) actions/checkout@v3 is causing trouble in the `BuildBundle-Linux` workflow as described in #3321 . This PR sets `ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true` to continue using `node16` so we don't find issues with `node20` dependencies in `ubuntu-18.04` jobs. This is the option recommended in this GitHub post: https://github.blog/changelog/2024-03-07-github-actions-all-actions-will-run-on-node20-instead-of-node16-by-default/ Resolves #3321 --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f7fbf32cc3eb..2097a9e8f5f3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,6 +16,7 @@ on: env: RUST_BACKTRACE: 1 + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true jobs: build_bundle_macos: From 53b702d90c8db2df5666ecb3cc1584ba77b7e585 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Mon, 8 Jul 2024 23:40:41 +0200 Subject: [PATCH 174/225] Auto-upload macOS ARM binaries to draft release (#3320) In #3266 a job to build macOS ARM binaries was added, but the artifacts weren't yet propagated to the release. This PR adds this missing step. --- .github/workflows/release.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2097a9e8f5f3..9fb35033d2d1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -323,7 +323,7 @@ jobs: if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/kani-') }} name: Release runs-on: ubuntu-20.04 - needs: [build_bundle_macos, build_bundle_linux, test_bundle, test_alt_platform] + needs: [build_bundle_macos, build_bundle_macos_aarch64, build_bundle_linux, test_bundle, test_alt_platform] permissions: contents: write outputs: @@ -355,6 +355,11 @@ jobs: with: name: ${{ needs.build_bundle_macos.outputs.bundle }} + - name: Download MacOS ARM bundle + uses: actions/download-artifact@v3 + with: + name: ${{ needs.build_bundle_macos_aarch64.outputs.bundle }} + - name: Download Linux bundle uses: actions/download-artifact@v3 with: @@ -368,7 +373,7 @@ jobs: with: name: kani-${{ env.TAG_VERSION }} tag: kani-${{ env.TAG_VERSION }} - artifacts: "${{ needs.build_bundle_linux.outputs.bundle }},${{ needs.build_bundle_macos.outputs.bundle }}" + artifacts: "${{ needs.build_bundle_linux.outputs.bundle }},${{ needs.build_bundle_macos.outputs.bundle }},${{ needs.build_bundle_macos_aarch64.outputs.bundle }}" body: | Kani Rust verifier release bundle version ${{ env.TAG_VERSION }}. draft: true From 34820bd8473b80f99c7863cb9f8780ad464a2687 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 09:27:07 -0400 Subject: [PATCH 175/225] Bump tests/perf/s2n-quic from `37335c1` to `cd20ac1` (#3330) --- 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 37335c196fb5..cd20ac16f5e4 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 37335c196fb5755dcbe2532e5a3820e46906d5ea +Subproject commit cd20ac16f5e4477238164074d2ed5b94ca8ba4fd From 923346c5cedd1a96619fc49dec385bd5c1c9a8db Mon Sep 17 00:00:00 2001 From: Artem Agvanian Date: Tue, 9 Jul 2024 09:03:16 -0700 Subject: [PATCH 176/225] Make Kani reject mutable pointer casts if padding is incompatible and memory initialization is checked (#3332) This PR introduces layout checks for types to instrument mutable pointer casts. If two types have incompatible padding (e.g. a padding byte in one is a data byte in the other or vice-versa), an "unsupported check" assertion is inserted. This overapproximates for soundness, since the casts do not cause UB themselves, but an alternative solution involves tracking every MIR place, which is costly. Resolves #3324 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../transform/check_uninit/uninit_visitor.rs | 70 +++++++++++++++++-- .../uninit/access-padding-via-cast/expected | 2 +- .../expected/uninit/delayed-ub/delayed-ub.rs | 14 ++++ tests/expected/uninit/delayed-ub/expected | 5 ++ 4 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 tests/expected/uninit/delayed-ub/delayed-ub.rs create mode 100644 tests/expected/uninit/delayed-ub/expected diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs b/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs index 19b13c6ab8b6..0614724be6bf 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs @@ -12,9 +12,11 @@ use stable_mir::mir::{ NonDivergingIntrinsic, Operand, Place, PointerCoercion, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; -use stable_mir::ty::{ConstantKind, MirConst, RigidTy, Span, TyKind, UintTy}; +use stable_mir::ty::{ConstantKind, MirConst, RigidTy, Span, Ty, TyKind, UintTy}; use strum_macros::AsRefStr; +use super::{PointeeInfo, PointeeLayout}; + /// Memory initialization operations: set or get memory initialization state for a given pointer. #[derive(AsRefStr, Clone, Debug)] pub enum MemoryInitOp { @@ -540,13 +542,34 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { } fn visit_rvalue(&mut self, rvalue: &Rvalue, location: Location) { - if let Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), _, ty) = rvalue { - if let TyKind::RigidTy(RigidTy::RawPtr(pointee_ty, _)) = ty.kind() { - if pointee_ty.kind().is_trait() { - self.push_target(MemoryInitOp::Unsupported { - reason: "Kani does not support reasoning about memory initialization of unsized pointers.".to_string(), - }); + 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 { + reason: "Kani does not support reasoning about memory initialization of unsized pointers.".to_string(), + }); + } + } } + CastKind::PtrToPtr => { + let operand_ty = operand.ty(&self.locals).unwrap(); + if let ( + RigidTy::RawPtr(from_ty, Mutability::Mut), + RigidTy::RawPtr(to_ty, Mutability::Mut), + ) = (operand_ty.kind().rigid().unwrap(), ty.kind().rigid().unwrap()) + { + if !tys_layout_compatible(from_ty, to_ty) { + // If casting from a mutable pointer to a mutable pointer with + // different layouts, delayed UB could occur. + self.push_target(MemoryInitOp::Unsupported { + reason: "Kani does not support reasoning about memory initialization in presence of mutable raw pointer casts that could cause delayed UB.".to_string(), + }); + } + } + } + _ => {} } }; self.super_rvalue(rvalue, location); @@ -711,3 +734,36 @@ fn try_resolve_instance(locals: &[LocalDecl], func: &Operand) -> Result bool { + // Retrieve layouts to assess compatibility. + let from_ty_info = PointeeInfo::from_ty(*from_ty); + let to_ty_info = PointeeInfo::from_ty(*to_ty); + if let (Ok(from_ty_info), Ok(to_ty_info)) = (from_ty_info, to_ty_info) { + let from_ty_layout = match from_ty_info.layout() { + PointeeLayout::Sized { layout } => layout, + PointeeLayout::Slice { element_layout } => element_layout, + PointeeLayout::TraitObject => return false, + }; + let to_ty_layout = match to_ty_info.layout() { + PointeeLayout::Sized { layout } => layout, + PointeeLayout::Slice { element_layout } => element_layout, + PointeeLayout::TraitObject => return false, + }; + // Ensure `to_ty_layout` does not have a larger size. + if to_ty_layout.len() <= from_ty_layout.len() { + // Check data and padding bytes pair-wise. + if from_ty_layout.iter().zip(to_ty_layout.iter()).all( + |(from_ty_layout_byte, to_ty_layout_byte)| { + // Make sure all data and padding bytes match. + from_ty_layout_byte == to_ty_layout_byte + }, + ) { + return true; + } + } + }; + false +} diff --git a/tests/expected/uninit/access-padding-via-cast/expected b/tests/expected/uninit/access-padding-via-cast/expected index 12c5c0a4a439..e02883b26cdf 100644 --- a/tests/expected/uninit/access-padding-via-cast/expected +++ b/tests/expected/uninit/access-padding-via-cast/expected @@ -1,4 +1,4 @@ -Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*mut [u8; 4]` +Failed Checks: Kani does not support reasoning about memory initialization in presence of mutable raw pointer casts that could cause delayed UB. VERIFICATION:- FAILED diff --git a/tests/expected/uninit/delayed-ub/delayed-ub.rs b/tests/expected/uninit/delayed-ub/delayed-ub.rs new file mode 100644 index 000000000000..bfed0a1f39a1 --- /dev/null +++ b/tests/expected/uninit/delayed-ub/delayed-ub.rs @@ -0,0 +1,14 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z ghost-state -Z uninit-checks + +/// Checks that Kani rejects mutable pointer casts between types of different padding. +#[kani::proof] +fn invalid_value() { + unsafe { + let mut value: u128 = 0; + let ptr = &mut value as *mut _ as *mut (u8, u32, u64); + *ptr = (4, 4, 4); // This assignment itself does not cause UB... + let c: u128 = value; // ...but this reads a padding value! + } +} diff --git a/tests/expected/uninit/delayed-ub/expected b/tests/expected/uninit/delayed-ub/expected new file mode 100644 index 000000000000..e02883b26cdf --- /dev/null +++ b/tests/expected/uninit/delayed-ub/expected @@ -0,0 +1,5 @@ +Failed Checks: Kani does not support reasoning about memory initialization in presence of mutable raw pointer casts that could cause delayed UB. + +VERIFICATION:- FAILED + +Complete - 0 successfully verified harnesses, 1 failures, 1 total. \ No newline at end of file From 3b4042ca701c4ae232263748e97a80511873c80c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 19:08:35 +0000 Subject: [PATCH 177/225] Automatic cargo update to 2024-07-08 (#3329) Dependency upgrade resulting from `cargo update`. --- Cargo.lock | 74 +++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cf47e75b0562..fd76b5e75b43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,7 +169,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.69", ] [[package]] @@ -482,7 +482,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.69", ] [[package]] @@ -909,29 +909,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.69", ] [[package]] name = "serde_json" -version = "1.0.119" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "itoa", "ryu", @@ -1030,7 +1030,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.68", + "syn 2.0.69", ] [[package]] @@ -1045,9 +1045,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.68" +version = "2.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "201fcda3845c23e8212cd466bfebf0bd20694490fc0356ae8e428e0824a915a6" dependencies = [ "proc-macro2", "quote", @@ -1083,7 +1083,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.69", ] [[package]] @@ -1149,7 +1149,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.69", ] [[package]] @@ -1320,9 +1320,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -1336,51 +1336,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -1399,20 +1399,20 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "zerocopy" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.68", + "syn 2.0.69", ] From 068f63cc32ab638b039ef29e1fb061559efe14d6 Mon Sep 17 00:00:00 2001 From: Artem Agvanian Date: Fri, 12 Jul 2024 09:01:32 -0700 Subject: [PATCH 178/225] Improve performance and language support of memory initialization checks (#3313) This is a follow-up PR for #3264, a part of the larger work towards #3300. This PR introduces: - Use of demonic non-determinism (prophecy variables) to improve memory initialization checks performance; - Instead of keeping track of memory initialization of each possibly uninitialized byte pointed to by some pointer, one is chosen non-deterministically; - Tests for proper memory initialization checks injection for compiler intrinsics; - Separate functions for checking memory initialization of slice chunks. 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> --- .../codegen_cprover_gotoc/codegen/contract.rs | 2 +- .../codegen_cprover_gotoc/overrides/hooks.rs | 36 +++ .../src/kani_middle/transform/body.rs | 21 +- .../kani_middle/transform/check_uninit/mod.rs | 161 ++++++++-- .../transform/check_uninit/uninit_visitor.rs | 169 +++++----- .../kani_middle/transform/kani_intrinsics.rs | 6 +- .../src/kani_middle/transform/mod.rs | 8 +- kani-driver/src/call_single_file.rs | 5 +- library/kani/src/lib.rs | 24 ++ library/kani/src/mem.rs | 18 +- library/kani/src/mem_init.rs | 288 +++++++++++++----- library/kani_core/src/lib.rs | 24 ++ library/kani_core/src/mem.rs | 19 +- .../access-padding-uninit.rs | 2 +- .../access-padding-via-cast.rs | 2 +- .../uninit/alloc-to-slice/alloc-to-slice.rs | 4 +- tests/expected/uninit/atomic/atomic.rs | 31 ++ tests/expected/uninit/atomic/expected | 13 + tests/expected/uninit/intrinsics/expected | 70 +++++ .../expected/uninit/intrinsics/intrinsics.rs | 127 ++++++++ .../vec-read-bad-len/vec-read-bad-len.rs | 2 +- .../vec-read-semi-init/vec-read-semi-init.rs | 2 +- .../uninit/vec-read-uninit/vec-read-uninit.rs | 2 +- .../access-padding-enum-diverging-variants.rs | 2 +- .../access-padding-enum-multiple-variants.rs | 2 +- .../access-padding-enum-single-field.rs | 2 +- .../access-padding-enum-single-variant.rs | 2 +- tests/kani/Uninit/access-padding-init.rs | 15 + tests/kani/Uninit/alloc-to-slice.rs | 19 ++ tests/kani/Uninit/alloc-zeroed-to-slice.rs | 22 ++ tests/kani/Uninit/atomic.rs | 66 ++++ .../Uninit/struct-padding-and-arr-init.rs | 21 ++ tests/kani/Uninit/vec-read-init.rs | 11 + tests/perf/uninit/Cargo.toml | 14 - tests/perf/uninit/expected | 1 - tests/perf/uninit/src/lib.rs | 68 ----- tests/std-checks/core/slice.expected | 2 +- tests/std-checks/core/src/slice.rs | 20 +- 38 files changed, 1013 insertions(+), 290 deletions(-) create mode 100644 tests/expected/uninit/atomic/atomic.rs create mode 100644 tests/expected/uninit/atomic/expected create mode 100644 tests/expected/uninit/intrinsics/expected create mode 100644 tests/expected/uninit/intrinsics/intrinsics.rs create mode 100644 tests/kani/Uninit/access-padding-init.rs create mode 100644 tests/kani/Uninit/alloc-to-slice.rs create mode 100644 tests/kani/Uninit/alloc-zeroed-to-slice.rs create mode 100644 tests/kani/Uninit/atomic.rs create mode 100644 tests/kani/Uninit/struct-padding-and-arr-init.rs create mode 100644 tests/kani/Uninit/vec-read-init.rs delete mode 100644 tests/perf/uninit/Cargo.toml delete mode 100644 tests/perf/uninit/expected delete mode 100644 tests/perf/uninit/src/lib.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs index d35015aa040d..c7e7d5b817bd 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs @@ -122,7 +122,7 @@ impl<'tcx> GotocCtx<'tcx> { .tcx .all_diagnostic_items(()) .name_to_id - .get(&rustc_span::symbol::Symbol::intern("KaniMemInitShadowMem")) + .get(&rustc_span::symbol::Symbol::intern("KaniMemoryInitializationState")) .map(|attr_id| { self.tcx .symbol_name(rustc_middle::ty::Instance::mono(self.tcx, *attr_id)) diff --git a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs index fc245fc6f4c9..c0df279932f6 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs @@ -143,6 +143,41 @@ impl GotocHook for Assert { } } +struct Check; +impl GotocHook for Check { + fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool { + matches_function(tcx, instance.def, "KaniCheck") + } + + fn handle( + &self, + gcx: &mut GotocCtx, + _instance: Instance, + mut fargs: Vec, + _assign_to: &Place, + target: Option, + span: Span, + ) -> Stmt { + assert_eq!(fargs.len(), 2); + let cond = fargs.remove(0).cast_to(Type::bool()); + let msg = fargs.remove(0); + let msg = gcx.extract_const_message(&msg).unwrap(); + let target = target.unwrap(); + let caller_loc = gcx.codegen_caller_span_stable(span); + + let (msg, reach_stmt) = gcx.codegen_reachability_check(msg, span); + + Stmt::block( + vec![ + reach_stmt, + gcx.codegen_assert(cond, PropertyClass::Assertion, &msg, caller_loc), + Stmt::goto(bb_label(target), caller_loc), + ], + caller_loc, + ) + } +} + struct Nondet; impl GotocHook for Nondet { @@ -510,6 +545,7 @@ pub fn fn_hooks() -> GotocHooks { Rc::new(Panic), Rc::new(Assume), Rc::new(Assert), + Rc::new(Check), Rc::new(Cover), Rc::new(Nondet), Rc::new(IsAllocated), diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index 22895bd8d20d..d82dda80cc05 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -387,12 +387,13 @@ pub enum CheckType { } impl CheckType { - /// This will create the type of check that is available in the current crate. + /// 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. /// /// 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(tcx: TyCtxt) -> CheckType { + pub fn new_assert_assume(tcx: TyCtxt) -> CheckType { if let Some(instance) = find_instance(tcx, "KaniAssert") { CheckType::Assert(instance) } else if find_instance(tcx, "panic_str").is_some() { @@ -401,6 +402,22 @@ impl CheckType { CheckType::NoCore } } + + /// 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(tcx: TyCtxt) -> CheckType { + if let Some(instance) = find_instance(tcx, "KaniCheck") { + CheckType::Assert(instance) + } else if find_instance(tcx, "panic_str").is_some() { + CheckType::Panic + } else { + CheckType::NoCore + } + } } /// We store the index of an instruction to avoid borrow checker issues and unnecessary copies. 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 6665ab697287..e1b265751fd5 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs @@ -29,8 +29,17 @@ mod uninit_visitor; pub use ty_layout::{PointeeInfo, PointeeLayout}; use uninit_visitor::{CheckUninitVisitor, InitRelevantInstruction, MemoryInitOp}; -const SKIPPED_DIAGNOSTIC_ITEMS: &[&str] = - &["KaniIsUnitPtrInitialized", "KaniSetUnitPtrInitialized"]; +// Function bodies of those functions will not be instrumented as not to cause infinite recursion. +const SKIPPED_DIAGNOSTIC_ITEMS: &[&str] = &[ + "KaniIsPtrInitialized", + "KaniSetPtrInitialized", + "KaniIsSliceChunkPtrInitialized", + "KaniSetSliceChunkPtrInitialized", + "KaniIsSlicePtrInitialized", + "KaniSetSlicePtrInitialized", + "KaniIsStrPtrInitialized", + "KaniSetStrPtrInitialized", +]; /// Instrument the code with checks for uninitialized memory. #[derive(Debug)] @@ -59,8 +68,8 @@ impl TransformPass for UninitPass { fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { trace!(function=?instance.name(), "transform"); - // Need to break infinite recursion when shadow memory checks are inserted, - // so the internal function responsible for shadow memory checks are skipped. + // Need to break infinite recursion when memory initialization checks are inserted, so the + // internal functions responsible for memory initialization are skipped. if tcx .get_diagnostic_name(rustc_internal::internal(tcx, instance.def.def_id())) .map(|diagnostic_name| { @@ -74,6 +83,11 @@ impl TransformPass for UninitPass { let mut new_body = MutableBody::from(body); let orig_len = new_body.blocks().len(); + // Inject a call to set-up memory initialization state if the function is a harness. + if is_harness(instance, tcx) { + inject_memory_init_setup(&mut new_body, tcx, &mut self.mem_init_fn_cache); + } + // Set of basic block indices for which analyzing first statement should be skipped. // // This is necessary because some checks are inserted before the source instruction, which, in @@ -159,10 +173,12 @@ impl UninitPass { }; match operation { - MemoryInitOp::Check { .. } => { + MemoryInitOp::CheckSliceChunk { .. } | MemoryInitOp::Check { .. } => { self.build_get_and_check(tcx, body, source, operation, pointee_ty_info, skip_first) } - MemoryInitOp::Set { .. } | MemoryInitOp::SetRef { .. } => { + MemoryInitOp::SetSliceChunk { .. } + | MemoryInitOp::Set { .. } + | MemoryInitOp::SetRef { .. } => { self.build_set(tcx, body, source, operation, pointee_ty_info, skip_first) } MemoryInitOp::Unsupported { .. } => { @@ -171,8 +187,8 @@ impl UninitPass { } } - /// Inject a load from shadow memory tracking memory initialization and an assertion that all - /// non-padding bytes are initialized. + /// Inject a load from memory initialization state and an assertion that all non-padding bytes + /// are initialized. fn build_get_and_check( &mut self, tcx: TyCtxt, @@ -189,18 +205,34 @@ impl UninitPass { let ptr_operand = operation.mk_operand(body, source); match pointee_info.layout() { PointeeLayout::Sized { layout } => { + let layout_operand = mk_layout_operand(body, source, operation.position(), &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 { + MemoryInitOp::Check { .. } => { + let diagnostic = "KaniIsPtrInitialized"; + let args = vec![ptr_operand.clone(), layout_operand]; + (diagnostic, args) + } + MemoryInitOp::CheckSliceChunk { .. } => { + let diagnostic = "KaniIsSliceChunkPtrInitialized"; + let args = + vec![ptr_operand.clone(), layout_operand, operation.expect_count()]; + (diagnostic, args) + } + _ => unreachable!(), + }; let is_ptr_initialized_instance = resolve_mem_init_fn( - get_mem_init_fn_def(tcx, "KaniIsPtrInitialized", &mut self.mem_init_fn_cache), + get_mem_init_fn_def(tcx, diagnostic, &mut self.mem_init_fn_cache), layout.len(), *pointee_info.ty(), ); - let layout_operand = mk_layout_operand(body, source, operation.position(), &layout); collect_skipped(&operation, body, skip_first); body.add_call( &is_ptr_initialized_instance, source, operation.position(), - vec![ptr_operand.clone(), layout_operand, operation.expect_count()], + args, ret_place.clone(), ); } @@ -252,8 +284,8 @@ impl UninitPass { ) } - /// Inject a store into shadow memory tracking memory initialization to initialize or - /// deinitialize all non-padding bytes. + /// Inject a store into memory initialization state to initialize or deinitialize all + /// non-padding bytes. fn build_set( &mut self, tcx: TyCtxt, @@ -272,27 +304,50 @@ impl UninitPass { match pointee_info.layout() { PointeeLayout::Sized { layout } => { + let layout_operand = mk_layout_operand(body, source, operation.position(), &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 { + MemoryInitOp::Set { .. } | MemoryInitOp::SetRef { .. } => { + let diagnostic = "KaniSetPtrInitialized"; + let args = vec![ + ptr_operand, + layout_operand, + Operand::Constant(ConstOperand { + span: source.span(body.blocks()), + user_ty: None, + const_: MirConst::from_bool(value), + }), + ]; + (diagnostic, args) + } + MemoryInitOp::SetSliceChunk { .. } => { + let diagnostic = "KaniSetSliceChunkPtrInitialized"; + let args = vec![ + ptr_operand, + layout_operand, + operation.expect_count(), + Operand::Constant(ConstOperand { + span: source.span(body.blocks()), + user_ty: None, + const_: MirConst::from_bool(value), + }), + ]; + (diagnostic, args) + } + _ => unreachable!(), + }; let set_ptr_initialized_instance = resolve_mem_init_fn( - get_mem_init_fn_def(tcx, "KaniSetPtrInitialized", &mut self.mem_init_fn_cache), + get_mem_init_fn_def(tcx, diagnostic, &mut self.mem_init_fn_cache), layout.len(), *pointee_info.ty(), ); - let layout_operand = mk_layout_operand(body, source, operation.position(), &layout); collect_skipped(&operation, body, skip_first); body.add_call( &set_ptr_initialized_instance, source, operation.position(), - vec![ - ptr_operand, - layout_operand, - operation.expect_count(), - Operand::Constant(ConstOperand { - span: source.span(body.blocks()), - user_ty: None, - const_: MirConst::from_bool(value), - }), - ], + args, ret_place, ); } @@ -426,3 +481,59 @@ pub fn resolve_mem_init_fn(fn_def: FnDef, layout_size: usize, associated_type: T ) .unwrap() } + +/// Checks if the instance is a harness -- an entry point of Kani analysis. +fn is_harness(instance: Instance, tcx: TyCtxt) -> bool { + let harness_identifiers = [ + vec![ + rustc_span::symbol::Symbol::intern("kanitool"), + rustc_span::symbol::Symbol::intern("proof_for_contract"), + ], + vec![ + rustc_span::symbol::Symbol::intern("kanitool"), + rustc_span::symbol::Symbol::intern("proof"), + ], + ]; + harness_identifiers.iter().any(|attr_path| { + tcx.has_attrs_with_path(rustc_internal::internal(tcx, instance.def.def_id()), attr_path) + }) +} + +/// Inject an initial call to set-up memory initialization tracking. +fn inject_memory_init_setup( + new_body: &mut MutableBody, + tcx: TyCtxt, + mem_init_fn_cache: &mut HashMap<&'static str, FnDef>, +) { + // First statement or terminator in the harness. + let mut source = if !new_body.blocks()[0].statements.is_empty() { + SourceInstruction::Statement { idx: 0, bb: 0 } + } else { + SourceInstruction::Terminator { bb: 0 } + }; + + // Dummy return place. + let ret_place = Place { + local: new_body.new_local( + Ty::new_tuple(&[]), + source.span(new_body.blocks()), + Mutability::Not, + ), + projection: vec![], + }; + + // Resolve the instance and inject a call to set-up the memory initialization state. + let memory_initialization_init = Instance::resolve( + get_mem_init_fn_def(tcx, "KaniInitializeMemoryInitializationState", mem_init_fn_cache), + &GenericArgs(vec![]), + ) + .unwrap(); + + new_body.add_call( + &memory_initialization_init, + &mut source, + InsertPosition::Before, + vec![], + ret_place, + ); +} diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs b/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs index 0614724be6bf..f68869e6681d 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs @@ -8,11 +8,11 @@ use stable_mir::mir::alloc::GlobalAlloc; use stable_mir::mir::mono::{Instance, InstanceKind}; use stable_mir::mir::visit::{Location, PlaceContext}; use stable_mir::mir::{ - BasicBlockIdx, CastKind, ConstOperand, LocalDecl, MirVisitor, Mutability, - NonDivergingIntrinsic, Operand, Place, PointerCoercion, ProjectionElem, Rvalue, Statement, - StatementKind, Terminator, TerminatorKind, + BasicBlockIdx, CastKind, LocalDecl, MirVisitor, Mutability, NonDivergingIntrinsic, Operand, + Place, PointerCoercion, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, + TerminatorKind, }; -use stable_mir::ty::{ConstantKind, MirConst, RigidTy, Span, Ty, TyKind, UintTy}; +use stable_mir::ty::{ConstantKind, RigidTy, Ty, TyKind}; use strum_macros::AsRefStr; use super::{PointeeInfo, PointeeLayout}; @@ -20,15 +20,21 @@ use super::{PointeeInfo, PointeeLayout}; /// Memory initialization operations: set or get memory initialization state for a given pointer. #[derive(AsRefStr, Clone, Debug)] pub enum MemoryInitOp { + /// Check memory initialization of data bytes in a memory region starting from the pointer + /// `operand` and of length `sizeof(operand)` bytes. + Check { operand: Operand }, + /// Set memory initialization state of data bytes in a memory region starting from the pointer + /// `operand` and of length `sizeof(operand)` bytes. + Set { operand: Operand, value: bool, position: InsertPosition }, /// Check memory initialization of data bytes in a memory region starting from the pointer /// `operand` and of length `count * sizeof(operand)` bytes. - Check { operand: Operand, count: Operand }, + CheckSliceChunk { operand: Operand, count: Operand }, /// Set memory initialization state of data bytes in a memory region starting from the pointer /// `operand` and of length `count * sizeof(operand)` bytes. - Set { operand: Operand, count: Operand, value: bool, position: InsertPosition }, + SetSliceChunk { operand: Operand, count: Operand, value: bool, position: InsertPosition }, /// Set memory initialization of data bytes in a memory region starting from the reference to - /// `operand` and of length `count * sizeof(operand)` bytes. - SetRef { operand: Operand, count: Operand, value: bool, position: InsertPosition }, + /// `operand` and of length `sizeof(operand)` bytes. + SetRef { operand: Operand, value: bool, position: InsertPosition }, /// Unsupported memory initialization operation. Unsupported { reason: String }, } @@ -39,9 +45,10 @@ impl MemoryInitOp { /// `MemoryInitOp::SetRef`. pub fn mk_operand(&self, body: &mut MutableBody, source: &mut SourceInstruction) -> Operand { match self { - MemoryInitOp::Check { operand, .. } | MemoryInitOp::Set { operand, .. } => { - operand.clone() - } + MemoryInitOp::Check { operand, .. } + | MemoryInitOp::Set { operand, .. } + | MemoryInitOp::CheckSliceChunk { operand, .. } + | MemoryInitOp::SetSliceChunk { operand, .. } => operand.clone(), MemoryInitOp::SetRef { operand, .. } => Operand::Copy(Place { local: { let place = match operand { @@ -62,24 +69,34 @@ impl MemoryInitOp { pub fn expect_count(&self) -> Operand { match self { - MemoryInitOp::Check { count, .. } - | MemoryInitOp::Set { count, .. } - | MemoryInitOp::SetRef { count, .. } => count.clone(), - MemoryInitOp::Unsupported { .. } => unreachable!(), + MemoryInitOp::CheckSliceChunk { count, .. } + | MemoryInitOp::SetSliceChunk { count, .. } => count.clone(), + MemoryInitOp::Check { .. } + | MemoryInitOp::Set { .. } + | MemoryInitOp::SetRef { .. } + | MemoryInitOp::Unsupported { .. } => unreachable!(), } } pub fn expect_value(&self) -> bool { match self { - MemoryInitOp::Set { value, .. } | MemoryInitOp::SetRef { value, .. } => *value, - MemoryInitOp::Check { .. } | MemoryInitOp::Unsupported { .. } => unreachable!(), + MemoryInitOp::Set { value, .. } + | MemoryInitOp::SetSliceChunk { value, .. } + | MemoryInitOp::SetRef { value, .. } => *value, + MemoryInitOp::Check { .. } + | MemoryInitOp::CheckSliceChunk { .. } + | MemoryInitOp::Unsupported { .. } => unreachable!(), } } pub fn position(&self) -> InsertPosition { match self { - MemoryInitOp::Set { position, .. } | MemoryInitOp::SetRef { position, .. } => *position, - MemoryInitOp::Check { .. } | MemoryInitOp::Unsupported { .. } => InsertPosition::Before, + MemoryInitOp::Set { position, .. } + | MemoryInitOp::SetSliceChunk { position, .. } + | MemoryInitOp::SetRef { position, .. } => *position, + MemoryInitOp::Check { .. } + | MemoryInitOp::CheckSliceChunk { .. } + | MemoryInitOp::Unsupported { .. } => InsertPosition::Before, } } } @@ -158,12 +175,12 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { StatementKind::Intrinsic(NonDivergingIntrinsic::CopyNonOverlapping(copy)) => { self.super_statement(stmt, location); // Source is a *const T and it must be initialized. - self.push_target(MemoryInitOp::Check { + self.push_target(MemoryInitOp::CheckSliceChunk { operand: copy.src.clone(), count: copy.count.clone(), }); // Destimation is a *mut T so it gets initialized. - self.push_target(MemoryInitOp::Set { + self.push_target(MemoryInitOp::SetSliceChunk { operand: copy.dst.clone(), count: copy.count.clone(), value: true, @@ -188,7 +205,6 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { { self.push_target(MemoryInitOp::Check { operand: Operand::Copy(place_to_add_projections.clone()), - count: mk_const_operand(1, location.span()), }); }; } @@ -197,7 +213,6 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { if place_without_deref.ty(&self.locals).unwrap().kind().is_raw_ptr() { self.push_target(MemoryInitOp::Set { operand: Operand::Copy(place_without_deref), - count: mk_const_operand(1, location.span()), value: true, position: InsertPosition::After, }); @@ -208,7 +223,6 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { if let Rvalue::AddressOf(..) = rvalue { self.push_target(MemoryInitOp::Set { operand: Operand::Copy(place.clone()), - count: mk_const_operand(1, location.span()), value: true, position: InsertPosition::After, }); @@ -219,7 +233,6 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { self.super_statement(stmt, location); self.push_target(MemoryInitOp::Set { operand: Operand::Copy(place.clone()), - count: mk_const_operand(1, location.span()), value: false, position: InsertPosition::After, }); @@ -263,8 +276,14 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { /* Intrinsics that can be safely skipped */ } name if name.starts_with("atomic") => { - let num_args = - if name.starts_with("atomic_cxchg") { 3 } else { 2 }; + let num_args = match name { + // All `atomic_cxchg` intrinsics take `dst, old, src` as arguments. + name if name.starts_with("atomic_cxchg") => 3, + // All `atomic_load` intrinsics take `src` as an argument. + name if name.starts_with("atomic_load") => 1, + // All other `atomic` intrinsics take `dst, src` as arguments. + _ => 2, + }; assert_eq!( args.len(), num_args, @@ -276,7 +295,6 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { )); self.push_target(MemoryInitOp::Check { operand: args[0].clone(), - count: mk_const_operand(1, location.span()), }); } "compare_bytes" => { @@ -293,11 +311,11 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { args[1].ty(self.locals).unwrap().kind(), TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Not)) )); - self.push_target(MemoryInitOp::Check { + self.push_target(MemoryInitOp::CheckSliceChunk { operand: args[0].clone(), count: args[2].clone(), }); - self.push_target(MemoryInitOp::Check { + self.push_target(MemoryInitOp::CheckSliceChunk { operand: args[1].clone(), count: args[2].clone(), }); @@ -318,11 +336,11 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { args[1].ty(self.locals).unwrap().kind(), TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Mut)) )); - self.push_target(MemoryInitOp::Check { + self.push_target(MemoryInitOp::CheckSliceChunk { operand: args[0].clone(), count: args[2].clone(), }); - self.push_target(MemoryInitOp::Set { + self.push_target(MemoryInitOp::SetSliceChunk { operand: args[1].clone(), count: args[2].clone(), value: true, @@ -345,29 +363,12 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { )); self.push_target(MemoryInitOp::Check { operand: args[0].clone(), - count: mk_const_operand(1, location.span()), }); self.push_target(MemoryInitOp::Check { operand: args[1].clone(), - count: mk_const_operand(1, location.span()), }); } - "unaligned_volatile_load" => { - assert_eq!( - args.len(), - 1, - "Unexpected number of arguments for `unaligned_volatile_load`" - ); - assert!(matches!( - args[0].ty(self.locals).unwrap().kind(), - TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Not)) - )); - self.push_target(MemoryInitOp::Check { - operand: args[0].clone(), - count: mk_const_operand(1, location.span()), - }); - } - "volatile_load" => { + "volatile_load" | "unaligned_volatile_load" => { assert_eq!( args.len(), 1, @@ -379,7 +380,6 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { )); self.push_target(MemoryInitOp::Check { operand: args[0].clone(), - count: mk_const_operand(1, location.span()), }); } "volatile_store" => { @@ -394,7 +394,6 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { )); self.push_target(MemoryInitOp::Set { operand: args[0].clone(), - count: mk_const_operand(1, location.span()), value: true, position: InsertPosition::After, }); @@ -409,7 +408,7 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { args[0].ty(self.locals).unwrap().kind(), TyKind::RigidTy(RigidTy::RawPtr(_, Mutability::Mut)) )); - self.push_target(MemoryInitOp::Set { + self.push_target(MemoryInitOp::SetSliceChunk { operand: args[0].clone(), count: args[2].clone(), value: true, @@ -432,7 +431,7 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { } "alloc::alloc::__rust_alloc_zeroed" => { /* Memory is initialized here, need to update shadow memory. */ - self.push_target(MemoryInitOp::Set { + self.push_target(MemoryInitOp::SetSliceChunk { operand: Operand::Copy(destination.clone()), count: args[0].clone(), value: true, @@ -441,7 +440,7 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { } "alloc::alloc::__rust_dealloc" => { /* Memory is uninitialized here, need to update shadow memory. */ - self.push_target(MemoryInitOp::Set { + self.push_target(MemoryInitOp::SetSliceChunk { operand: args[0].clone(), count: args[1].clone(), value: false, @@ -458,18 +457,30 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { TerminatorKind::Drop { place, .. } => { self.super_terminator(term, location); let place_ty = place.ty(&self.locals).unwrap(); - // When drop is codegen'ed, a reference is taken to the place which is later implicitly coerced to a pointer. - // Hence, we need to bless this pointer as initialized. - self.push_target(MemoryInitOp::SetRef { - operand: Operand::Copy(place.clone()), - count: mk_const_operand(1, location.span()), - value: true, - position: InsertPosition::Before, - }); + + // 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) + .unwrap() + .kind() + .rigid() + .expect("should be working with monomorphized code") + { + RigidTy::Adt(..) | RigidTy::Dynamic(_, _, _) => { + self.push_target(MemoryInitOp::SetRef { + operand: Operand::Copy(place.clone()), + value: true, + position: InsertPosition::Before, + }); + } + _ => {} + } + if place_ty.kind().is_raw_ptr() { self.push_target(MemoryInitOp::Set { operand: Operand::Copy(place.clone()), - count: mk_const_operand(1, location.span()), value: false, position: InsertPosition::After, }); @@ -497,7 +508,6 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { if ptr_ty.kind().is_raw_ptr() { self.push_target(MemoryInitOp::Check { operand: Operand::Copy(intermediate_place.clone()), - count: mk_const_operand(1, location.span()), }); } } @@ -528,12 +538,14 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { if let ConstantKind::Allocated(allocation) = constant.const_.kind() { for (_, prov) in &allocation.provenance.ptrs { if let GlobalAlloc::Static(_) = GlobalAlloc::from(prov.0) { - self.push_target(MemoryInitOp::Set { - operand: Operand::Constant(constant.clone()), - count: mk_const_operand(1, location.span()), - value: true, - position: InsertPosition::Before, - }); + 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, + }); + } }; } } @@ -679,6 +691,12 @@ fn can_skip_intrinsic(intrinsic_name: &str) -> bool { /* SIMD operations */ true } + name if name.starts_with("atomic_fence") + || name.starts_with("atomic_singlethreadfence") => + { + /* Atomic fences */ + true + } "copy_nonoverlapping" => unreachable!( "Expected `core::intrinsics::unreachable` to be handled by `StatementKind::CopyNonOverlapping`" ), @@ -705,15 +723,6 @@ fn can_skip_intrinsic(intrinsic_name: &str) -> bool { } } -/// Create a constant operand with a given value and span. -fn mk_const_operand(value: usize, span: Span) -> Operand { - Operand::Constant(ConstOperand { - span, - user_ty: None, - const_: MirConst::try_from_uint(value as u128, UintTy::Usize).unwrap(), - }) -} - /// Try removing a topmost deref projection from a place if it exists, returning a place without it. fn try_remove_topmost_deref(place: &Place) -> Option { let mut new_place = place.clone(); diff --git a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs index ea7bf8625228..c4534bf11b4d 100644 --- a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs +++ b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs @@ -216,11 +216,7 @@ impl IntrinsicGeneratorPass { &is_ptr_initialized_instance, &mut terminator, InsertPosition::Before, - vec![ - Operand::Copy(Place::from(1)), - layout_operand, - Operand::Copy(Place::from(2)), - ], + vec![Operand::Copy(Place::from(1)), layout_operand], Place::from(ret_var), ); } diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index 56ab0be493c4..26a95978fcaf 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -60,7 +60,7 @@ impl BodyTransformation { inst_passes: vec![], cache: Default::default(), }; - let check_type = CheckType::new(tcx); + let check_type = CheckType::new_assert_assume(tcx); transformer.add_pass(queries, FnStubPass::new(&unit.stubs)); transformer.add_pass(queries, ExternFnStubPass::new(&unit.stubs)); // This has to come after stubs since we want this to replace the stubbed body. @@ -73,7 +73,11 @@ impl BodyTransformation { // generated code for future instrumentation passes. transformer.add_pass( queries, - UninitPass { check_type: check_type.clone(), mem_init_fn_cache: HashMap::new() }, + UninitPass { + // Since this uses demonic non-determinism under the hood, should not assume the assertion. + check_type: CheckType::new_assert(tcx), + mem_init_fn_cache: HashMap::new(), + }, ); transformer.add_pass( queries, diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index 7cbe150427e9..411dabb5270e 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -140,7 +140,10 @@ impl KaniSession { } if self.args.common_args.unstable_features.contains(UnstableFeature::UninitChecks) { - flags.push("--ub-check=uninit".into()) + // Automatically enable shadow memory, since the version of uninitialized memory checks + // without non-determinism depends on it. + flags.push("-Z ghost-state".into()); + flags.push("--ub-check=uninit".into()); } if self.args.ignore_locals_lifetime { diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index 6eab2a331811..7487cc26b186 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -123,6 +123,30 @@ pub const fn assert(cond: bool, msg: &'static str) { assert!(cond, "{}", msg); } +/// Creates an assertion of the specified condition, but does not assume it afterwards. +/// +/// # Example: +/// +/// ```rust +/// let x: bool = kani::any(); +/// let y = !x; +/// kani::check(x || y, "ORing a boolean variable with its negation must be true") +/// ``` +#[cfg(not(feature = "concrete_playback"))] +#[inline(never)] +#[rustc_diagnostic_item = "KaniCheck"] +pub const fn check(cond: bool, msg: &'static str) { + let _ = cond; + let _ = msg; +} + +#[cfg(feature = "concrete_playback")] +#[inline(never)] +#[rustc_diagnostic_item = "KaniCheck"] +pub const fn check(cond: bool, msg: &'static str) { + assert!(cond, "{}", msg); +} + /// Creates a cover property with the specified condition and message. /// /// # Example: diff --git a/library/kani/src/mem.rs b/library/kani/src/mem.rs index 0b390e74288d..f861f6b2ba25 100644 --- a/library/kani/src/mem.rs +++ b/library/kani/src/mem.rs @@ -118,9 +118,11 @@ where ::Metadata: PtrProperties, { let (thin_ptr, metadata) = ptr.to_raw_parts(); + // Need to assert `is_initialized` because non-determinism is used under the hood, so it does + // not make sense to use it inside assumption context. metadata.is_ptr_aligned(thin_ptr, Internal) && is_inbounds(&metadata, thin_ptr) - && is_initialized(ptr, 1) + && assert_is_initialized(ptr) && unsafe { has_valid_value(ptr) } } @@ -148,7 +150,11 @@ where ::Metadata: PtrProperties, { let (thin_ptr, metadata) = ptr.to_raw_parts(); - is_inbounds(&metadata, thin_ptr) && is_initialized(ptr, 1) && unsafe { has_valid_value(ptr) } + // Need to assert `is_initialized` because non-determinism is used under the hood, so it does + // not make sense to use it inside assumption context. + is_inbounds(&metadata, thin_ptr) + && assert_is_initialized(ptr) + && unsafe { has_valid_value(ptr) } } /// Checks that `data_ptr` points to an allocation that can hold data of size calculated from `T`. @@ -294,10 +300,16 @@ unsafe fn has_valid_value(_ptr: *const T) -> bool { /// Check whether `len * size_of::()` bytes are initialized starting from `ptr`. #[rustc_diagnostic_item = "KaniIsInitialized"] #[inline(never)] -pub fn is_initialized(_ptr: *const T, _len: usize) -> bool { +pub fn is_initialized(_ptr: *const T) -> bool { kani_intrinsic() } +/// A helper to assert `is_initialized` to use it as a part of other predicates. +fn assert_is_initialized(ptr: *const T) -> bool { + crate::check(is_initialized(ptr), "Undefined Behavior: Reading from an uninitialized pointer"); + true +} + /// Get the object ID of the given pointer. #[rustc_diagnostic_item = "KaniPointerObject"] #[inline(never)] diff --git a/library/kani/src/mem_init.rs b/library/kani/src/mem_init.rs index 37832ea32604..a09e515d7e17 100644 --- a/library/kani/src/mem_init.rs +++ b/library/kani/src/mem_init.rs @@ -1,20 +1,23 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -//! This module uses shadow memory API to track memory initialization of raw pointers. +//! 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. 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. Padding bytes are always considered -//! uninitialized: type layout is determined at compile time and statically injected into the -//! program (see `Layout`). +//! 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. // Definitions in this module are not meant to be visible to the end user, only the compiler. #![allow(dead_code)] -use crate::shadow::ShadowMem; - /// Bytewise mask, representing which bytes of a type are data and which are padding. /// For example, for a type like this: /// ``` @@ -25,98 +28,243 @@ use crate::shadow::ShadowMem; /// } /// ``` /// the layout would be [true, true, true, false]; -type Layout = [bool; N]; - -/// Global shadow memory object for tracking memory initialization. -#[rustc_diagnostic_item = "KaniMemInitShadowMem"] -static mut MEM_INIT_SHADOW_MEM: ShadowMem = ShadowMem::new(false); - -/// Get initialization state of `len` items laid out according to the `layout` starting at address `ptr`. -#[rustc_diagnostic_item = "KaniIsUnitPtrInitialized"] -fn is_unit_ptr_initialized(ptr: *const (), layout: Layout, len: usize) -> bool { - let mut count: usize = 0; - while count < len { - let mut offset: usize = 0; - while offset < N { - unsafe { - if layout[offset] - && !MEM_INIT_SHADOW_MEM.get((ptr as *const u8).add(count * N + offset)) - { - return false; - } - offset += 1; - } +type Layout = [bool; LAYOUT_SIZE]; + +/// Currently tracked non-deterministically chosen memory initialization state. +struct MemoryInitializationState { + pub tracked_object_id: usize, + pub tracked_offset: usize, + pub value: bool, +} + +impl MemoryInitializationState { + /// This is a dummy initialization function -- the values will be eventually overwritten by a + /// call to `initialize_memory_initialization_state`. + pub const fn new() -> Self { + Self { tracked_object_id: 0, tracked_offset: 0, value: false } + } + + /// Return currently tracked memory initialization state if `ptr` points to the currently + /// tracked object and the tracked offset lies within `LAYOUT_SIZE` bytes of `ptr`. Return + /// `true` otherwise. + /// + /// Such definition is necessary since both tracked object and tracked offset are chosen + /// non-deterministically. + pub fn get( + &mut self, + ptr: *const u8, + layout: Layout, + ) -> bool { + let obj = crate::mem::pointer_object(ptr); + let offset = crate::mem::pointer_offset(ptr); + if self.tracked_object_id == obj + && self.tracked_offset >= offset + && self.tracked_offset < offset + LAYOUT_SIZE + { + !layout[(self.tracked_offset - offset) % LAYOUT_SIZE] || self.value + } else { + true } - count += 1; } - true -} -/// Set initialization state to `value` for `len` items laid out according to the `layout` starting at address `ptr`. -#[rustc_diagnostic_item = "KaniSetUnitPtrInitialized"] -fn set_unit_ptr_initialized( - ptr: *const (), - layout: Layout, - len: usize, - value: bool, -) { - let mut count: usize = 0; - while count < len { - let mut offset: usize = 0; - while offset < N { - unsafe { - MEM_INIT_SHADOW_MEM - .set((ptr as *const u8).add(count * N + offset), value && layout[offset]); - } - offset += 1; + /// Set currently tracked memory initialization state if `ptr` points to the currently tracked + /// object and the tracked offset lies within `LAYOUT_SIZE` bytes of `ptr`. Do nothing + /// otherwise. + /// + /// Such definition is necessary since both tracked object and tracked offset are chosen + /// non-deterministically. + pub fn set( + &mut self, + ptr: *const u8, + layout: Layout, + value: bool, + ) { + let obj = crate::mem::pointer_object(ptr); + let offset = crate::mem::pointer_offset(ptr); + if self.tracked_object_id == obj + && self.tracked_offset >= offset + && self.tracked_offset < offset + LAYOUT_SIZE + { + self.value = layout[(self.tracked_offset - offset) % LAYOUT_SIZE] && value; + } + } + + /// Return currently tracked memory initialization state if `ptr` points to the currently + /// tracked object and the tracked offset lies within `LAYOUT_SIZE * num_elts` bytes of `ptr`. + /// Return `true` otherwise. + /// + /// Such definition is necessary since both tracked object and tracked offset are chosen + /// non-deterministically. + pub fn get_slice( + &mut self, + ptr: *const u8, + layout: Layout, + num_elts: usize, + ) -> bool { + let obj = crate::mem::pointer_object(ptr); + let offset = crate::mem::pointer_offset(ptr); + if self.tracked_object_id == obj + && self.tracked_offset >= offset + && self.tracked_offset < offset + num_elts * LAYOUT_SIZE + { + !layout[(self.tracked_offset - offset) % LAYOUT_SIZE] || self.value + } else { + true + } + } + + /// Set currently tracked memory initialization state if `ptr` points to the currently tracked + /// object and the tracked offset lies within `LAYOUT_SIZE * num_elts` bytes of `ptr`. Do + /// nothing otherwise. + /// + /// Such definition is necessary since both tracked object and tracked offset are chosen + /// non-deterministically. + pub fn set_slice( + &mut self, + ptr: *const u8, + layout: Layout, + num_elts: usize, + value: bool, + ) { + let obj = crate::mem::pointer_object(ptr); + let offset = crate::mem::pointer_offset(ptr); + if self.tracked_object_id == obj + && self.tracked_offset >= offset + && self.tracked_offset < offset + num_elts * LAYOUT_SIZE + { + self.value = layout[(self.tracked_offset - offset) % LAYOUT_SIZE] && value; } - count += 1; } } -/// Get initialization state of `len` items laid out according to the `layout` starting at address `ptr`. +/// Global object for tracking memory initialization state. +#[rustc_diagnostic_item = "KaniMemoryInitializationState"] +static mut MEM_INIT_STATE: MemoryInitializationState = MemoryInitializationState::new(); + +/// Set tracked object and tracked offset to a non-deterministic value. +#[rustc_diagnostic_item = "KaniInitializeMemoryInitializationState"] +fn initialize_memory_initialization_state() { + unsafe { + MEM_INIT_STATE.tracked_object_id = crate::any(); + MEM_INIT_STATE.tracked_offset = crate::any(); + MEM_INIT_STATE.value = false; + } +} + +/// Get initialization state of `num_elts` items laid out according to the `layout` starting at address `ptr`. #[rustc_diagnostic_item = "KaniIsPtrInitialized"] -fn is_ptr_initialized(ptr: *const T, layout: Layout, len: usize) -> bool { +fn is_ptr_initialized( + ptr: *const T, + layout: Layout, +) -> bool { + if LAYOUT_SIZE == 0 { + return true; + } let (ptr, _) = ptr.to_raw_parts(); - is_unit_ptr_initialized(ptr, layout, len) + unsafe { MEM_INIT_STATE.get(ptr as *const u8, layout) } } -/// Set initialization state to `value` for `len` items laid out according to the `layout` starting at address `ptr`. +/// Set initialization state to `value` for `num_elts` items laid out according to the `layout` starting at address `ptr`. #[rustc_diagnostic_item = "KaniSetPtrInitialized"] -fn set_ptr_initialized( +fn set_ptr_initialized( + ptr: *const T, + layout: Layout, + value: bool, +) { + if LAYOUT_SIZE == 0 { + return; + } + let (ptr, _) = ptr.to_raw_parts(); + unsafe { + MEM_INIT_STATE.set(ptr as *const u8, layout, value); + } +} + +/// Get initialization state of `num_elts` items laid out according to the `layout` starting at address `ptr`. +#[rustc_diagnostic_item = "KaniIsSliceChunkPtrInitialized"] +fn is_slice_chunk_ptr_initialized( + ptr: *const T, + layout: Layout, + num_elts: usize, +) -> bool { + if LAYOUT_SIZE == 0 { + return true; + } + let (ptr, _) = ptr.to_raw_parts(); + unsafe { MEM_INIT_STATE.get_slice(ptr as *const u8, layout, num_elts) } +} + +/// Set initialization state to `value` for `num_elts` items laid out according to the `layout` starting at address `ptr`. +#[rustc_diagnostic_item = "KaniSetSliceChunkPtrInitialized"] +fn set_slice_chunk_ptr_initialized( ptr: *const T, - layout: Layout, - len: usize, + layout: Layout, + num_elts: usize, value: bool, ) { + if LAYOUT_SIZE == 0 { + return; + } let (ptr, _) = ptr.to_raw_parts(); - set_unit_ptr_initialized(ptr, layout, len, value); + unsafe { + MEM_INIT_STATE.set_slice(ptr as *const u8, layout, num_elts, value); + } } /// Get initialization state of the slice, items of which are laid out according to the `layout` starting at address `ptr`. #[rustc_diagnostic_item = "KaniIsSlicePtrInitialized"] -fn is_slice_ptr_initialized(ptr: *const [T], layout: Layout) -> bool { - let (ptr, len) = ptr.to_raw_parts(); - is_unit_ptr_initialized(ptr, layout, len) +fn is_slice_ptr_initialized( + ptr: *const [T], + layout: Layout, +) -> bool { + if LAYOUT_SIZE == 0 { + return true; + } + let (ptr, num_elts) = ptr.to_raw_parts(); + unsafe { MEM_INIT_STATE.get_slice(ptr as *const u8, layout, num_elts) } } /// Set initialization state of the slice, items of which are laid out according to the `layout` starting at address `ptr` to `value`. #[rustc_diagnostic_item = "KaniSetSlicePtrInitialized"] -fn set_slice_ptr_initialized(ptr: *const [T], layout: Layout, value: bool) { - let (ptr, len) = ptr.to_raw_parts(); - set_unit_ptr_initialized(ptr, layout, len, value); +fn set_slice_ptr_initialized( + ptr: *const [T], + layout: Layout, + value: bool, +) { + if LAYOUT_SIZE == 0 { + return; + } + let (ptr, num_elts) = ptr.to_raw_parts(); + unsafe { + MEM_INIT_STATE.set_slice(ptr as *const u8, layout, num_elts, value); + } } /// Get initialization state of the string slice, items of which are laid out according to the `layout` starting at address `ptr`. #[rustc_diagnostic_item = "KaniIsStrPtrInitialized"] -fn is_str_ptr_initialized(ptr: *const str, layout: Layout) -> bool { - let (ptr, len) = ptr.to_raw_parts(); - is_unit_ptr_initialized(ptr, layout, len) +fn is_str_ptr_initialized( + ptr: *const str, + layout: Layout, +) -> bool { + if LAYOUT_SIZE == 0 { + return true; + } + let (ptr, num_elts) = ptr.to_raw_parts(); + unsafe { MEM_INIT_STATE.get_slice(ptr as *const u8, layout, num_elts) } } /// Set initialization state of the string slice, items of which are laid out according to the `layout` starting at address `ptr` to `value`. #[rustc_diagnostic_item = "KaniSetStrPtrInitialized"] -fn set_str_ptr_initialized(ptr: *const str, layout: Layout, value: bool) { - let (ptr, len) = ptr.to_raw_parts(); - set_unit_ptr_initialized(ptr, layout, len, value); +fn set_str_ptr_initialized( + ptr: *const str, + layout: Layout, + value: bool, +) { + if LAYOUT_SIZE == 0 { + return; + } + let (ptr, num_elts) = ptr.to_raw_parts(); + unsafe { + MEM_INIT_STATE.set_slice(ptr as *const u8, layout, num_elts, value); + } } diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index 143fbb7ef825..68a8e79658f1 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -124,6 +124,30 @@ macro_rules! kani_intrinsics { assert!(cond, "{}", msg); } + /// Creates an assertion of the specified condition and message, but does not assume it afterwards. + /// + /// # Example: + /// + /// ```rust + /// let x: bool = kani::any(); + /// let y = !x; + /// kani::check(x || y, "ORing a boolean variable with its negation must be true") + /// ``` + #[cfg(not(feature = "concrete_playback"))] + #[inline(never)] + #[rustc_diagnostic_item = "KaniCheck"] + pub const fn check(cond: bool, msg: &'static str) { + let _ = cond; + let _ = msg; + } + + #[cfg(feature = "concrete_playback")] + #[inline(never)] + #[rustc_diagnostic_item = "KaniCheck"] + pub const fn check(cond: bool, msg: &'static str) { + assert!(cond, "{}", msg); + } + /// Creates a cover property with the specified condition and message. /// /// # Example: diff --git a/library/kani_core/src/mem.rs b/library/kani_core/src/mem.rs index 3b10856765a5..bcf4cd2248a6 100644 --- a/library/kani_core/src/mem.rs +++ b/library/kani_core/src/mem.rs @@ -123,9 +123,11 @@ macro_rules! kani_mem { ::Metadata: PtrProperties, { let (thin_ptr, metadata) = ptr.to_raw_parts(); + // Need to assert `is_initialized` because non-determinism is used under the hood, so it + // does not make sense to use it inside assumption context. metadata.is_ptr_aligned(thin_ptr, Internal) && is_inbounds(&metadata, thin_ptr) - && is_initialized(ptr, 1) + && assert_is_initialized(ptr) && unsafe { has_valid_value(ptr) } } @@ -154,8 +156,10 @@ macro_rules! kani_mem { ::Metadata: PtrProperties, { let (thin_ptr, metadata) = ptr.to_raw_parts(); + // Need to assert `is_initialized` because non-determinism is used under the hood, so it + // does not make sense to use it inside assumption context. is_inbounds(&metadata, thin_ptr) - && is_initialized(ptr, 1) + && assert_is_initialized(ptr) && unsafe { has_valid_value(ptr) } } @@ -302,10 +306,19 @@ macro_rules! kani_mem { /// Check whether `len * size_of::()` bytes are initialized starting from `ptr`. #[rustc_diagnostic_item = "KaniIsInitialized"] #[inline(never)] - pub fn is_initialized(_ptr: *const T, _len: usize) -> bool { + pub fn is_initialized(_ptr: *const T) -> bool { kani_intrinsic() } + /// A helper to assert `is_initialized` to use it as a part of other predicates. + fn assert_is_initialized(ptr: *const T) -> bool { + super::check( + is_initialized(ptr), + "Undefined Behavior: Reading from an uninitialized pointer", + ); + true + } + /// Get the object ID of the given pointer. #[rustc_diagnostic_item = "KaniPointerObject"] #[inline(never)] diff --git a/tests/expected/uninit/access-padding-uninit/access-padding-uninit.rs b/tests/expected/uninit/access-padding-uninit/access-padding-uninit.rs index d6e735e219ad..8e65b95aefa2 100644 --- a/tests/expected/uninit/access-padding-uninit/access-padding-uninit.rs +++ b/tests/expected/uninit/access-padding-uninit/access-padding-uninit.rs @@ -1,6 +1,6 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: -Z ghost-state -Z uninit-checks +// kani-flags: -Z uninit-checks use std::ptr::addr_of; diff --git a/tests/expected/uninit/access-padding-via-cast/access-padding-via-cast.rs b/tests/expected/uninit/access-padding-via-cast/access-padding-via-cast.rs index b73bebc827bc..1604625fc54c 100644 --- a/tests/expected/uninit/access-padding-via-cast/access-padding-via-cast.rs +++ b/tests/expected/uninit/access-padding-via-cast/access-padding-via-cast.rs @@ -1,6 +1,6 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: -Z ghost-state -Z uninit-checks +// kani-flags: -Z uninit-checks use std::ptr; #[repr(C)] diff --git a/tests/expected/uninit/alloc-to-slice/alloc-to-slice.rs b/tests/expected/uninit/alloc-to-slice/alloc-to-slice.rs index 3c4420f5791f..318f234c31be 100644 --- a/tests/expected/uninit/alloc-to-slice/alloc-to-slice.rs +++ b/tests/expected/uninit/alloc-to-slice/alloc-to-slice.rs @@ -1,8 +1,8 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: -Z ghost-state -Z uninit-checks +// kani-flags: -Z uninit-checks -use std::alloc::{alloc, dealloc, Layout}; +use std::alloc::{alloc, Layout}; use std::slice::from_raw_parts; /// Checks that Kani catches an attempt to form a slice from uninitialized memory. diff --git a/tests/expected/uninit/atomic/atomic.rs b/tests/expected/uninit/atomic/atomic.rs new file mode 100644 index 000000000000..63c85af41a3f --- /dev/null +++ b/tests/expected/uninit/atomic/atomic.rs @@ -0,0 +1,31 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z uninit-checks + +#![feature(core_intrinsics)] + +use std::alloc::{alloc, Layout}; +use std::sync::atomic::{AtomicU8, Ordering}; + +// Checks if memory initialization checks correctly fail when uninitialized memory is passed to +// atomic intrinsics. +#[kani::proof] +fn local_atomic_uninit() { + // Get a pointer to an uninitialized value + let layout = Layout::from_size_align(16, 8).unwrap(); + let ptr: *mut u8 = unsafe { alloc(layout) }; + // Try accessing `ptr` via atomic intrinsics, should be UB in each case. + unsafe { + match kani::any() { + 0 => { + std::intrinsics::atomic_store_relaxed(ptr, 1); + } + 1 => { + std::intrinsics::atomic_load_relaxed(ptr as *const u8); + } + _ => { + std::intrinsics::atomic_cxchg_relaxed_relaxed(ptr, 1, 1); + } + }; + } +} diff --git a/tests/expected/uninit/atomic/expected b/tests/expected/uninit/atomic/expected new file mode 100644 index 000000000000..c7d9593f22a7 --- /dev/null +++ b/tests/expected/uninit/atomic/expected @@ -0,0 +1,13 @@ +SUMMARY: + +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*mut u8` + +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const u8` + +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*mut u8` + +VERIFICATION:- FAILED + +Summary:\ +Verification failed for - local_atomic_uninit\ +Complete - 0 successfully verified harnesses, 1 failures, 1 total. diff --git a/tests/expected/uninit/intrinsics/expected b/tests/expected/uninit/intrinsics/expected new file mode 100644 index 000000000000..ffa98b6f1140 --- /dev/null +++ b/tests/expected/uninit/intrinsics/expected @@ -0,0 +1,70 @@ +Checking harness check_typed_swap_safe... + +Failed Checks: Kani does not support reasoning about memory initialization in presence of mutable raw pointer casts that could cause delayed UB. + +VERIFICATION:- FAILED + +Checking harness check_typed_swap... + +Failed Checks: Kani does not support reasoning about memory initialization in presence of mutable raw pointer casts that could cause delayed UB. + +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*mut u8` + +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*mut u8` + +VERIFICATION:- FAILED + +Checking harness check_volatile_store_and_load_safe... + +VERIFICATION:- SUCCESSFUL + +Checking harness check_volatile_load... + +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const u8` + +VERIFICATION:- FAILED + +Checking harness check_write_bytes_safe... + +VERIFICATION:- SUCCESSFUL + +Checking harness check_compare_bytes_safe... + +VERIFICATION:- SUCCESSFUL + +Checking harness check_compare_bytes... + +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 + +Checking harness check_copy_safe... + +VERIFICATION:- SUCCESSFUL + +Checking harness check_copy... + +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const u8` + +VERIFICATION:- FAILED + +Checking harness check_copy_nonoverlapping_safe... + +VERIFICATION:- SUCCESSFUL + +Checking harness check_copy_nonoverlapping... + +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const u8` + +VERIFICATION:- FAILED + +Summary: +Verification failed for - check_typed_swap_safe +Verification failed for - check_typed_swap +Verification failed for - check_volatile_load +Verification failed for - check_compare_bytes +Verification failed for - check_copy +Verification failed for - check_copy_nonoverlapping +Complete - 5 successfully verified harnesses, 6 failures, 11 total. diff --git a/tests/expected/uninit/intrinsics/intrinsics.rs b/tests/expected/uninit/intrinsics/intrinsics.rs new file mode 100644 index 000000000000..aa8a89b7b959 --- /dev/null +++ b/tests/expected/uninit/intrinsics/intrinsics.rs @@ -0,0 +1,127 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z uninit-checks +//! Checks that Kani supports memory initialization checks via intrinsics. + +#![feature(core_intrinsics)] + +use std::alloc::{alloc, alloc_zeroed, Layout}; +use std::intrinsics::*; + +#[kani::proof] +fn check_copy_nonoverlapping() { + unsafe { + let layout = Layout::from_size_align(16, 8).unwrap(); + let src: *mut u8 = alloc(layout); + let dst: *mut u8 = alloc(layout); + copy_nonoverlapping(src as *const u8, dst, 2); // ~ERROR: Accessing `src` here, which is uninitialized. + } +} + +#[kani::proof] +fn check_copy_nonoverlapping_safe() { + unsafe { + let layout = Layout::from_size_align(16, 8).unwrap(); + let src: *mut u8 = alloc_zeroed(layout); + let dst: *mut u8 = alloc(layout); + // `src` is initialized here, `dst` is uninitialized, but it is fine since we are writing into it. + copy_nonoverlapping(src as *const u8, dst, 2); + } +} + +#[kani::proof] +fn check_copy() { + unsafe { + let layout = Layout::from_size_align(16, 8).unwrap(); + let src: *mut u8 = alloc(layout); + let dst: *mut u8 = alloc(layout); + copy(src as *const u8, dst, 2); // ~ERROR: Accessing `src` here, which is uninitialized. + } +} + +#[kani::proof] +fn check_copy_safe() { + unsafe { + let layout = Layout::from_size_align(16, 8).unwrap(); + let src: *mut u8 = alloc_zeroed(layout); + let dst: *mut u8 = alloc(layout); + // `src` is initialized here, `dst` is uninitialized, but it is fine since we are writing into it. + copy(src as *const u8, dst, 2); + } +} + +#[kani::proof] +fn check_compare_bytes() { + unsafe { + let layout = Layout::from_size_align(16, 8).unwrap(); + let left: *mut u8 = alloc(layout); + let right: *mut u8 = alloc(layout); + // ~ERROR: Accessing `left` and `right` here, both of which are uninitialized. + compare_bytes(left as *const u8, right as *const u8, 2); + } +} + +#[kani::proof] +fn check_compare_bytes_safe() { + unsafe { + let layout = Layout::from_size_align(16, 8).unwrap(); + let left: *mut u8 = alloc_zeroed(layout); + let right: *mut u8 = alloc_zeroed(layout); + // Both `left` and `right` are initialized here. + compare_bytes(left as *const u8, right as *const u8, 2); + } +} + +#[kani::proof] +fn check_write_bytes_safe() { + unsafe { + let layout = Layout::from_size_align(16, 8).unwrap(); + let left: *mut u8 = alloc(layout); + let right: *mut u8 = alloc(layout); + write_bytes(left, 0, 2); + write_bytes(right, 0, 2); + // Both `left` and `right` are initialized here. + compare_bytes(left as *const u8, right as *const u8, 2); + } +} + +#[kani::proof] +fn check_volatile_load() { + unsafe { + let layout = Layout::from_size_align(16, 8).unwrap(); + let src: *mut u8 = alloc(layout); + volatile_load(src as *const u8); // ~ERROR: Accessing `src` here, which is uninitialized. + } +} + +#[kani::proof] +fn check_volatile_store_and_load_safe() { + unsafe { + let layout = Layout::from_size_align(16, 8).unwrap(); + let src: *mut u8 = alloc(layout); + volatile_store(src, 0); + volatile_load(src as *const u8); // `src` is initialized here. + } +} + +#[kani::proof] +fn check_typed_swap() { + unsafe { + let layout = Layout::from_size_align(16, 8).unwrap(); + let left: *mut u8 = alloc(layout); + let right: *mut u8 = alloc(layout); + // ~ERROR: Accessing `left` and `right` here, both of which are uninitialized. + typed_swap(left, right); + } +} + +#[kani::proof] +fn check_typed_swap_safe() { + unsafe { + let layout = Layout::from_size_align(16, 8).unwrap(); + let left: *mut u8 = alloc_zeroed(layout); + let right: *mut u8 = alloc_zeroed(layout); + // Both `left` and `right` are initialized here. + typed_swap(left, right); + } +} diff --git a/tests/expected/uninit/vec-read-bad-len/vec-read-bad-len.rs b/tests/expected/uninit/vec-read-bad-len/vec-read-bad-len.rs index 9778bb11a277..f5cae82c5350 100644 --- a/tests/expected/uninit/vec-read-bad-len/vec-read-bad-len.rs +++ b/tests/expected/uninit/vec-read-bad-len/vec-read-bad-len.rs @@ -1,6 +1,6 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: -Z ghost-state -Z uninit-checks +// kani-flags: -Z uninit-checks use std::ops::Index; diff --git a/tests/expected/uninit/vec-read-semi-init/vec-read-semi-init.rs b/tests/expected/uninit/vec-read-semi-init/vec-read-semi-init.rs index 4330f5f53023..2e007cabaced 100644 --- a/tests/expected/uninit/vec-read-semi-init/vec-read-semi-init.rs +++ b/tests/expected/uninit/vec-read-semi-init/vec-read-semi-init.rs @@ -1,6 +1,6 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: -Z ghost-state -Z uninit-checks +// kani-flags: -Z uninit-checks /// Checks that Kani catches an attempt to read uninitialized memory from a semi-initialized vector. #[kani::proof] diff --git a/tests/expected/uninit/vec-read-uninit/vec-read-uninit.rs b/tests/expected/uninit/vec-read-uninit/vec-read-uninit.rs index c322b4d33bb2..e6daf80cd5e3 100644 --- a/tests/expected/uninit/vec-read-uninit/vec-read-uninit.rs +++ b/tests/expected/uninit/vec-read-uninit/vec-read-uninit.rs @@ -1,6 +1,6 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: -Z ghost-state -Z uninit-checks +// kani-flags: -Z uninit-checks /// Checks that Kani catches an attempt to read uninitialized memory from an uninitialized vector. #[kani::proof] diff --git a/tests/kani/Uninit/access-padding-enum-diverging-variants.rs b/tests/kani/Uninit/access-padding-enum-diverging-variants.rs index 7feb493a5b3f..fae491c40622 100644 --- a/tests/kani/Uninit/access-padding-enum-diverging-variants.rs +++ b/tests/kani/Uninit/access-padding-enum-diverging-variants.rs @@ -1,6 +1,6 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: -Z ghost-state -Z uninit-checks +// kani-flags: -Z uninit-checks use std::ptr; use std::ptr::addr_of; diff --git a/tests/kani/Uninit/access-padding-enum-multiple-variants.rs b/tests/kani/Uninit/access-padding-enum-multiple-variants.rs index fb8fae06ca59..dd6942252cb2 100644 --- a/tests/kani/Uninit/access-padding-enum-multiple-variants.rs +++ b/tests/kani/Uninit/access-padding-enum-multiple-variants.rs @@ -1,6 +1,6 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: -Z ghost-state -Z uninit-checks +// kani-flags: -Z uninit-checks use std::ptr; use std::ptr::addr_of; diff --git a/tests/kani/Uninit/access-padding-enum-single-field.rs b/tests/kani/Uninit/access-padding-enum-single-field.rs index c283b603f705..63f7f6043905 100644 --- a/tests/kani/Uninit/access-padding-enum-single-field.rs +++ b/tests/kani/Uninit/access-padding-enum-single-field.rs @@ -1,6 +1,6 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: -Z ghost-state -Z uninit-checks +// kani-flags: -Z uninit-checks use std::ptr; use std::ptr::addr_of; diff --git a/tests/kani/Uninit/access-padding-enum-single-variant.rs b/tests/kani/Uninit/access-padding-enum-single-variant.rs index f33cfe7ce6fb..bb87d36d26c8 100644 --- a/tests/kani/Uninit/access-padding-enum-single-variant.rs +++ b/tests/kani/Uninit/access-padding-enum-single-variant.rs @@ -1,6 +1,6 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: -Z ghost-state -Z uninit-checks +// kani-flags: -Z uninit-checks use std::ptr; use std::ptr::addr_of; diff --git a/tests/kani/Uninit/access-padding-init.rs b/tests/kani/Uninit/access-padding-init.rs new file mode 100644 index 000000000000..7523622a6106 --- /dev/null +++ b/tests/kani/Uninit/access-padding-init.rs @@ -0,0 +1,15 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z uninit-checks + +use std::ptr::addr_of; + +#[repr(C)] +struct S(u32, u8); + +#[kani::proof] +fn access_padding_init() { + let s = S(0, 0); + let ptr: *const u8 = addr_of!(s) as *const u8; + let data = unsafe { *(ptr.add(3)) }; // Accessing data bytes is valid. +} diff --git a/tests/kani/Uninit/alloc-to-slice.rs b/tests/kani/Uninit/alloc-to-slice.rs new file mode 100644 index 000000000000..863d3b40b390 --- /dev/null +++ b/tests/kani/Uninit/alloc-to-slice.rs @@ -0,0 +1,19 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z uninit-checks + +use std::alloc::{alloc, Layout}; + +#[kani::proof] +fn alloc_to_slice() { + let layout = Layout::from_size_align(32, 8).unwrap(); + unsafe { + let ptr = alloc(layout); + *ptr = 0x41; + *ptr.add(1) = 0x42; + *ptr.add(2) = 0x43; + *ptr.add(3) = 0x44; + *ptr.add(16) = 0x00; + let val = *(ptr.add(2)); // Accessing previously initialized byte is valid. + } +} diff --git a/tests/kani/Uninit/alloc-zeroed-to-slice.rs b/tests/kani/Uninit/alloc-zeroed-to-slice.rs new file mode 100644 index 000000000000..d00ca4c6abff --- /dev/null +++ b/tests/kani/Uninit/alloc-zeroed-to-slice.rs @@ -0,0 +1,22 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z uninit-checks + +use std::alloc::{alloc_zeroed, Layout}; +use std::slice::from_raw_parts; + +#[kani::proof] +fn alloc_zeroed_to_slice() { + let layout = Layout::from_size_align(32, 8).unwrap(); + unsafe { + // This returns initialized memory, so any further accesses are valid. + let ptr = alloc_zeroed(layout); + *ptr = 0x41; + *ptr.add(1) = 0x42; + *ptr.add(2) = 0x43; + *ptr.add(3) = 0x44; + *ptr.add(16) = 0x00; + let slice1 = from_raw_parts(ptr, 16); + let slice2 = from_raw_parts(ptr.add(16), 16); + } +} diff --git a/tests/kani/Uninit/atomic.rs b/tests/kani/Uninit/atomic.rs new file mode 100644 index 000000000000..376f365d408c --- /dev/null +++ b/tests/kani/Uninit/atomic.rs @@ -0,0 +1,66 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z uninit-checks + +use std::sync::atomic::{AtomicUsize, Ordering}; + +fn any_ordering() -> Ordering { + match kani::any() { + 0 => Ordering::Relaxed, + 1 => Ordering::Release, + 2 => Ordering::Acquire, + 3 => Ordering::AcqRel, + _ => Ordering::SeqCst, + } +} + +fn store_ordering() -> Ordering { + match kani::any() { + 0 => Ordering::Relaxed, + 1 => Ordering::Release, + _ => Ordering::SeqCst, + } +} + +fn load_ordering() -> Ordering { + match kani::any() { + 0 => Ordering::Relaxed, + 1 => Ordering::Acquire, + _ => Ordering::SeqCst, + } +} + +static GLOBAL_ATOMIC: AtomicUsize = AtomicUsize::new(0); + +// Checks if memory initialization checks work with atomics defined in the global scope. +#[kani::proof] +fn global_atomic() { + let old_value = GLOBAL_ATOMIC.fetch_add(1, any_ordering()); +} + +// Checks if memory initialization checks work with atomics. +#[kani::proof] +fn local_atomic() { + // Get a pointer to an allocated value + let ptr: *mut usize = Box::into_raw(Box::new(0)); + + // Create an atomic from the allocated value + let atomic = unsafe { AtomicUsize::from_ptr(ptr) }; + + // Use `atomic` for atomic operations + atomic.store(1, store_ordering()); + let old_val = atomic.load(load_ordering()); + let old_val = atomic.swap(2, any_ordering()); + + // Deallocate the value + unsafe { drop(Box::from_raw(ptr)) } +} + +// Checks if memory initialization checks work with compare-and-swap atomics. +#[kani::proof] +fn compare_exchange_atomic() { + // Create an atomic. + let some_var = AtomicUsize::new(5); + // Perform a `compare-and-swap` operation. + some_var.compare_exchange(5, 10, any_ordering(), load_ordering()); +} diff --git a/tests/kani/Uninit/struct-padding-and-arr-init.rs b/tests/kani/Uninit/struct-padding-and-arr-init.rs new file mode 100644 index 000000000000..c67b1177bfa3 --- /dev/null +++ b/tests/kani/Uninit/struct-padding-and-arr-init.rs @@ -0,0 +1,21 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z uninit-checks + +use std::ptr::addr_of_mut; + +#[repr(C)] +struct S(u32, u8); + +#[kani::proof] +fn struct_padding_and_arr_init() { + unsafe { + let mut s = S(0, 0); + let sptr = addr_of_mut!(s); + let sptr2 = sptr as *mut [u8; 4]; + *sptr2 = [0; 4]; + *sptr = S(0, 0); + // Both S(u32, u8) and [u8; 4] have the same layout, so the memory is initialized. + let val = *sptr2; + } +} diff --git a/tests/kani/Uninit/vec-read-init.rs b/tests/kani/Uninit/vec-read-init.rs new file mode 100644 index 000000000000..78812c59830e --- /dev/null +++ b/tests/kani/Uninit/vec-read-init.rs @@ -0,0 +1,11 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z uninit-checks + +#[kani::proof] +fn vec_read_init() { + let mut v: Vec = Vec::with_capacity(10); + unsafe { *v.as_mut_ptr().add(5) = 0x42 }; + let def = unsafe { *v.as_ptr().add(5) }; // Accessing previously initialized byte is valid. + let x = def + 1; +} diff --git a/tests/perf/uninit/Cargo.toml b/tests/perf/uninit/Cargo.toml deleted file mode 100644 index 9f44cb3fe103..000000000000 --- a/tests/perf/uninit/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright Kani Contributors -# SPDX-License-Identifier: Apache-2.0 OR MIT - -[package] -name = "uninit" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -[package.metadata.kani] -unstable = { ghost-state = true, uninit-checks = true } diff --git a/tests/perf/uninit/expected b/tests/perf/uninit/expected deleted file mode 100644 index f7b4fd303a77..000000000000 --- a/tests/perf/uninit/expected +++ /dev/null @@ -1 +0,0 @@ -Complete - 5 successfully verified harnesses, 0 failures, 5 total. \ No newline at end of file diff --git a/tests/perf/uninit/src/lib.rs b/tests/perf/uninit/src/lib.rs deleted file mode 100644 index 86b101c0e5d8..000000000000 --- a/tests/perf/uninit/src/lib.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT - -use std::alloc::{alloc, alloc_zeroed, Layout}; -use std::ptr; -use std::ptr::addr_of; -use std::slice::from_raw_parts; - -#[repr(C)] -struct S(u32, u8); - -#[kani::proof] -fn access_padding_init() { - let s = S(0, 0); - let ptr: *const u8 = addr_of!(s) as *const u8; - let data = unsafe { *(ptr.add(3)) }; // Accessing data bytes is valid. -} - -#[kani::proof] -fn alloc_to_slice() { - let layout = Layout::from_size_align(32, 8).unwrap(); - unsafe { - let ptr = alloc(layout); - *ptr = 0x41; - *ptr.add(1) = 0x42; - *ptr.add(2) = 0x43; - *ptr.add(3) = 0x44; - *ptr.add(16) = 0x00; - let val = *(ptr.add(2)); // Accessing previously initialized byte is valid. - } -} - -#[kani::proof] -fn alloc_zeroed_to_slice() { - let layout = Layout::from_size_align(32, 8).unwrap(); - unsafe { - // This returns initialized memory, so any further accesses are valid. - let ptr = alloc_zeroed(layout); - *ptr = 0x41; - *ptr.add(1) = 0x42; - *ptr.add(2) = 0x43; - *ptr.add(3) = 0x44; - *ptr.add(16) = 0x00; - let slice1 = from_raw_parts(ptr, 16); - let slice2 = from_raw_parts(ptr.add(16), 16); - } -} - -#[kani::proof] -fn struct_padding_and_arr_init() { - unsafe { - let mut s = S(0, 0); - let sptr = ptr::addr_of_mut!(s); - let sptr2 = sptr as *mut [u8; 4]; - *sptr2 = [0; 4]; - *sptr = S(0, 0); - // Both S(u32, u8) and [u8; 4] have the same layout, so the memory is initialized. - let val = *sptr2; - } -} - -#[kani::proof] -fn vec_read_init() { - let mut v: Vec = Vec::with_capacity(10); - unsafe { *v.as_mut_ptr().add(5) = 0x42 }; - let def = unsafe { *v.as_ptr().add(5) }; // Accessing previously initialized byte is valid. - let x = def + 1; -} diff --git a/tests/std-checks/core/slice.expected b/tests/std-checks/core/slice.expected index 01a90d50b557..4426ff6c02cd 100644 --- a/tests/std-checks/core/slice.expected +++ b/tests/std-checks/core/slice.expected @@ -1 +1 @@ -Complete - 1 successfully verified harnesses, 0 failures, 1 total. +Complete - 2 successfully verified harnesses, 0 failures, 2 total. diff --git a/tests/std-checks/core/src/slice.rs b/tests/std-checks/core/src/slice.rs index 044f4bd38586..fe627f21a5ae 100644 --- a/tests/std-checks/core/src/slice.rs +++ b/tests/std-checks/core/src/slice.rs @@ -1,25 +1,30 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT +extern crate kani; + /// Create wrapper functions to standard library functions that contains their contract. pub mod contracts { use kani::{mem::*, requires}; #[requires(can_dereference(std::ptr::slice_from_raw_parts(data, len)))] - #[requires(is_initialized(data, len))] pub unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { std::slice::from_raw_parts(data, len) } + + #[requires(can_dereference(std::ptr::slice_from_raw_parts(data, len)))] + pub unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] { + std::slice::from_raw_parts_mut(data, len) + } } #[cfg(kani)] mod verify { use super::*; - const MAX_LEN: usize = 2; + const MAX_LEN: usize = isize::MAX as usize; #[kani::proof_for_contract(contracts::from_raw_parts)] - #[kani::unwind(25)] pub fn check_from_raw_parts_primitive() { let len: usize = kani::any(); kani::assume(len < MAX_LEN); @@ -27,4 +32,13 @@ mod verify { let arr = vec![0u8; len]; let _slice = unsafe { contracts::from_raw_parts(arr.as_ptr(), len) }; } + + #[kani::proof_for_contract(contracts::from_raw_parts_mut)] + pub fn check_from_raw_parts_mut_primitive() { + let len: usize = kani::any(); + kani::assume(len < MAX_LEN); + + let mut arr = vec![0u8; len]; + let _slice = unsafe { contracts::from_raw_parts_mut(arr.as_mut_ptr(), len) }; + } } From 02e33276cc27bc027d79779205d63d0ed6285241 Mon Sep 17 00:00:00 2001 From: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> Date: Fri, 12 Jul 2024 15:05:44 -0400 Subject: [PATCH 179/225] Upgrade toolchain to 7/12 (#3337) Upgrade toolchain to 7/12 Relevant PRs: https://github.com/rust-lang/rust/pull/127176/ and https://github.com/rust-lang/rust/pull/125507 Resolves #3319 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/typ.rs | 2 +- rust-toolchain.toml | 2 +- tests/expected/function-contract/const_fn_with_effect.rs | 2 ++ tools/compiletest/src/json.rs | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index 6e6547295ff9..1703335dda51 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -574,7 +574,7 @@ impl<'tcx> GotocCtx<'tcx> { ty::Ref(_, t, _) | ty::RawPtr(t, _) => self.codegen_ty_ref(*t), ty::FnDef(def_id, args) => { let instance = - Instance::resolve(self.tcx, ty::ParamEnv::reveal_all(), *def_id, args) + Instance::try_resolve(self.tcx, ty::ParamEnv::reveal_all(), *def_id, args) .unwrap() .unwrap(); self.codegen_fndef_type(instance) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index dfe3febbb927..f2e8fede457c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-07-01" +channel = "nightly-2024-07-12" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/expected/function-contract/const_fn_with_effect.rs b/tests/expected/function-contract/const_fn_with_effect.rs index d57c1f42fe16..070c44482a80 100644 --- a/tests/expected/function-contract/const_fn_with_effect.rs +++ b/tests/expected/function-contract/const_fn_with_effect.rs @@ -1,11 +1,13 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zfunction-contracts -Zmem-predicates +// compile-flags: -Znext-solver //! Check that Kani contract can be applied to a constant function. //! #![feature(effects)] +#![allow(incomplete_features)] #[kani::requires(kani::mem::can_dereference(arg))] const unsafe fn dummy(arg: *const T) -> T { diff --git a/tools/compiletest/src/json.rs b/tools/compiletest/src/json.rs index c46c3b3225e8..89c733abc59a 100644 --- a/tools/compiletest/src/json.rs +++ b/tools/compiletest/src/json.rs @@ -35,6 +35,7 @@ struct FutureBreakageItem { } #[derive(Deserialize, Clone)] +#[allow(dead_code)] struct DiagnosticSpanMacroExpansion { /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]") _macro_decl_name: String, From 3d57f529dade68e43c5f914eb6774b31cbc94783 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 13 Jul 2024 08:47:23 -0400 Subject: [PATCH 180/225] Automatic toolchain upgrade to nightly-2024-07-13 (#3339) Update Rust toolchain from nightly-2024-07-12 to nightly-2024-07-13 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/5315cbe15b79533f380bbb6685aa5480d5ff4ef5 up to https://github.com/rust-lang/rust/commit/c6727fc9b5c64cefa7263486497ee95e529bd0f8. The log for this commit range is: https://github.com/rust-lang/rust/commit/c6727fc9b5 Auto merge of #123351 - beetrees:x86-ret-snan-rust, r=nikic,workingjubilee https://github.com/rust-lang/rust/commit/62c068feea Auto merge of #127636 - nnethercote:fix-Parser-look_ahead, r=oli-obk https://github.com/rust-lang/rust/commit/5d76a13bbe Auto merge of #127653 - matthiaskrgr:rollup-72bqgvp, r=matthiaskrgr https://github.com/rust-lang/rust/commit/f11c2c8e95 Rollup merge of #127648 - Kobzol:ci-lower-timeout, r=pietroalbini https://github.com/rust-lang/rust/commit/526da2366a Rollup merge of #127627 - lcnr:rustc_search_graph, r=compiler-errors https://github.com/rust-lang/rust/commit/f5fa6fb602 Rollup merge of #127613 - nikic:riscv-update, r=cuviper https://github.com/rust-lang/rust/commit/b4f002d2e5 Rollup merge of #127552 - onur-ozkan:unnecessary-git-usage, r=Kobzol https://github.com/rust-lang/rust/commit/8ceb4e49ff Rollup merge of #127433 - dtolnay:conststrlen, r=workingjubilee https://github.com/rust-lang/rust/commit/f9b3e8b387 Rollup merge of #126827 - the8472:pidfd-spawn, r=workingjubilee https://github.com/rust-lang/rust/commit/18152d72a4 Rollup merge of #126639 - sayantn:amx, r=Amanieu https://github.com/rust-lang/rust/commit/65ea92d4a1 Rollup merge of #124980 - zachs18:rc-allocator, r=Amanieu https://github.com/rust-lang/rust/commit/05eac57ef6 Auto merge of #127479 - Urgau:rustc-stable-hash, r=michaelwoerister https://github.com/rust-lang/rust/commit/15f770b143 enable fuzzing of `SearchGraph` https://github.com/rust-lang/rust/commit/cae9d480bf Adjust tests for x86 "Rust" ABI changes https://github.com/rust-lang/rust/commit/3f4b9dd463 Lower timeout of CI jobs to 4 hours https://github.com/rust-lang/rust/commit/7f1518bddd Add instability attribute on private const_strlen function https://github.com/rust-lang/rust/commit/b286722878 Auto merge of #127635 - matthiaskrgr:rollup-foopajr, r=matthiaskrgr https://github.com/rust-lang/rust/commit/100f3fd133 Add a new special case to `Parser::look_ahead`. https://github.com/rust-lang/rust/commit/ebe1305b1e Remove the bogus special case from `Parser::look_ahead`. https://github.com/rust-lang/rust/commit/dad95578b0 Add unit tests for `Parser::look_ahead`. https://github.com/rust-lang/rust/commit/ec05c4ea3f Add the feature gate and target-features https://github.com/rust-lang/rust/commit/c2b7842555 Rollup merge of #127625 - SkiFire13:revert-comment-deletion, r=workingjubilee https://github.com/rust-lang/rust/commit/ca576eae4e Rollup merge of #127622 - compiler-errors:builtin-internal, r=lqd https://github.com/rust-lang/rust/commit/fe564c10ab Rollup merge of #127607 - Zalathar:normalize-hint, r=wesleywiser https://github.com/rust-lang/rust/commit/83d1a1b252 Rollup merge of #127596 - tesuji:help-unwrap-or, r=compiler-errors https://github.com/rust-lang/rust/commit/1e7ad4c3ed Rollup merge of #127422 - greaka:master, r=workingjubilee https://github.com/rust-lang/rust/commit/58fe37f2c3 Rollup merge of #127164 - Nadrieril:clean-lowering-loop, r=matthewjasper https://github.com/rust-lang/rust/commit/4a31a6c32a Auto merge of #127382 - estebank:const-let, r=compiler-errors https://github.com/rust-lang/rust/commit/5e311f933d Auto merge of #127614 - matthiaskrgr:rollup-8geziwi, r=matthiaskrgr https://github.com/rust-lang/rust/commit/a776e5f922 Add doc for deconstruct_option_or_result https://github.com/rust-lang/rust/commit/872d7b82e1 Add suggestion for `Option<&Vec> -> Option<&[T]` https://github.com/rust-lang/rust/commit/d9170dc666 Add regression test for issue 127545 https://github.com/rust-lang/rust/commit/4df75140dd Fix aarch64 test https://github.com/rust-lang/rust/commit/cbe75486f7 Account for `let foo = expr`; to suggest `const foo: Ty = expr;` https://github.com/rust-lang/rust/commit/b56dc8ee90 Use verbose style when suggesting changing `const` with `let` https://github.com/rust-lang/rust/commit/d9021791eb Revert accidental comment deletion https://github.com/rust-lang/rust/commit/b77d3ef7c4 Mark builtin syntax as internal https://github.com/rust-lang/rust/commit/fa3ce50f0b Rollup merge of #127605 - nikic:remove-extern-wasm, r=oli-obk https://github.com/rust-lang/rust/commit/d433f176ef Rollup merge of #127601 - trevyn:issue-127600, r=compiler-errors https://github.com/rust-lang/rust/commit/47ab86653e Rollup merge of #127599 - tgross35:lazy_cell_consume-rename, r=workingjubilee https://github.com/rust-lang/rust/commit/a10b4d1463 Rollup merge of #127598 - weiznich:diagnostic_do_not_recommend_also_skips_help, r=compiler-errors https://github.com/rust-lang/rust/commit/73c500b3a7 Rollup merge of #127591 - compiler-errors:label-after-primary, r=lcnr https://github.com/rust-lang/rust/commit/380c78741e Rollup merge of #127588 - uweigand:s390x-f16-doctests, r=tgross35 https://github.com/rust-lang/rust/commit/6fd955549a Rollup merge of #127572 - tbu-:pr_debug_event_nonpacked, r=jhpratt https://github.com/rust-lang/rust/commit/8de487fdbd Rollup merge of #124599 - estebank:issue-41708, r=wesleywiser https://github.com/rust-lang/rust/commit/55256c5a18 Update dist-riscv64-linux to binutils 2.40 https://github.com/rust-lang/rust/commit/977439d9b8 Use uplifted `rustc-stable-hash` crate in `rustc_data_structures` https://github.com/rust-lang/rust/commit/f56b2074c6 solve -> solve/mod https://github.com/rust-lang/rust/commit/08a2992d6b compiletest: Better error message for bad `normalize-*` headers https://github.com/rust-lang/rust/commit/8a50bcbdce Remove extern "wasm" ABI https://github.com/rust-lang/rust/commit/a01f49e7f3 check is_ident before parse_ident https://github.com/rust-lang/rust/commit/ab56fe2053 Rename `lazy_cell_consume` to `lazy_cell_into_inner` https://github.com/rust-lang/rust/commit/27d5db166e Allows `#[diagnostic::do_not_recommend]` to supress trait impls in suggestions as well https://github.com/rust-lang/rust/commit/12ae282987 Fix diagnostic and add a test for it https://github.com/rust-lang/rust/commit/df72e478b0 Make sure that labels are defined after the primary span in diagnostics https://github.com/rust-lang/rust/commit/0065763950 core: Limit remaining f16 doctests to x86_64 linux https://github.com/rust-lang/rust/commit/45ad522e87 Don't mark `DEBUG_EVENT` struct as `repr(packed)` https://github.com/rust-lang/rust/commit/0134bd2e67 remove unnecessary `git` usages https://github.com/rust-lang/rust/commit/42772e98e0 Address review comments https://github.com/rust-lang/rust/commit/3e030b38ef Return the `otherwise_block` instead of passing it as argument https://github.com/rust-lang/rust/commit/fc40247c6b Factor out the "process remaining candidates" cases https://github.com/rust-lang/rust/commit/8a222ffd6b Don't try to save an extra block https://github.com/rust-lang/rust/commit/c5062f7318 Move or-pattern expansion inside the main part of the algorithm https://github.com/rust-lang/rust/commit/bff4d213fa Factor out the special handling of or-patterns https://github.com/rust-lang/rust/commit/5bf50e66f9 Move a function https://github.com/rust-lang/rust/commit/53d3e6217b Stabilize const_cstr_from_ptr (CStr::from_ptr, CStr::count_bytes) https://github.com/rust-lang/rust/commit/585ca16e0b as_simd: fix comment to be in line with 507583a (#121201) https://github.com/rust-lang/rust/commit/0f643c449a Ensure tests don't fail on i586 in CI https://github.com/rust-lang/rust/commit/ec0c755704 Check that we get somewhat sane PIDs when spawning with pidfds https://github.com/rust-lang/rust/commit/3e4e31b7bf more fine-grained feature-detection for pidfd spawning https://github.com/rust-lang/rust/commit/0ce361938e document safety properties of the internal Process::new constructor https://github.com/rust-lang/rust/commit/6687a3f7da use pidfd_spawn for faster process creation when pidfds are requested https://github.com/rust-lang/rust/commit/5c46acac04 document the cvt methods https://github.com/rust-lang/rust/commit/0e1c832dbd Update `platform-support.md` to reflect improvements in returning floats on 32-bit x86 https://github.com/rust-lang/rust/commit/952becc0bd Ensure floats are returned losslessly by the Rust ABI on 32-bit x86 https://github.com/rust-lang/rust/commit/a1ad6346d6 Add fn allocator method to rc/sync::Weak. Relax Rc/Arc::allocator to allow unsized T. https://github.com/rust-lang/rust/commit/2df4f7dd8c Suggest borrowing on fn argument that is `impl AsRef` 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 f2e8fede457c..04240657fe9c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-07-12" +channel = "nightly-2024-07-13" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 4592a4a3c68c4fc1d98bb79aa5d6747fbbf3d226 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 14 Jul 2024 11:46:48 +0200 Subject: [PATCH 181/225] Automatic toolchain upgrade to nightly-2024-07-14 (#3340) Update Rust toolchain from nightly-2024-07-13 to nightly-2024-07-14 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/c6727fc9b5c64cefa7263486497ee95e529bd0f8 up to https://github.com/rust-lang/rust/commit/fcaa6fdfbee1316184e7ad98c53241d52cd30a5f. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 04240657fe9c..79dbe24358d1 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-07-13" +channel = "nightly-2024-07-14" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From da092caac9de96640240b5ae6b053973c20e7f8e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 10:02:27 +0200 Subject: [PATCH 182/225] Automatic toolchain upgrade to nightly-2024-07-15 (#3341) Update Rust toolchain from nightly-2024-07-14 to nightly-2024-07-15 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/fcaa6fdfbee1316184e7ad98c53241d52cd30a5f up to https://github.com/rust-lang/rust/commit/d9284afea99e0969a0e692b9e9fd61ea4ba21366. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 79dbe24358d1..a20f8e321860 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-07-14" +channel = "nightly-2024-07-15" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 0b8acc4ddfc0a07e2c17c384b889367caeba5328 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 09:21:38 +0000 Subject: [PATCH 183/225] Automatic cargo update to 2024-07-15 (#3342) Dependency upgrade resulting from `cargo update`. --- Cargo.lock | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd76b5e75b43..fe420de27a88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,9 +140,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.8" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" +checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" dependencies = [ "clap_builder", "clap_derive", @@ -150,9 +150,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.8" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" +checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" dependencies = [ "anstream", "anstyle", @@ -169,7 +169,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.71", ] [[package]] @@ -482,7 +482,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.71", ] [[package]] @@ -924,7 +924,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.71", ] [[package]] @@ -1030,7 +1030,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.69", + "syn 2.0.71", ] [[package]] @@ -1045,9 +1045,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.69" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201fcda3845c23e8212cd466bfebf0bd20694490fc0356ae8e428e0824a915a6" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ "proc-macro2", "quote", @@ -1068,22 +1068,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.71", ] [[package]] @@ -1119,9 +1119,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.14" +version = "0.22.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" dependencies = [ "indexmap", "serde", @@ -1149,7 +1149,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.71", ] [[package]] @@ -1414,5 +1414,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.69", + "syn 2.0.71", ] From 441451c725c7ddd534628e1d0fa4dcd5271fa59b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 10:24:19 -0700 Subject: [PATCH 184/225] Bump tests/perf/s2n-quic from `cd20ac1` to `71f8d9f` (#3343) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `cd20ac1` to `71f8d9f`.
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 cd20ac16f5e4..71f8d9f5aafb 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit cd20ac16f5e4477238164074d2ed5b94ca8ba4fd +Subproject commit 71f8d9f5aafbf59f31ad85eeb7b4b67a7564a685 From 398729c0e36a01ace37283b37a1fee23e00e1e0f Mon Sep 17 00:00:00 2001 From: Artem Agvanian Date: Tue, 16 Jul 2024 08:50:07 -0700 Subject: [PATCH 185/225] Fix visibility of some Kani intrinsics (#3323) This PR fixes inadvertently exposing some of the unstable Kani intrinsics. 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/src/mem.rs | 14 +++++++++++++- library/kani_core/src/mem.rs | 20 +++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/library/kani/src/mem.rs b/library/kani/src/mem.rs index f861f6b2ba25..f718c09ec38d 100644 --- a/library/kani/src/mem.rs +++ b/library/kani/src/mem.rs @@ -300,7 +300,7 @@ unsafe fn has_valid_value(_ptr: *const T) -> bool { /// Check whether `len * size_of::()` bytes are initialized starting from `ptr`. #[rustc_diagnostic_item = "KaniIsInitialized"] #[inline(never)] -pub fn is_initialized(_ptr: *const T) -> bool { +pub(crate) fn is_initialized(_ptr: *const T) -> bool { kani_intrinsic() } @@ -311,6 +311,12 @@ fn assert_is_initialized(ptr: *const T) -> bool { } /// Get the object ID of the given pointer. +#[doc(hidden)] +#[crate::unstable( + feature = "ghost-state", + issue = 3184, + reason = "experimental ghost state/shadow memory API" +)] #[rustc_diagnostic_item = "KaniPointerObject"] #[inline(never)] pub fn pointer_object(_ptr: *const T) -> usize { @@ -318,6 +324,12 @@ pub fn pointer_object(_ptr: *const T) -> usize { } /// Get the object offset of the given pointer. +#[doc(hidden)] +#[crate::unstable( + feature = "ghost-state", + issue = 3184, + reason = "experimental ghost state/shadow memory API" +)] #[rustc_diagnostic_item = "KaniPointerOffset"] #[inline(never)] pub fn pointer_offset(_ptr: *const T) -> usize { diff --git a/library/kani_core/src/mem.rs b/library/kani_core/src/mem.rs index bcf4cd2248a6..0b029ad53089 100644 --- a/library/kani_core/src/mem.rs +++ b/library/kani_core/src/mem.rs @@ -306,7 +306,7 @@ macro_rules! kani_mem { /// Check whether `len * size_of::()` bytes are initialized starting from `ptr`. #[rustc_diagnostic_item = "KaniIsInitialized"] #[inline(never)] - pub fn is_initialized(_ptr: *const T) -> bool { + pub(crate) fn is_initialized(_ptr: *const T) -> bool { kani_intrinsic() } @@ -320,16 +320,30 @@ macro_rules! kani_mem { } /// Get the object ID of the given pointer. + // TODO: Add this back later, as there is no unstable attribute here. + // #[doc(hidden)] + // #[crate::unstable( + // feature = "ghost-state", + // issue = 3184, + // reason = "experimental ghost state/shadow memory API" + // )] #[rustc_diagnostic_item = "KaniPointerObject"] #[inline(never)] - pub fn pointer_object(_ptr: *const T) -> usize { + pub(crate) fn pointer_object(_ptr: *const T) -> usize { kani_intrinsic() } /// Get the object offset of the given pointer. + // TODO: Add this back later, as there is no unstable attribute here. + // #[doc(hidden)] + // #[crate::unstable( + // feature = "ghost-state", + // issue = 3184, + // reason = "experimental ghost state/shadow memory API" + // )] #[rustc_diagnostic_item = "KaniPointerOffset"] #[inline(never)] - pub fn pointer_offset(_ptr: *const T) -> usize { + pub(crate) fn pointer_offset(_ptr: *const T) -> usize { kani_intrinsic() } }; From ff917622f97ad122c3b56d30215a2151069b3169 Mon Sep 17 00:00:00 2001 From: Artem Agvanian Date: Tue, 16 Jul 2024 09:44:21 -0700 Subject: [PATCH 186/225] Mitigate invalid `transmute` when checking memory initialization (#3338) This PR addresses another aspect of #3324, where delayed UB could be caused by transmuting a mutable pointer into the one of incompatible padding. It also adds a check to error whenever transmuting between two types of incompatible padding. 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/check_uninit/mod.rs | 14 ++++--- .../transform/check_uninit/uninit_visitor.rs | 38 +++++++++++++++++-- .../delayed-ub-transmute.rs | 14 +++++++ .../uninit/delayed-ub-transmute/expected | 5 +++ .../uninit/transmute-padding/expected | 5 +++ .../transmute-padding/transmute_padding.rs | 19 ++++++++++ 6 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 tests/expected/uninit/delayed-ub-transmute/delayed-ub-transmute.rs create mode 100644 tests/expected/uninit/delayed-ub-transmute/expected create mode 100644 tests/expected/uninit/transmute-padding/expected create mode 100644 tests/expected/uninit/transmute-padding/transmute_padding.rs 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 e1b265751fd5..4f94f94d17f1 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs @@ -142,9 +142,11 @@ impl UninitPass { operation: MemoryInitOp, skip_first: &mut HashSet, ) { - if let MemoryInitOp::Unsupported { reason } = &operation { + if let MemoryInitOp::Unsupported { reason } | MemoryInitOp::TriviallyUnsafe { reason } = + &operation + { collect_skipped(&operation, body, skip_first); - self.unsupported_check(tcx, body, source, operation.position(), reason); + self.inject_assert_false(tcx, body, source, operation.position(), reason); return; }; @@ -166,7 +168,7 @@ impl UninitPass { "Kani currently doesn't support checking memory initialization for pointers to `{pointee_ty}.", ); collect_skipped(&operation, body, skip_first); - self.unsupported_check(tcx, body, source, operation.position(), &reason); + self.inject_assert_false(tcx, body, source, operation.position(), &reason); return; } } @@ -181,7 +183,7 @@ impl UninitPass { | MemoryInitOp::SetRef { .. } => { self.build_set(tcx, body, source, operation, pointee_ty_info, skip_first) } - MemoryInitOp::Unsupported { .. } => { + MemoryInitOp::Unsupported { .. } | MemoryInitOp::TriviallyUnsafe { .. } => { unreachable!() } } @@ -266,7 +268,7 @@ impl UninitPass { PointeeLayout::TraitObject => { collect_skipped(&operation, body, skip_first); let reason = "Kani does not support reasoning about memory initialization of pointers to trait objects."; - self.unsupported_check(tcx, body, source, operation.position(), reason); + self.inject_assert_false(tcx, body, source, operation.position(), reason); return; } }; @@ -392,7 +394,7 @@ impl UninitPass { }; } - fn unsupported_check( + fn inject_assert_false( &self, tcx: TyCtxt, body: &mut MutableBody, diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs b/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs index f68869e6681d..4c768aa2ee81 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs @@ -37,6 +37,8 @@ pub enum MemoryInitOp { SetRef { operand: Operand, value: bool, position: InsertPosition }, /// Unsupported memory initialization operation. Unsupported { reason: String }, + /// Operation that trivially accesses uninitialized memory, results in injecting `assert!(false)`. + TriviallyUnsafe { reason: String }, } impl MemoryInitOp { @@ -63,7 +65,9 @@ impl MemoryInitOp { }, projection: vec![], }), - MemoryInitOp::Unsupported { .. } => unreachable!(), + MemoryInitOp::Unsupported { .. } | MemoryInitOp::TriviallyUnsafe { .. } => { + unreachable!() + } } } @@ -74,7 +78,8 @@ impl MemoryInitOp { MemoryInitOp::Check { .. } | MemoryInitOp::Set { .. } | MemoryInitOp::SetRef { .. } - | MemoryInitOp::Unsupported { .. } => unreachable!(), + | MemoryInitOp::Unsupported { .. } + | MemoryInitOp::TriviallyUnsafe { .. } => unreachable!(), } } @@ -85,7 +90,8 @@ impl MemoryInitOp { | MemoryInitOp::SetRef { value, .. } => *value, MemoryInitOp::Check { .. } | MemoryInitOp::CheckSliceChunk { .. } - | MemoryInitOp::Unsupported { .. } => unreachable!(), + | MemoryInitOp::Unsupported { .. } + | MemoryInitOp::TriviallyUnsafe { .. } => unreachable!(), } } @@ -96,7 +102,8 @@ impl MemoryInitOp { | MemoryInitOp::SetRef { position, .. } => *position, MemoryInitOp::Check { .. } | MemoryInitOp::CheckSliceChunk { .. } - | MemoryInitOp::Unsupported { .. } => InsertPosition::Before, + | MemoryInitOp::Unsupported { .. } + | MemoryInitOp::TriviallyUnsafe { .. } => InsertPosition::Before, } } } @@ -581,6 +588,29 @@ impl<'a> MirVisitor for CheckUninitVisitor<'a> { } } } + CastKind::Transmute => { + let operand_ty = operand.ty(&self.locals).unwrap(); + if let ( + RigidTy::RawPtr(from_ty, Mutability::Mut), + RigidTy::RawPtr(to_ty, Mutability::Mut), + ) = (operand_ty.kind().rigid().unwrap(), ty.kind().rigid().unwrap()) + { + if !tys_layout_compatible(from_ty, to_ty) { + // If casting from a mutable pointer to a mutable pointer with different + // layouts, delayed UB could occur. + self.push_target(MemoryInitOp::Unsupported { + reason: "Kani does not support reasoning about memory initialization in presence of mutable raw pointer casts that could cause delayed UB.".to_string(), + }); + } + } else if !tys_layout_compatible(&operand_ty, &ty) { + // If transmuting between two types of incompatible layouts, padding + // bytes are exposed, which is UB. + self.push_target(MemoryInitOp::TriviallyUnsafe { + reason: "Transmuting between types of incompatible layouts." + .to_string(), + }); + } + } _ => {} } }; diff --git a/tests/expected/uninit/delayed-ub-transmute/delayed-ub-transmute.rs b/tests/expected/uninit/delayed-ub-transmute/delayed-ub-transmute.rs new file mode 100644 index 000000000000..df769e39a8b2 --- /dev/null +++ b/tests/expected/uninit/delayed-ub-transmute/delayed-ub-transmute.rs @@ -0,0 +1,14 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z uninit-checks + +/// Checks that Kani rejects mutable pointer casts between types of different padding. +#[kani::proof] +fn invalid_value() { + unsafe { + let mut value: u128 = 0; + let ptr: *mut (u8, u32, u64) = std::mem::transmute(&mut value as *mut _); + *ptr = (4, 4, 4); // This assignment itself does not cause UB... + let c: u128 = value; // ...but this reads a padding value! + } +} diff --git a/tests/expected/uninit/delayed-ub-transmute/expected b/tests/expected/uninit/delayed-ub-transmute/expected new file mode 100644 index 000000000000..e02883b26cdf --- /dev/null +++ b/tests/expected/uninit/delayed-ub-transmute/expected @@ -0,0 +1,5 @@ +Failed Checks: Kani does not support reasoning about memory initialization in presence of mutable raw pointer casts that could cause delayed UB. + +VERIFICATION:- FAILED + +Complete - 0 successfully verified harnesses, 1 failures, 1 total. \ No newline at end of file diff --git a/tests/expected/uninit/transmute-padding/expected b/tests/expected/uninit/transmute-padding/expected new file mode 100644 index 000000000000..980a01748fc7 --- /dev/null +++ b/tests/expected/uninit/transmute-padding/expected @@ -0,0 +1,5 @@ +Failed Checks: Transmuting between types of incompatible layouts. + +VERIFICATION:- FAILED + +Complete - 0 successfully verified harnesses, 1 failures, 1 total. \ No newline at end of file diff --git a/tests/expected/uninit/transmute-padding/transmute_padding.rs b/tests/expected/uninit/transmute-padding/transmute_padding.rs new file mode 100644 index 000000000000..b74346c98160 --- /dev/null +++ b/tests/expected/uninit/transmute-padding/transmute_padding.rs @@ -0,0 +1,19 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z uninit-checks + +// 5 bytes of data + 3 bytes of padding. +#[repr(C)] +#[derive(kani::Arbitrary)] +struct S(u32, u8); + +/// Checks that Kani catches an attempt to access padding of a struct using transmute. +#[kani::proof] +fn check_uninit_padding() { + let s = kani::any(); + access_padding(s); +} + +fn access_padding(s: S) { + let _padding: u64 = unsafe { std::mem::transmute(s) }; // ~ERROR: padding bytes are uninitialized, so reading them is UB. +} From 053f45c0b701d621cadfdd8cd52a0e6b0364c2bb Mon Sep 17 00:00:00 2001 From: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Date: Tue, 16 Jul 2024 19:44:45 -0400 Subject: [PATCH 187/225] Add a few examples of using shadow memory to check initialization of slices (#3237) A follow-up on #3200: use API to check that slices produced by some slice operations that internally use `unsafe` are properly initialized. 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: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> Co-authored-by: Artem Agvanian --- .../shadow/slices/slice_of_array/expected | 1 + .../shadow/slices/slice_of_array/test.rs | 34 ++++++++++++++++++ .../shadow/slices/slice_reverse/expected | 1 + .../shadow/slices/slice_reverse/test.rs | 28 +++++++++++++++ .../shadow/slices/slice_split/expected | 1 + .../shadow/slices/slice_split/test.rs | 35 +++++++++++++++++++ 6 files changed, 100 insertions(+) create mode 100644 tests/expected/shadow/slices/slice_of_array/expected create mode 100644 tests/expected/shadow/slices/slice_of_array/test.rs create mode 100644 tests/expected/shadow/slices/slice_reverse/expected create mode 100644 tests/expected/shadow/slices/slice_reverse/test.rs create mode 100644 tests/expected/shadow/slices/slice_split/expected create mode 100644 tests/expected/shadow/slices/slice_split/test.rs diff --git a/tests/expected/shadow/slices/slice_of_array/expected b/tests/expected/shadow/slices/slice_of_array/expected new file mode 100644 index 000000000000..34c886c358cb --- /dev/null +++ b/tests/expected/shadow/slices/slice_of_array/expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/shadow/slices/slice_of_array/test.rs b/tests/expected/shadow/slices/slice_of_array/test.rs new file mode 100644 index 000000000000..b5ac3abae126 --- /dev/null +++ b/tests/expected/shadow/slices/slice_of_array/test.rs @@ -0,0 +1,34 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zghost-state + +// This test demonstrates a possible usage of the shadow memory API to check that +// every element of an arbitrary slice of an array is initialized. +// Since the instrumentation is done manually in the harness only but not inside +// the library functions, the test only verifies that the slices point to memory +// that is within the original array. + +const N: usize = 16; + +static mut SM: kani::shadow::ShadowMem = kani::shadow::ShadowMem::new(false); + +#[kani::proof] +#[kani::unwind(31)] +fn check_slice_init() { + let arr: [char; N] = kani::any(); + // tag every element of the array as initialized + for i in &arr { + unsafe { + SM.set(i as *const char, true); + } + } + // create an arbitrary slice of the array + let end: usize = kani::any_where(|x| *x <= N); + let begin: usize = kani::any_where(|x| *x < end); + let slice = &arr[begin..end]; + + // verify that all elements of the slice are initialized + for i in slice { + assert!(unsafe { SM.get(i as *const char) }); + } +} diff --git a/tests/expected/shadow/slices/slice_reverse/expected b/tests/expected/shadow/slices/slice_reverse/expected new file mode 100644 index 000000000000..34c886c358cb --- /dev/null +++ b/tests/expected/shadow/slices/slice_reverse/expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/shadow/slices/slice_reverse/test.rs b/tests/expected/shadow/slices/slice_reverse/test.rs new file mode 100644 index 000000000000..4810958e2fe1 --- /dev/null +++ b/tests/expected/shadow/slices/slice_reverse/test.rs @@ -0,0 +1,28 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zghost-state + +// This test demonstrates a possible usage of the shadow memory API to check that +// every element of a reversed array is initialized. +// Since the instrumentation is done manually in the harness only but not inside +// the `reverse` function, the test only verifies that the resulting array +// occupies the same memory as the original one. + +const N: usize = 32; + +static mut SM: kani::shadow::ShadowMem = kani::shadow::ShadowMem::new(false); + +#[kani::proof] +fn check_reverse() { + let mut a: [u16; N] = kani::any(); + for i in &a { + unsafe { SM.set(i as *const u16, true) }; + } + a.reverse(); + + for i in &a { + unsafe { + assert!(SM.get(i as *const u16)); + } + } +} diff --git a/tests/expected/shadow/slices/slice_split/expected b/tests/expected/shadow/slices/slice_split/expected new file mode 100644 index 000000000000..34c886c358cb --- /dev/null +++ b/tests/expected/shadow/slices/slice_split/expected @@ -0,0 +1 @@ +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/shadow/slices/slice_split/test.rs b/tests/expected/shadow/slices/slice_split/test.rs new file mode 100644 index 000000000000..273d717572d1 --- /dev/null +++ b/tests/expected/shadow/slices/slice_split/test.rs @@ -0,0 +1,35 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zghost-state + +// This test demonstrates a possible usage of the shadow memory API to check that +// every element of an array split into two slices is initialized. +// Since the instrumentation is done manually in the harness only but not inside +// the `split_at_checked` function, the test only verifies that the resulting +// slices occupy the same memory as the original array. + +const N: usize = 16; + +static mut SM: kani::shadow::ShadowMem = kani::shadow::ShadowMem::new(false); + +#[kani::proof] +#[kani::unwind(17)] +fn check_reverse() { + let a: [bool; N] = kani::any(); + for i in &a { + unsafe { SM.set(i as *const bool, true) }; + } + let index: usize = kani::any_where(|x| *x <= N); + let (s1, s2) = a.split_at_checked(index).unwrap(); + + for i in s1 { + unsafe { + assert!(SM.get(i as *const bool)); + } + } + for i in s2 { + unsafe { + assert!(SM.get(i as *const bool)); + } + } +} From b1681e7c6b07baf57b5fa41b2145c2dca7212cf1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 10:33:46 +0000 Subject: [PATCH 188/225] Automatic toolchain upgrade to nightly-2024-07-16 (#3346) Update Rust toolchain from nightly-2024-07-15 to nightly-2024-07-16 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/d9284afea99e0969a0e692b9e9fd61ea4ba21366 up to https://github.com/rust-lang/rust/commit/24d2ac0b56fcbde13d827745f66e73efb1e17156. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index a20f8e321860..855d4b943288 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-07-15" +channel = "nightly-2024-07-16" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 852fd8f605c9e2196eaec75b70452c9832beaf67 Mon Sep 17 00:00:00 2001 From: Matias Scharager Date: Wed, 17 Jul 2024 12:17:25 -0400 Subject: [PATCH 189/225] Function Contracts: Modify Slices (#3295) Using the `__CPROVER_object_upto` function to pass modifies clauses to asserts clauses in goto for rust slices. Resolves #2908 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: Matias Scharager Co-authored-by: Celina G. Val Co-authored-by: Felipe R. Monteiro --- .../codegen_cprover_gotoc/codegen/contract.rs | 66 ++++++++++++++-- .../src/kani_middle/transform/contracts.rs | 79 ++++++++++++++++++- library/kani/src/internal.rs | 77 ++++++++++++++---- .../src/sysroot/contracts/check.rs | 12 ++- .../src/sysroot/contracts/replace.rs | 2 +- .../nondeterministic_size.expected | 5 ++ .../nondeterministic_size.rs | 17 ++++ .../slice_of_array.expected | 5 ++ .../modifies_fat_pointer/slice_of_array.rs | 17 ++++ .../string_rewrite_ignore.rs | 19 +++++ .../modifies_fat_pointer/u32slice.expected | 5 ++ .../modifies_fat_pointer/u32slice.rs | 17 ++++ .../modifies_fat_pointer/u8slice.expected | 5 ++ .../modifies_fat_pointer/u8slice.rs | 17 ++++ 14 files changed, 314 insertions(+), 29 deletions(-) create mode 100644 tests/expected/function-contract/modifies_fat_pointer/nondeterministic_size.expected create mode 100644 tests/expected/function-contract/modifies_fat_pointer/nondeterministic_size.rs create mode 100644 tests/expected/function-contract/modifies_fat_pointer/slice_of_array.expected create mode 100644 tests/expected/function-contract/modifies_fat_pointer/slice_of_array.rs create mode 100644 tests/expected/function-contract/modifies_fat_pointer/string_rewrite_ignore.rs create mode 100644 tests/expected/function-contract/modifies_fat_pointer/u32slice.expected create mode 100644 tests/expected/function-contract/modifies_fat_pointer/u32slice.rs create mode 100644 tests/expected/function-contract/modifies_fat_pointer/u8slice.expected create mode 100644 tests/expected/function-contract/modifies_fat_pointer/u8slice.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs index c7e7d5b817bd..7f19e5814ce0 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/contract.rs @@ -3,7 +3,7 @@ use crate::codegen_cprover_gotoc::GotocCtx; use crate::kani_middle::attributes::KaniAttributes; use cbmc::goto_program::FunctionContract; -use cbmc::goto_program::{Lambda, Location}; +use cbmc::goto_program::{Expr, Lambda, Location, Type}; use kani_metadata::AssignsContract; use rustc_hir::def_id::DefId as InternalDefId; use rustc_smir::rustc_internal; @@ -12,6 +12,8 @@ use stable_mir::mir::Local; use stable_mir::CrateDef; use tracing::debug; +use stable_mir::ty::{RigidTy, TyKind}; + impl<'tcx> GotocCtx<'tcx> { /// Given the `proof_for_contract` target `function_under_contract` and the reachable `items`, /// find or create the `AssignsContract` that needs to be enforced and attach it to the symbol @@ -142,11 +144,63 @@ impl<'tcx> GotocCtx<'tcx> { let assigns = modified_places .into_iter() .map(|local| { - Lambda::as_contract_for( - &goto_annotated_fn_typ, - None, - self.codegen_place_stable(&local.into(), loc).unwrap().goto_expr.dereference(), - ) + if self.is_fat_pointer_stable(self.local_ty_stable(local)) { + let unref = match self.local_ty_stable(local).kind() { + TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => ty, + kind => unreachable!("{:?} is not a reference", kind), + }; + let size = match unref.kind() { + TyKind::RigidTy(RigidTy::Slice(elt_type)) => { + elt_type.layout().unwrap().shape().size.bytes() + } + TyKind::RigidTy(RigidTy::Str) => 1, + // For adt, see https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp + TyKind::RigidTy(RigidTy::Adt(..)) => { + todo!("Adt fat pointers not implemented") + } + kind => unreachable!("Generating a slice fat pointer to {:?}", kind), + }; + Lambda::as_contract_for( + &goto_annotated_fn_typ, + None, + Expr::symbol_expression( + "__CPROVER_object_upto", + Type::code( + vec![ + Type::empty() + .to_pointer() + .as_parameter(None, Some("ptr".into())), + Type::size_t().as_parameter(None, Some("size".into())), + ], + Type::empty(), + ), + ) + .call(vec![ + self.codegen_place_stable(&local.into(), loc) + .unwrap() + .goto_expr + .member("data", &self.symbol_table) + .cast_to(Type::empty().to_pointer()), + self.codegen_place_stable(&local.into(), loc) + .unwrap() + .goto_expr + .member("len", &self.symbol_table) + .mul(Expr::size_constant( + size.try_into().unwrap(), + &self.symbol_table, + )), + ]), + ) + } else { + Lambda::as_contract_for( + &goto_annotated_fn_typ, + None, + self.codegen_place_stable(&local.into(), loc) + .unwrap() + .goto_expr + .dereference(), + ) + } }) .chain(shadow_memory_assign) .collect(); diff --git a/kani-compiler/src/kani_middle/transform/contracts.rs b/kani-compiler/src/kani_middle/transform/contracts.rs index eb5266e0a0eb..3a835c7f3cb6 100644 --- a/kani-compiler/src/kani_middle/transform/contracts.rs +++ b/kani-compiler/src/kani_middle/transform/contracts.rs @@ -11,20 +11,29 @@ use rustc_middle::ty::TyCtxt; use rustc_smir::rustc_internal; use stable_mir::mir::mono::Instance; use stable_mir::mir::{Body, ConstOperand, Operand, TerminatorKind}; -use stable_mir::ty::{FnDef, MirConst, RigidTy, TyKind}; +use stable_mir::ty::{FnDef, MirConst, RigidTy, TyKind, TypeAndMut}; use stable_mir::{CrateDef, DefId}; use std::collections::HashSet; use std::fmt::Debug; use tracing::{debug, trace}; -/// Check if we can replace calls to any_modifies. +/// Check if we can replace calls to any_modifies or write_any. /// /// This pass will replace the entire body, and it should only be applied to stubs /// that have a body. +/// +/// write_any is replaced with one of write_any_slim, write_any_slice, or write_any_str +/// depending on what the type of the input it +/// +/// any_modifies is replaced with any #[derive(Debug)] pub struct AnyModifiesPass { kani_any: Option, kani_any_modifies: Option, + kani_write_any: Option, + kani_write_any_slim: Option, + kani_write_any_slice: Option, + kani_write_any_str: Option, stubbed: HashSet, target_fn: Option, } @@ -78,6 +87,18 @@ impl AnyModifiesPass { let kani_any_modifies = tcx .get_diagnostic_item(rustc_span::symbol::Symbol::intern("KaniAnyModifies")) .map(item_fn_def); + let kani_write_any = tcx + .get_diagnostic_item(rustc_span::symbol::Symbol::intern("KaniWriteAny")) + .map(item_fn_def); + let kani_write_any_slim = tcx + .get_diagnostic_item(rustc_span::symbol::Symbol::intern("KaniWriteAnySlim")) + .map(item_fn_def); + let kani_write_any_slice = tcx + .get_diagnostic_item(rustc_span::symbol::Symbol::intern("KaniWriteAnySlice")) + .map(item_fn_def); + let kani_write_any_str = tcx + .get_diagnostic_item(rustc_span::symbol::Symbol::intern("KaniWriteAnyStr")) + .map(item_fn_def); let (target_fn, stubbed) = if let Some(harness) = unit.harnesses.first() { let attributes = KaniAttributes::for_instance(tcx, *harness); let target_fn = @@ -86,7 +107,16 @@ impl AnyModifiesPass { } else { (None, HashSet::new()) }; - AnyModifiesPass { kani_any, kani_any_modifies, target_fn, stubbed } + AnyModifiesPass { + kani_any, + kani_any_modifies, + kani_write_any, + kani_write_any_slim, + kani_write_any_slice, + kani_write_any_str, + target_fn, + stubbed, + } } /// If we apply `transform_any_modifies` in all contract-generated items, @@ -105,7 +135,7 @@ impl AnyModifiesPass { let mut changed = false; let locals = body.locals().to_vec(); for bb in body.blocks.iter_mut() { - let TerminatorKind::Call { func, .. } = &mut bb.terminator.kind else { continue }; + let TerminatorKind::Call { func, args, .. } = &mut bb.terminator.kind else { continue }; if let TyKind::RigidTy(RigidTy::FnDef(def, instance_args)) = func.ty(&locals).unwrap().kind() && Some(def) == self.kani_any_modifies @@ -117,6 +147,47 @@ impl AnyModifiesPass { *func = Operand::Constant(new_func); changed = true; } + + // if this is a valid kani::write_any function + if let TyKind::RigidTy(RigidTy::FnDef(def, instance_args)) = + func.ty(&locals).unwrap().kind() + && Some(def) == self.kani_write_any + && args.len() == 1 + && let Some(fn_sig) = func.ty(&locals).unwrap().kind().fn_sig() + && let Some(TypeAndMut { ty: internal_type, mutability: _ }) = + fn_sig.skip_binder().inputs()[0].kind().builtin_deref(true) + { + // case on the type of the input + if let TyKind::RigidTy(RigidTy::Slice(_)) = internal_type.kind() { + //if the input is a slice, use write_any_slice + let instance = + Instance::resolve(self.kani_write_any_slice.unwrap(), &instance_args) + .unwrap(); + let literal = MirConst::try_new_zero_sized(instance.ty()).unwrap(); + let span = bb.terminator.span; + let new_func = ConstOperand { span, user_ty: None, const_: literal }; + *func = Operand::Constant(new_func); + } else if let TyKind::RigidTy(RigidTy::Str) = internal_type.kind() { + //if the input is a str, use write_any_str + let instance = + Instance::resolve(self.kani_write_any_str.unwrap(), &instance_args) + .unwrap(); + let literal = MirConst::try_new_zero_sized(instance.ty()).unwrap(); + let span = bb.terminator.span; + let new_func = ConstOperand { span, user_ty: None, const_: literal }; + *func = Operand::Constant(new_func); + } else { + //otherwise, use write_any_slim + let instance = + Instance::resolve(self.kani_write_any_slim.unwrap(), &instance_args) + .unwrap(); + let literal = MirConst::try_new_zero_sized(instance.ty()).unwrap(); + let span = bb.terminator.span; + let new_func = ConstOperand { span, user_ty: None, const_: literal }; + *func = Operand::Constant(new_func); + } + changed = true; + } } (changed, body) } diff --git a/library/kani/src/internal.rs b/library/kani/src/internal.rs index 509f2cf51962..22026065106a 100644 --- a/library/kani/src/internal.rs +++ b/library/kani/src/internal.rs @@ -1,13 +1,16 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT +use crate::arbitrary::Arbitrary; +use std::ptr; + /// Helper trait for code generation for `modifies` contracts. /// /// We allow the user to provide us with a pointer-like object that we convert as needed. #[doc(hidden)] pub trait Pointer<'a> { /// Type of the pointed-to data - type Inner; + type Inner: ?Sized; /// Used for checking assigns contracts where we pass immutable references to the function. /// @@ -15,23 +18,21 @@ pub trait Pointer<'a> { /// argument, for instance one of type `&mut _`, in the `modifies` clause which would move it. unsafe fn decouple_lifetime(&self) -> &'a Self::Inner; - /// used for havocking on replecement of a `modifies` clause. - unsafe fn assignable(self) -> &'a mut Self::Inner; + unsafe fn assignable(self) -> *mut Self::Inner; } -impl<'a, 'b, T> Pointer<'a> for &'b T { +impl<'a, 'b, T: ?Sized> Pointer<'a> for &'b T { type Inner = T; unsafe fn decouple_lifetime(&self) -> &'a Self::Inner { std::mem::transmute(*self) } - #[allow(clippy::transmute_ptr_to_ref)] - unsafe fn assignable(self) -> &'a mut Self::Inner { + unsafe fn assignable(self) -> *mut Self::Inner { std::mem::transmute(self as *const T) } } -impl<'a, 'b, T> Pointer<'a> for &'b mut T { +impl<'a, 'b, T: ?Sized> Pointer<'a> for &'b mut T { type Inner = T; #[allow(clippy::transmute_ptr_to_ref)] @@ -39,32 +40,30 @@ impl<'a, 'b, T> Pointer<'a> for &'b mut T { std::mem::transmute::<_, &&'a T>(self) } - unsafe fn assignable(self) -> &'a mut Self::Inner { - std::mem::transmute(self) + unsafe fn assignable(self) -> *mut Self::Inner { + self as *mut T } } -impl<'a, T> Pointer<'a> for *const T { +impl<'a, T: ?Sized> Pointer<'a> for *const T { type Inner = T; unsafe fn decouple_lifetime(&self) -> &'a Self::Inner { &**self as &'a T } - #[allow(clippy::transmute_ptr_to_ref)] - unsafe fn assignable(self) -> &'a mut Self::Inner { + unsafe fn assignable(self) -> *mut Self::Inner { std::mem::transmute(self) } } -impl<'a, T> Pointer<'a> for *mut T { +impl<'a, T: ?Sized> Pointer<'a> for *mut T { type Inner = T; unsafe fn decouple_lifetime(&self) -> &'a Self::Inner { &**self as &'a T } - #[allow(clippy::transmute_ptr_to_ref)] - unsafe fn assignable(self) -> &'a mut Self::Inner { - std::mem::transmute(self) + unsafe fn assignable(self) -> *mut Self::Inner { + self } } @@ -97,3 +96,49 @@ pub fn init_contracts() {} pub fn apply_closure bool>(f: U, x: &T) -> bool { f(x) } + +/// Recieves a reference to a pointer-like object and assigns kani::any_modifies to that object. +/// Only for use within function contracts and will not be replaced if the recursive or function stub +/// replace contracts are not used. +#[crate::unstable(feature = "function-contracts", issue = "none", reason = "function-contracts")] +#[rustc_diagnostic_item = "KaniWriteAny"] +#[inline(never)] +#[doc(hidden)] +pub unsafe fn write_any(_pointer: *mut T) { + // This function should not be reacheable. + // Users must include `#[kani::recursion]` in any function contracts for recursive functions; + // otherwise, this might not be properly instantiate. We mark this as unreachable to make + // sure Kani doesn't report any false positives. + unreachable!() +} + +/// Fill in a slice with kani::any. +/// Intended as a post compilation replacement for write_any +#[crate::unstable(feature = "function-contracts", issue = "none", reason = "function-contracts")] +#[rustc_diagnostic_item = "KaniWriteAnySlice"] +#[inline(always)] +pub unsafe fn write_any_slice(slice: *mut [T]) { + (*slice).fill_with(T::any) +} + +/// Fill in a pointer with kani::any. +/// Intended as a post compilation replacement for write_any +#[crate::unstable(feature = "function-contracts", issue = "none", reason = "function-contracts")] +#[rustc_diagnostic_item = "KaniWriteAnySlim"] +#[inline(always)] +pub unsafe fn write_any_slim(pointer: *mut T) { + ptr::write(pointer, T::any()) +} + +/// Fill in a str with kani::any. +/// Intended as a post compilation replacement for write_any. +/// Not yet implemented +#[crate::unstable(feature = "function-contracts", issue = "none", reason = "function-contracts")] +#[rustc_diagnostic_item = "KaniWriteAnyStr"] +#[inline(always)] +pub unsafe fn write_any_str(_s: *mut str) { + //TODO: strings introduce new UB + //(*s).as_bytes_mut().fill_with(u8::any) + //TODO: String validation + unimplemented!("Kani does not support creating arbitrary `str`") +} diff --git a/library/kani_macros/src/sysroot/contracts/check.rs b/library/kani_macros/src/sysroot/contracts/check.rs index e76286b398cb..f804fc70c5f5 100644 --- a/library/kani_macros/src/sysroot/contracts/check.rs +++ b/library/kani_macros/src/sysroot/contracts/check.rs @@ -156,11 +156,19 @@ impl<'a> ContractConditionsHandler<'a> { let sig = &mut self.annotated_fn.sig; for (arg, arg_type) in wrapper_args.clone().zip(type_params) { // Add the type parameter to the function signature's generic parameters list + let mut bounds: syn::punctuated::Punctuated = + syn::punctuated::Punctuated::new(); + bounds.push(syn::TypeParamBound::Trait(syn::TraitBound { + paren_token: None, + modifier: syn::TraitBoundModifier::Maybe(Token![?](Span::call_site())), + lifetimes: None, + path: syn::Ident::new("Sized", Span::call_site()).into(), + })); sig.generics.params.push(syn::GenericParam::Type(syn::TypeParam { attrs: vec![], ident: arg_type.clone(), - colon_token: None, - bounds: Default::default(), + colon_token: Some(Token![:](Span::call_site())), + bounds: bounds, eq_token: None, default: None, })); diff --git a/library/kani_macros/src/sysroot/contracts/replace.rs b/library/kani_macros/src/sysroot/contracts/replace.rs index 7a522ea98e08..280839dcafca 100644 --- a/library/kani_macros/src/sysroot/contracts/replace.rs +++ b/library/kani_macros/src/sysroot/contracts/replace.rs @@ -94,7 +94,7 @@ impl<'a> ContractConditionsHandler<'a> { let result = Ident::new(INTERNAL_RESULT_IDENT, Span::call_site()); quote!( #(#before)* - #(*unsafe { kani::internal::Pointer::assignable(kani::internal::untracked_deref(&(#attr))) } = kani::any_modifies();)* + #(unsafe{kani::internal::write_any(kani::internal::Pointer::assignable(kani::internal::untracked_deref(&#attr)))};)* #(#after)* #result ) diff --git a/tests/expected/function-contract/modifies_fat_pointer/nondeterministic_size.expected b/tests/expected/function-contract/modifies_fat_pointer/nondeterministic_size.expected new file mode 100644 index 000000000000..2fab679b5cba --- /dev/null +++ b/tests/expected/function-contract/modifies_fat_pointer/nondeterministic_size.expected @@ -0,0 +1,5 @@ +assertion\ +- Status: SUCCESS\ +- Description: "|_| x.iter().map(|v| *v == 0).fold(true,|a,b|a&b)"\ + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/modifies_fat_pointer/nondeterministic_size.rs b/tests/expected/function-contract/modifies_fat_pointer/nondeterministic_size.rs new file mode 100644 index 000000000000..faf1d9d44581 --- /dev/null +++ b/tests/expected/function-contract/modifies_fat_pointer/nondeterministic_size.rs @@ -0,0 +1,17 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +// Test that modifies a slice of nondeterministic size + +#[kani::modifies(x)] +#[kani::ensures(|_| x.iter().map(|v| *v == 0).fold(true,|a,b|a&b))] +fn zero(x: &mut [u8]) { + x.fill(0) +} + +#[kani::proof_for_contract(zero)] +fn harness() { + let mut x = [0..kani::any()].map(|_| kani::any()); + zero(&mut x); +} diff --git a/tests/expected/function-contract/modifies_fat_pointer/slice_of_array.expected b/tests/expected/function-contract/modifies_fat_pointer/slice_of_array.expected new file mode 100644 index 000000000000..d0fc296ef4d7 --- /dev/null +++ b/tests/expected/function-contract/modifies_fat_pointer/slice_of_array.expected @@ -0,0 +1,5 @@ +assertion\ +- Status: SUCCESS\ +- Description: "|_| x[0..3].iter().map(|v| *v == 0).fold(true,|a,b|a&b)"\ + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/modifies_fat_pointer/slice_of_array.rs b/tests/expected/function-contract/modifies_fat_pointer/slice_of_array.rs new file mode 100644 index 000000000000..a0f6c10ad694 --- /dev/null +++ b/tests/expected/function-contract/modifies_fat_pointer/slice_of_array.rs @@ -0,0 +1,17 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +// Test that modifies a slice containing u32 size data + +#[kani::modifies(&x[0..3])] +#[kani::ensures(|_| x[0..3].iter().map(|v| *v == 0).fold(true,|a,b|a&b))] +fn zero(x: &mut [u32; 6]) { + x[0..3].fill(0) +} + +#[kani::proof_for_contract(zero)] +fn harness() { + let mut x = [kani::any(), kani::any(), kani::any(), kani::any(), kani::any(), kani::any()]; + zero(&mut x); +} diff --git a/tests/expected/function-contract/modifies_fat_pointer/string_rewrite_ignore.rs b/tests/expected/function-contract/modifies_fat_pointer/string_rewrite_ignore.rs new file mode 100644 index 000000000000..d8780068a0da --- /dev/null +++ b/tests/expected/function-contract/modifies_fat_pointer/string_rewrite_ignore.rs @@ -0,0 +1,19 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +// Test that modifies a string + +#[kani::requires(x == "aaa")] +#[kani::modifies(x)] +#[kani::ensures(|_| x == "AAA")] +fn to_upper(x: &mut str) { + x.make_ascii_uppercase(); +} + +#[kani::proof_for_contract(to_upper)] +fn harness() { + let mut s = String::from("aaa"); + let x: &mut str = s.as_mut_str(); + to_upper(x); +} diff --git a/tests/expected/function-contract/modifies_fat_pointer/u32slice.expected b/tests/expected/function-contract/modifies_fat_pointer/u32slice.expected new file mode 100644 index 000000000000..2fab679b5cba --- /dev/null +++ b/tests/expected/function-contract/modifies_fat_pointer/u32slice.expected @@ -0,0 +1,5 @@ +assertion\ +- Status: SUCCESS\ +- Description: "|_| x.iter().map(|v| *v == 0).fold(true,|a,b|a&b)"\ + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/modifies_fat_pointer/u32slice.rs b/tests/expected/function-contract/modifies_fat_pointer/u32slice.rs new file mode 100644 index 000000000000..ea61de83e969 --- /dev/null +++ b/tests/expected/function-contract/modifies_fat_pointer/u32slice.rs @@ -0,0 +1,17 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +// Test that modifies a slice containing u32 size data + +#[kani::modifies(x)] +#[kani::ensures(|_| x.iter().map(|v| *v == 0).fold(true,|a,b|a&b))] +fn zero(x: &mut [u32]) { + x.fill(0) +} + +#[kani::proof_for_contract(zero)] +fn harness() { + let mut x = [kani::any(), kani::any(), kani::any()]; + zero(&mut x); +} diff --git a/tests/expected/function-contract/modifies_fat_pointer/u8slice.expected b/tests/expected/function-contract/modifies_fat_pointer/u8slice.expected new file mode 100644 index 000000000000..2fab679b5cba --- /dev/null +++ b/tests/expected/function-contract/modifies_fat_pointer/u8slice.expected @@ -0,0 +1,5 @@ +assertion\ +- Status: SUCCESS\ +- Description: "|_| x.iter().map(|v| *v == 0).fold(true,|a,b|a&b)"\ + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/modifies_fat_pointer/u8slice.rs b/tests/expected/function-contract/modifies_fat_pointer/u8slice.rs new file mode 100644 index 000000000000..07e213db0adc --- /dev/null +++ b/tests/expected/function-contract/modifies_fat_pointer/u8slice.rs @@ -0,0 +1,17 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +// Test that modifies a slice containing bytesize data + +#[kani::modifies(x)] +#[kani::ensures(|_| x.iter().map(|v| *v == 0).fold(true,|a,b|a&b))] +fn zero(x: &mut [u8]) { + x.fill(0) +} + +#[kani::proof_for_contract(zero)] +fn harness() { + let mut x = [kani::any(), kani::any(), kani::any()]; + zero(&mut x); +} From f51149c27668c4ded872792222eeb7f0673fb487 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 07:25:39 +0200 Subject: [PATCH 190/225] Automatic toolchain upgrade to nightly-2024-07-17 (#3352) Update Rust toolchain from nightly-2024-07-16 to nightly-2024-07-17 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/24d2ac0b56fcbde13d827745f66e73efb1e17156 up to https://github.com/rust-lang/rust/commit/032be6f7bbe091c7dfa29f115e94b9cc9bae1758. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 855d4b943288..bd69934d573c 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-07-16" +channel = "nightly-2024-07-17" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From ac823d33a3540d71ecdb115395013373f8e6e55c Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Thu, 18 Jul 2024 18:37:27 -0500 Subject: [PATCH 191/225] Upgrade toolchain to 2024-07-18 (#3355) Resolves #3353 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 | 6 ++---- rust-toolchain.toml | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index abb69a655472..ff98880ac4d7 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -24,9 +24,7 @@ use kani_metadata::artifact::convert_type; use kani_metadata::UnsupportedFeature; use kani_metadata::{ArtifactType, HarnessMetadata, KaniMetadata}; use kani_metadata::{AssignsContract, CompilerArtifactStub}; -use rustc_codegen_ssa::back::archive::{ - get_native_object_symbols, ArArchiveBuilder, ArchiveBuilder, -}; +use rustc_codegen_ssa::back::archive::{ArArchiveBuilder, ArchiveBuilder, DEFAULT_OBJECT_READER}; use rustc_codegen_ssa::back::metadata::create_wrapper_file; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CodegenResults, CrateInfo}; @@ -402,7 +400,7 @@ impl CodegenBackend for GotocCodegenBackend { debug!(?crate_type, ?out_path, "link"); if *crate_type == CrateType::Rlib { // Emit the `rlib` that contains just one file: `.rmeta` - let mut builder = Box::new(ArArchiveBuilder::new(sess, get_native_object_symbols)); + let mut builder = Box::new(ArArchiveBuilder::new(sess, &DEFAULT_OBJECT_READER)); let tmp_dir = TempFileBuilder::new().prefix("kani").tempdir().unwrap(); let path = MaybeTempDir::new(tmp_dir, sess.opts.cg.save_temps); let (metadata, _metadata_position) = create_wrapper_file( diff --git a/rust-toolchain.toml b/rust-toolchain.toml index bd69934d573c..c04db242fd5b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-07-17" +channel = "nightly-2024-07-18" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 96d1147fc3b59bf71ac6618aca957ac6ba40073a Mon Sep 17 00:00:00 2001 From: Artem Agvanian Date: Fri, 19 Jul 2024 08:56:01 -0700 Subject: [PATCH 192/225] Support for disabling automatically generated pointer checks to avoid reinstrumentation (#3344) Recently added memory initialization checks (see #3300 for an overview) code suffers from re-instrumentation due to automatically added pointer checks to the instrumentation code. This PR adds an internal `kanitool::disable_checks` attribute to disable automatically generated CBMC checks inside internal instrumentation. This, in turn, relies on CBMC pragmas (https://www.cprover.org/cprover-manual/properties/#usinggotoinstrument). 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/location.rs | 35 +++++++--- cprover_bindings/src/irep/to_irep.rs | 46 ++++++++++--- .../src/codegen_cprover_gotoc/codegen/span.rs | 68 +++++++++++++++++++ kani-compiler/src/kani_middle/attributes.rs | 14 +++- library/kani/src/mem_init.rs | 17 ++++- .../alloc-zeroed.rs | 22 ++++++ .../mem-init-reinstrumentation/config.yml | 4 ++ .../mem-init-reinstrumentation.expected | 1 + .../mem-init-reinstrumentation.sh | 20 ++++++ 9 files changed, 204 insertions(+), 23 deletions(-) create mode 100644 tests/script-based-pre/mem-init-reinstrumentation/alloc-zeroed.rs create mode 100644 tests/script-based-pre/mem-init-reinstrumentation/config.yml create mode 100644 tests/script-based-pre/mem-init-reinstrumentation/mem-init-reinstrumentation.expected create mode 100755 tests/script-based-pre/mem-init-reinstrumentation/mem-init-reinstrumentation.sh diff --git a/cprover_bindings/src/goto_program/location.rs b/cprover_bindings/src/goto_program/location.rs index 4097d075276d..9a2a046f6387 100644 --- a/cprover_bindings/src/goto_program/location.rs +++ b/cprover_bindings/src/goto_program/location.rs @@ -19,6 +19,7 @@ pub enum Location { start_col: Option, end_line: u64, end_col: Option, + pragmas: &'static [&'static str], // CBMC `#pragma check` statements per location. }, /// Location for Statements that use Property Class and Description - Assert, Assume, Cover etc. Property { @@ -28,6 +29,7 @@ pub enum Location { col: Option, comment: InternedString, property_class: InternedString, + pragmas: &'static [&'static str], // CBMC `#pragma check` statements per location. }, /// Covers cases where Location Details are unknown or set as None but Property Class is needed. PropertyUnknownLocation { comment: InternedString, property_class: InternedString }, @@ -99,6 +101,7 @@ impl Location { start_col: Option, end_line: T, end_col: Option, + pragmas: &'static [&'static str], ) -> Location where T: TryInto, @@ -117,6 +120,7 @@ impl Location { start_col: start_col_into, end_line: end_line_into, end_col: end_col_into, + pragmas, } } @@ -128,6 +132,7 @@ impl Location { col: Option, comment: U, property_name: U, + pragmas: &'static [&'static str], ) -> Location where T: TryInto, @@ -140,7 +145,7 @@ impl Location { let function = function.intern(); let property_class = property_name.into(); let comment = comment.into(); - Location::Property { file, function, line, col, comment, property_class } + Location::Property { file, function, line, col, comment, property_class, pragmas } } /// Create a Property type Location from an already existing Location type @@ -157,17 +162,25 @@ impl Location { None, comment.into(), property_name.into(), + &[], + ), + Location::Loc { + file, + function, + start_line, + start_col, + end_line: _, + end_col: _, + pragmas, + } => Location::property_location( + file.into(), + function.intern(), + start_line, + start_col, + comment.into(), + property_name.into(), + pragmas, ), - Location::Loc { file, function, start_line, start_col, end_line: _, end_col: _ } => { - Location::property_location( - file.into(), - function.intern(), - start_line, - start_col, - comment.into(), - property_name.into(), - ) - } Location::Property { .. } => location, Location::PropertyUnknownLocation { .. } => location, // This converts None type Locations to PropertyUnknownLocation type which inserts Property Class and Description diff --git a/cprover_bindings/src/irep/to_irep.rs b/cprover_bindings/src/irep/to_irep.rs index 4b5c86350172..d1e84669121d 100644 --- a/cprover_bindings/src/irep/to_irep.rs +++ b/cprover_bindings/src/irep/to_irep.rs @@ -389,15 +389,32 @@ impl ToIrep for Location { (IrepId::Function, Irep::just_string_id(function_name.to_string())), ]) .with_named_sub_option(IrepId::Line, line.map(Irep::just_int_id)), - Location::Loc { file, function, start_line, start_col, end_line: _, end_col: _ } => { - Irep::just_named_sub(linear_map![ - (IrepId::File, Irep::just_string_id(file.to_string())), - (IrepId::Line, Irep::just_int_id(*start_line)), - ]) - .with_named_sub_option(IrepId::Column, start_col.map(Irep::just_int_id)) - .with_named_sub_option(IrepId::Function, function.map(Irep::just_string_id)) - } - Location::Property { file, function, line, col, property_class, comment } => { + Location::Loc { + file, + function, + start_line, + start_col, + end_line: _, + end_col: _, + pragmas, + } => Irep::just_named_sub(linear_map![ + (IrepId::File, Irep::just_string_id(file.to_string())), + (IrepId::Line, Irep::just_int_id(*start_line)), + ]) + .with_named_sub_option(IrepId::Column, start_col.map(Irep::just_int_id)) + .with_named_sub_option(IrepId::Function, function.map(Irep::just_string_id)) + .with_named_sub_option( + IrepId::Pragma, + Some(Irep::just_named_sub( + pragmas + .iter() + .map(|pragma| { + (IrepId::from_string(*pragma), Irep::just_id(IrepId::EmptyString)) + }) + .collect(), + )), + ), + Location::Property { file, function, line, col, property_class, comment, pragmas } => { Irep::just_named_sub(linear_map![ (IrepId::File, Irep::just_string_id(file.to_string())), (IrepId::Line, Irep::just_int_id(*line)), @@ -409,6 +426,17 @@ impl ToIrep for Location { IrepId::PropertyClass, Irep::just_string_id(property_class.to_string()), ) + .with_named_sub_option( + IrepId::Pragma, + Some(Irep::just_named_sub( + pragmas + .iter() + .map(|pragma| { + (IrepId::from_string(*pragma), Irep::just_id(IrepId::EmptyString)) + }) + .collect(), + )), + ) } Location::PropertyUnknownLocation { property_class, comment } => { Irep::just_named_sub(linear_map![ diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs index aadb4fbebed9..41a3da11324b 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/span.rs @@ -5,9 +5,31 @@ use crate::codegen_cprover_gotoc::GotocCtx; use cbmc::goto_program::Location; +use lazy_static::lazy_static; +use rustc_ast::Attribute; use rustc_smir::rustc_internal; use rustc_span::Span; use stable_mir::ty::Span as SpanStable; +use std::collections::HashMap; + +lazy_static! { + /// Pragmas key-value store to prevent CBMC from generating automatic checks. + /// This list is taken from https://github.com/diffblue/cbmc/blob/develop/regression/cbmc/pragma_cprover_enable_all/main.c. + static ref PRAGMAS: HashMap<&'static str, &'static str> = + [("bounds", "disable:bounds-check"), + ("pointer", "disable:pointer-check"), + ("div-by-zero", "disable:div-by-zero-check"), + ("float-div-by-zero", "disable:float-div-by-zero-check"), + ("enum-range", "disable:enum-range-check"), + ("signed-overflow", "disable:signed-overflow-check"), + ("unsigned-overflow", "disable:unsigned-overflow-check"), + ("pointer-overflow", "disable:pointer-overflow-check"), + ("float-overflow", "disable:float-overflow-check"), + ("conversion", "disable:conversion-check"), + ("undefined-shift", "disable:undefined-shift-check"), + ("nan", "disable:nan-check"), + ("pointer-primitive", "disable:pointer-primitive-check")].iter().copied().collect(); +} impl<'tcx> GotocCtx<'tcx> { pub fn codegen_span(&self, sp: &Span) -> Location { @@ -15,6 +37,36 @@ impl<'tcx> GotocCtx<'tcx> { } pub fn codegen_span_stable(&self, sp: SpanStable) -> Location { + // Attribute to mark functions as where automatic pointer checks should not be generated. + let should_skip_ptr_checks_attr = vec![ + rustc_span::symbol::Symbol::intern("kanitool"), + rustc_span::symbol::Symbol::intern("disable_checks"), + ]; + let pragmas: &'static [&str] = { + let disabled_checks: Vec<_> = self + .current_fn + .as_ref() + .map(|current_fn| { + let instance = current_fn.instance(); + self.tcx + .get_attrs_by_path(instance.def.def_id(), &should_skip_ptr_checks_attr) + .collect() + }) + .unwrap_or_default(); + disabled_checks + .iter() + .map(|attr| { + 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()) + }) + .collect::>() + .leak() // This is to preserve `Location` being Copy, but could blow up the memory utilization of compiler. + }; let loc = sp.get_lines(); Location::new( sp.get_filename().to_string(), @@ -23,6 +75,7 @@ impl<'tcx> GotocCtx<'tcx> { Some(loc.start_col), loc.end_line, Some(loc.end_col), + pragmas, ) } @@ -39,3 +92,18 @@ impl<'tcx> GotocCtx<'tcx> { self.codegen_span(&topmost) } } + +/// Extracts the single argument from the attribute provided as a string. +/// For example, `disable_checks(foo)` return `Some("foo")` +fn parse_word(attr: &Attribute) -> Option { + // Vector of meta items , that contain the arguments given the attribute + let attr_args = attr.meta_item_list()?; + // Only extracts one string ident as a string + if attr_args.len() == 1 { + attr_args[0].ident().map(|ident| ident.to_string()) + } + // Return none if there are no attributes or if there's too many attributes + else { + None + } +} diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index 84ec8d627c59..9eb6d3d6ee4e 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -74,6 +74,9 @@ enum KaniAttributeKind { /// We use this attribute to properly instantiate `kani::any_modifies` in /// cases when recursion is present given our contracts instrumentation. Recursion, + /// Used to mark functions where generating automatic pointer checks should be disabled. This is + /// used later to automatically attach pragma statements to locations. + DisableChecks, } impl KaniAttributeKind { @@ -93,7 +96,8 @@ impl KaniAttributeKind { | KaniAttributeKind::CheckedWith | KaniAttributeKind::Modifies | KaniAttributeKind::InnerCheck - | KaniAttributeKind::IsContractGenerated => false, + | KaniAttributeKind::IsContractGenerated + | KaniAttributeKind::DisableChecks => false, } } @@ -382,6 +386,10 @@ impl<'tcx> KaniAttributes<'tcx> { KaniAttributeKind::InnerCheck => { self.inner_check(); } + KaniAttributeKind::DisableChecks => { + // Ignored here, because it should be an internal attribute. Actual validation + // happens when pragmas are generated. + } } } } @@ -490,6 +498,10 @@ impl<'tcx> KaniAttributes<'tcx> { | KaniAttributeKind::InnerCheck | KaniAttributeKind::ReplacedWith => { self.tcx.dcx().span_err(self.tcx.def_span(self.item), format!("Contracts are not supported on harnesses. (Found the kani-internal contract attribute `{}`)", kind.as_ref())); + }, + KaniAttributeKind::DisableChecks => { + // Internal attribute which shouldn't exist here. + unreachable!() } }; harness diff --git a/library/kani/src/mem_init.rs b/library/kani/src/mem_init.rs index a09e515d7e17..88847e9c4f3c 100644 --- a/library/kani/src/mem_init.rs +++ b/library/kani/src/mem_init.rs @@ -50,6 +50,7 @@ impl MemoryInitializationState { /// /// Such definition is necessary since both tracked object and tracked offset are chosen /// non-deterministically. + #[kanitool::disable_checks(pointer)] pub fn get( &mut self, ptr: *const u8, @@ -61,7 +62,7 @@ impl MemoryInitializationState { && self.tracked_offset >= offset && self.tracked_offset < offset + LAYOUT_SIZE { - !layout[(self.tracked_offset - offset) % LAYOUT_SIZE] || self.value + !layout[self.tracked_offset - offset] || self.value } else { true } @@ -73,6 +74,7 @@ impl MemoryInitializationState { /// /// Such definition is necessary since both tracked object and tracked offset are chosen /// non-deterministically. + #[kanitool::disable_checks(pointer)] pub fn set( &mut self, ptr: *const u8, @@ -85,7 +87,7 @@ impl MemoryInitializationState { && self.tracked_offset >= offset && self.tracked_offset < offset + LAYOUT_SIZE { - self.value = layout[(self.tracked_offset - offset) % LAYOUT_SIZE] && value; + self.value = layout[self.tracked_offset - offset] && value; } } @@ -95,6 +97,7 @@ impl MemoryInitializationState { /// /// Such definition is necessary since both tracked object and tracked offset are chosen /// non-deterministically. + #[kanitool::disable_checks(pointer)] pub fn get_slice( &mut self, ptr: *const u8, @@ -119,6 +122,7 @@ impl MemoryInitializationState { /// /// Such definition is necessary since both tracked object and tracked offset are chosen /// non-deterministically. + #[kanitool::disable_checks(pointer)] pub fn set_slice( &mut self, ptr: *const u8, @@ -142,6 +146,7 @@ impl MemoryInitializationState { static mut MEM_INIT_STATE: MemoryInitializationState = MemoryInitializationState::new(); /// Set tracked object and tracked offset to a non-deterministic value. +#[kanitool::disable_checks(pointer)] #[rustc_diagnostic_item = "KaniInitializeMemoryInitializationState"] fn initialize_memory_initialization_state() { unsafe { @@ -152,6 +157,7 @@ fn initialize_memory_initialization_state() { } /// Get initialization state of `num_elts` items laid out according to the `layout` starting at address `ptr`. +#[kanitool::disable_checks(pointer)] #[rustc_diagnostic_item = "KaniIsPtrInitialized"] fn is_ptr_initialized( ptr: *const T, @@ -165,6 +171,7 @@ fn is_ptr_initialized( } /// Set initialization state to `value` for `num_elts` items laid out according to the `layout` starting at address `ptr`. +#[kanitool::disable_checks(pointer)] #[rustc_diagnostic_item = "KaniSetPtrInitialized"] fn set_ptr_initialized( ptr: *const T, @@ -181,6 +188,7 @@ fn set_ptr_initialized( } /// Get initialization state of `num_elts` items laid out according to the `layout` starting at address `ptr`. +#[kanitool::disable_checks(pointer)] #[rustc_diagnostic_item = "KaniIsSliceChunkPtrInitialized"] fn is_slice_chunk_ptr_initialized( ptr: *const T, @@ -195,6 +203,7 @@ fn is_slice_chunk_ptr_initialized( } /// Set initialization state to `value` for `num_elts` items laid out according to the `layout` starting at address `ptr`. +#[kanitool::disable_checks(pointer)] #[rustc_diagnostic_item = "KaniSetSliceChunkPtrInitialized"] fn set_slice_chunk_ptr_initialized( ptr: *const T, @@ -212,6 +221,7 @@ fn set_slice_chunk_ptr_initialized( } /// Get initialization state of the slice, items of which are laid out according to the `layout` starting at address `ptr`. +#[kanitool::disable_checks(pointer)] #[rustc_diagnostic_item = "KaniIsSlicePtrInitialized"] fn is_slice_ptr_initialized( ptr: *const [T], @@ -225,6 +235,7 @@ fn is_slice_ptr_initialized( } /// Set initialization state of the slice, items of which are laid out according to the `layout` starting at address `ptr` to `value`. +#[kanitool::disable_checks(pointer)] #[rustc_diagnostic_item = "KaniSetSlicePtrInitialized"] fn set_slice_ptr_initialized( ptr: *const [T], @@ -241,6 +252,7 @@ fn set_slice_ptr_initialized( } /// Get initialization state of the string slice, items of which are laid out according to the `layout` starting at address `ptr`. +#[kanitool::disable_checks(pointer)] #[rustc_diagnostic_item = "KaniIsStrPtrInitialized"] fn is_str_ptr_initialized( ptr: *const str, @@ -254,6 +266,7 @@ fn is_str_ptr_initialized( } /// Set initialization state of the string slice, items of which are laid out according to the `layout` starting at address `ptr` to `value`. +#[kanitool::disable_checks(pointer)] #[rustc_diagnostic_item = "KaniSetStrPtrInitialized"] fn set_str_ptr_initialized( ptr: *const str, diff --git a/tests/script-based-pre/mem-init-reinstrumentation/alloc-zeroed.rs b/tests/script-based-pre/mem-init-reinstrumentation/alloc-zeroed.rs new file mode 100644 index 000000000000..891a97fd8c22 --- /dev/null +++ b/tests/script-based-pre/mem-init-reinstrumentation/alloc-zeroed.rs @@ -0,0 +1,22 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z uninit-checks + +use std::alloc::{alloc_zeroed, Layout}; +use std::slice::from_raw_parts; + +#[kani::proof] +fn alloc_zeroed_to_slice() { + let layout = Layout::from_size_align(32, 8).unwrap(); + unsafe { + // This returns initialized memory, so any further accesses are valid. + let ptr = alloc_zeroed(layout); + *ptr = 0x41; + *ptr.add(1) = 0x42; + *ptr.add(2) = 0x43; + *ptr.add(3) = 0x44; + *ptr.add(16) = 0x00; + let _slice1 = from_raw_parts(ptr, 16); + let _slice2 = from_raw_parts(ptr.add(16), 16); + } +} diff --git a/tests/script-based-pre/mem-init-reinstrumentation/config.yml b/tests/script-based-pre/mem-init-reinstrumentation/config.yml new file mode 100644 index 000000000000..ef61a9171954 --- /dev/null +++ b/tests/script-based-pre/mem-init-reinstrumentation/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: mem-init-reinstrumentation.sh +expected: mem-init-reinstrumentation.expected diff --git a/tests/script-based-pre/mem-init-reinstrumentation/mem-init-reinstrumentation.expected b/tests/script-based-pre/mem-init-reinstrumentation/mem-init-reinstrumentation.expected new file mode 100644 index 000000000000..59b64322ad50 --- /dev/null +++ b/tests/script-based-pre/mem-init-reinstrumentation/mem-init-reinstrumentation.expected @@ -0,0 +1 @@ +success: no pointer checks are detected in initialized memory instrumentaiton diff --git a/tests/script-based-pre/mem-init-reinstrumentation/mem-init-reinstrumentation.sh b/tests/script-based-pre/mem-init-reinstrumentation/mem-init-reinstrumentation.sh new file mode 100755 index 000000000000..f859864d1c9a --- /dev/null +++ b/tests/script-based-pre/mem-init-reinstrumentation/mem-init-reinstrumentation.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +set -u + +KANI_OUTPUT=`kani -Z uninit-checks alloc-zeroed.rs` +echo "$KANI_OUTPUT" | egrep -q "kani::mem_init::.*pointer_dereference" +INSTRUMENTATION_DETECTED=$? + +if [[ $INSTRUMENTATION_DETECTED == 0 ]]; then + echo "failed: pointer checks are detected in initialized memory instrumentaiton" + exit 1 +elif [[ $INSTRUMENTATION_DETECTED == 1 ]]; then + echo "success: no pointer checks are detected in initialized memory instrumentaiton" + exit 0 +else + echo "failed: error occured when runnning egrep" + exit 0 +fi From 03679923d1f1a862021532a66e1ab43248f36e56 Mon Sep 17 00:00:00 2001 From: Artem Agvanian Date: Fri, 19 Jul 2024 13:53:08 -0700 Subject: [PATCH 193/225] Add support for global transformations (#3348) This PR adds basic support for global transformations, which will be useful to implement full support of #3324 Changes: - Add edge annotations to the call graph - Implement global transformation pass infrastructure - Implement `dump_mir` as a global transformation pass 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 --- .../compiler_interface.rs | 30 +- kani-compiler/src/kani_middle/mod.rs | 43 +-- kani-compiler/src/kani_middle/reachability.rs | 304 +++++++++++------- .../kani_middle/transform/dump_mir_pass.rs | 69 ++++ .../src/kani_middle/transform/mod.rs | 59 +++- 5 files changed, 336 insertions(+), 169 deletions(-) create mode 100644 kani-compiler/src/kani_middle/transform/dump_mir_pass.rs diff --git a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs index ff98880ac4d7..986cd00e32a5 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs @@ -7,14 +7,14 @@ use crate::args::ReachabilityType; use crate::codegen_cprover_gotoc::GotocCtx; use crate::kani_middle::analysis; use crate::kani_middle::attributes::{is_test_harness_description, KaniAttributes}; +use crate::kani_middle::check_reachable_items; use crate::kani_middle::codegen_units::{CodegenUnit, CodegenUnits}; use crate::kani_middle::metadata::gen_test_metadata; use crate::kani_middle::provide; use crate::kani_middle::reachability::{ collect_reachable_items, filter_const_crate_items, filter_crate_items, }; -use crate::kani_middle::transform::BodyTransformation; -use crate::kani_middle::{check_reachable_items, dump_mir_items}; +use crate::kani_middle::transform::{BodyTransformation, GlobalPasses}; use crate::kani_queries::QueryDb; use cbmc::goto_program::Location; use cbmc::irep::goto_binary_serde::write_goto_binary_file; @@ -87,11 +87,33 @@ impl GotocCodegenBackend { check_contract: Option, mut transformer: BodyTransformation, ) -> (GotocCtx<'tcx>, Vec, Option) { - let items = with_timer( + let (items, call_graph) = with_timer( || collect_reachable_items(tcx, &mut transformer, starting_items), "codegen reachability analysis", ); - dump_mir_items(tcx, &mut transformer, &items, &symtab_goto.with_extension("kani.mir")); + + // Retrieve all instances from the currently codegened items. + let instances = items + .iter() + .filter_map(|item| match item { + MonoItem::Fn(instance) => Some(*instance), + MonoItem::Static(static_def) => { + let instance: Instance = (*static_def).into(); + instance.has_body().then_some(instance) + } + MonoItem::GlobalAsm(_) => None, + }) + .collect(); + + // Apply all transformation passes, including global passes. + let mut global_passes = GlobalPasses::new(&self.queries.lock().unwrap(), tcx); + global_passes.run_global_passes( + &mut transformer, + tcx, + starting_items, + instances, + call_graph, + ); // Follow rustc naming convention (cx is abbrev for context). // https://rustc-dev-guide.rust-lang.org/conventions.html#naming-conventions diff --git a/kani-compiler/src/kani_middle/mod.rs b/kani-compiler/src/kani_middle/mod.rs index 17b08b687e30..3c300e9da52c 100644 --- a/kani-compiler/src/kani_middle/mod.rs +++ b/kani-compiler/src/kani_middle/mod.rs @@ -4,9 +4,7 @@ //! and transformations. use std::collections::HashSet; -use std::path::Path; -use crate::kani_middle::transform::BodyTransformation; use crate::kani_queries::QueryDb; use rustc_hir::{def::DefKind, def_id::LOCAL_CRATE}; use rustc_middle::span_bug; @@ -15,18 +13,14 @@ use rustc_middle::ty::layout::{ LayoutOfHelpers, TyAndLayout, }; use rustc_middle::ty::{self, Instance as InstanceInternal, Ty as TyInternal, TyCtxt}; -use rustc_session::config::OutputType; use rustc_smir::rustc_internal; use rustc_span::source_map::respan; use rustc_span::Span; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{HasDataLayout, TargetDataLayout}; -use stable_mir::mir::mono::{Instance, MonoItem}; +use stable_mir::mir::mono::MonoItem; use stable_mir::ty::{FnDef, RigidTy, Span as SpanStable, TyKind}; use stable_mir::CrateDef; -use std::fs::File; -use std::io::BufWriter; -use std::io::Write; use self::attributes::KaniAttributes; @@ -92,41 +86,6 @@ pub fn check_reachable_items(tcx: TyCtxt, queries: &QueryDb, items: &[MonoItem]) tcx.dcx().abort_if_errors(); } -/// Print MIR for the reachable items if the `--emit mir` option was provided to rustc. -pub fn dump_mir_items( - tcx: TyCtxt, - transformer: &mut BodyTransformation, - items: &[MonoItem], - output: &Path, -) { - /// Convert MonoItem into a DefId. - /// Skip stuff that we cannot generate the MIR items. - fn get_instance(item: &MonoItem) -> Option { - match item { - // Exclude FnShims and others that cannot be dumped. - MonoItem::Fn(instance) => Some(*instance), - MonoItem::Static(def) => { - let instance: Instance = (*def).into(); - instance.has_body().then_some(instance) - } - MonoItem::GlobalAsm(_) => None, - } - } - - if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) { - // Create output buffer. - let out_file = File::create(output).unwrap(); - let mut writer = BufWriter::new(out_file); - - // For each def_id, dump their MIR - for instance in items.iter().filter_map(get_instance) { - writeln!(writer, "// Item: {} ({})", instance.name(), instance.mangled_name()).unwrap(); - let body = transformer.body(tcx, instance); - let _ = body.dump(&mut writer, &instance.name()); - } - } -} - /// Structure that represents the source location of a definition. /// TODO: Use `InternedString` once we move it out of the cprover_bindings. /// diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 279dcf8cc107..d2c9d50515c4 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -22,6 +22,7 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_middle::ty::{TyCtxt, VtblEntry}; +use rustc_session::config::OutputType; use rustc_smir::rustc_internal; use stable_mir::mir::alloc::{AllocId, GlobalAlloc}; use stable_mir::mir::mono::{Instance, InstanceKind, MonoItem, StaticDef}; @@ -32,6 +33,12 @@ use stable_mir::mir::{ use stable_mir::ty::{Allocation, ClosureKind, ConstantKind, RigidTy, Ty, TyKind}; use stable_mir::CrateItem; use stable_mir::{CrateDef, ItemKind}; +use std::fmt::{Display, Formatter}; +use std::{ + collections::{HashMap, HashSet}, + fs::File, + io::{BufWriter, Write}, +}; use crate::kani_middle::coercion; use crate::kani_middle::coercion::CoercionBase; @@ -42,7 +49,7 @@ pub fn collect_reachable_items( tcx: TyCtxt, transformer: &mut BodyTransformation, starting_points: &[MonoItem], -) -> Vec { +) -> (Vec, CallGraph) { // For each harness, collect items using the same collector. // I.e.: This will return any item that is reachable from one or more of the starting points. let mut collector = MonoItemsCollector::new(tcx, transformer); @@ -62,7 +69,7 @@ pub fn collect_reachable_items( // order of the errors and warnings is stable. let mut sorted_items: Vec<_> = collector.collected.into_iter().collect(); sorted_items.sort_by_cached_key(|item| to_fingerprint(tcx, item)); - sorted_items + (sorted_items, collector.call_graph) } /// Collect all (top-level) items in the crate that matches the given predicate. @@ -118,7 +125,24 @@ where } } } - roots + roots.into_iter().map(|root| root.item).collect() +} + +/// Reason for introducing an edge in the call graph. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +enum CollectionReason { + DirectCall, + IndirectCall, + VTableMethod, + Static, + StaticDrop, +} + +/// A destination of the edge in the call graph. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +struct CollectedItem { + item: MonoItem, + reason: CollectionReason, } struct MonoItemsCollector<'tcx, 'a> { @@ -130,8 +154,8 @@ struct MonoItemsCollector<'tcx, 'a> { collected: FxHashSet, /// Items enqueued for visiting. queue: Vec, - #[cfg(debug_assertions)] - call_graph: debug::CallGraph, + /// Call graph used for dataflow analysis. + call_graph: CallGraph, } impl<'tcx, 'a> MonoItemsCollector<'tcx, 'a> { @@ -140,8 +164,7 @@ impl<'tcx, 'a> MonoItemsCollector<'tcx, 'a> { tcx, collected: FxHashSet::default(), queue: vec![], - #[cfg(debug_assertions)] - call_graph: debug::CallGraph::default(), + call_graph: CallGraph::default(), transformer, } } @@ -167,17 +190,17 @@ impl<'tcx, 'a> MonoItemsCollector<'tcx, 'a> { vec![] } }; - #[cfg(debug_assertions)] self.call_graph.add_edges(to_visit, &next_items); - self.queue - .extend(next_items.into_iter().filter(|item| !self.collected.contains(item))); + self.queue.extend(next_items.into_iter().filter_map( + |CollectedItem { item, .. }| (!self.collected.contains(&item)).then_some(item), + )); } } } /// Visit a function and collect all mono-items reachable from its instructions. - fn visit_fn(&mut self, instance: Instance) -> Vec { + fn visit_fn(&mut self, instance: Instance) -> Vec { let _guard = debug_span!("visit_fn", function=?instance).entered(); let body = self.transformer.body(self.tcx, instance); let mut collector = @@ -187,19 +210,24 @@ impl<'tcx, 'a> MonoItemsCollector<'tcx, 'a> { } /// Visit a static object and collect drop / initialization functions. - fn visit_static(&mut self, def: StaticDef) -> Vec { + fn visit_static(&mut self, def: StaticDef) -> Vec { 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(instance.into()); + 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()); + next_items.extend( + collect_alloc_items(prov.0) + .into_iter() + .map(|item| CollectedItem { item, reason: CollectionReason::Static }), + ); } next_items @@ -213,7 +241,7 @@ impl<'tcx, 'a> MonoItemsCollector<'tcx, 'a> { struct MonoItemsFnCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, - collected: FxHashSet, + collected: FxHashSet, body: &'a Body, } @@ -251,7 +279,9 @@ impl<'a, 'tcx> MonoItemsFnCollector<'a, 'tcx> { } }); trace!(methods=?methods.clone().collect::>(), "collect_vtable_methods"); - self.collected.extend(methods); + self.collected.extend( + methods.map(|item| CollectedItem { item, reason: CollectionReason::VTableMethod }), + ); } // Add the destructor for the concrete type. @@ -282,7 +312,12 @@ impl<'a, 'tcx> MonoItemsFnCollector<'a, 'tcx> { }; if should_collect && should_codegen_locally(&instance) { trace!(?instance, "collect_instance"); - self.collected.insert(instance.into()); + let reason = if is_direct_call { + CollectionReason::DirectCall + } else { + CollectionReason::IndirectCall + }; + self.collected.insert(CollectedItem { item: instance.into(), reason }); } } @@ -290,7 +325,11 @@ impl<'a, 'tcx> MonoItemsFnCollector<'a, 'tcx> { fn collect_allocation(&mut self, alloc: &Allocation) { debug!(?alloc, "collect_allocation"); for (_, id) in &alloc.provenance.ptrs { - self.collected.extend(collect_alloc_items(id.0).into_iter()) + self.collected.extend( + collect_alloc_items(id.0) + .into_iter() + .map(|item| CollectedItem { item, reason: CollectionReason::Static }), + ) } } } @@ -366,7 +405,8 @@ impl<'a, 'tcx> MirVisitor for MonoItemsFnCollector<'a, 'tcx> { } Rvalue::ThreadLocalRef(item) => { trace!(?item, "visit_rvalue thread_local"); - self.collected.insert(MonoItem::Static(StaticDef::try_from(item).unwrap())); + let item = MonoItem::Static(StaticDef::try_from(item).unwrap()); + self.collected.insert(CollectedItem { item, reason: CollectionReason::Static }); } _ => { /* not interesting */ } } @@ -485,128 +525,150 @@ fn collect_alloc_items(alloc_id: AllocId) -> Vec { items } -#[cfg(debug_assertions)] -#[allow(dead_code)] -mod debug { - - use std::fmt::{Display, Formatter}; - use std::{ - collections::{HashMap, HashSet}, - fs::File, - io::{BufWriter, Write}, - }; - - use rustc_session::config::OutputType; - - use super::*; +/// Call graph with edges annotated with the reason why they were added to the graph. +#[derive(Debug, Default)] +pub struct CallGraph { + /// Nodes of the graph. + nodes: HashSet, + /// Edges of the graph. + edges: HashMap>, + /// Since the graph is directed, we also store back edges. + back_edges: HashMap>, +} - #[derive(Debug, Default)] - pub struct CallGraph { - // Nodes of the graph. - nodes: HashSet, - edges: HashMap>, - back_edges: HashMap>, +/// Newtype around MonoItem. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +struct Node(pub MonoItem); + +/// Newtype around CollectedItem. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +struct CollectedNode(pub CollectedItem); + +impl CallGraph { + /// Add a new node into a graph. + fn add_node(&mut self, item: MonoItem) { + let node = Node(item); + self.nodes.insert(node.clone()); + self.edges.entry(node.clone()).or_default(); + self.back_edges.entry(node).or_default(); } - #[derive(Clone, Debug, Eq, PartialEq, Hash)] - struct Node(pub MonoItem); - - impl CallGraph { - pub fn add_node(&mut self, item: MonoItem) { - let node = Node(item); - self.nodes.insert(node.clone()); - self.edges.entry(node.clone()).or_default(); - self.back_edges.entry(node).or_default(); - } + /// Add a new edge "from" -> "to". + fn add_edge(&mut self, from: MonoItem, to: MonoItem, collection_reason: CollectionReason) { + let from_node = Node(from.clone()); + let to_node = Node(to.clone()); + self.add_node(from.clone()); + self.add_node(to.clone()); + self.edges + .get_mut(&from_node) + .unwrap() + .push(CollectedNode(CollectedItem { item: to, reason: collection_reason })); + self.back_edges + .get_mut(&to_node) + .unwrap() + .push(CollectedNode(CollectedItem { item: from, reason: collection_reason })); + } - /// Add a new edge "from" -> "to". - pub fn add_edge(&mut self, from: MonoItem, to: MonoItem) { - let from_node = Node(from.clone()); - let to_node = Node(to.clone()); - self.add_node(from); - self.add_node(to); - self.edges.get_mut(&from_node).unwrap().push(to_node.clone()); - self.back_edges.get_mut(&to_node).unwrap().push(from_node); + /// Add multiple new edges for the "from" node. + fn add_edges(&mut self, from: MonoItem, to: &[CollectedItem]) { + self.add_node(from.clone()); + for CollectedItem { item, reason } in to { + self.add_edge(from.clone(), item.clone(), *reason); } + } - /// Add multiple new edges for the "from" node. - pub fn add_edges(&mut self, from: MonoItem, to: &[MonoItem]) { - self.add_node(from.clone()); - for item in to { - self.add_edge(from.clone(), item.clone()); + /// Print the graph in DOT format to a file. + /// See for more information. + fn dump_dot(&self, tcx: TyCtxt) -> std::io::Result<()> { + if let Ok(target) = std::env::var("KANI_REACH_DEBUG") { + debug!(?target, "dump_dot"); + let outputs = tcx.output_filenames(()); + let base_path = outputs.path(OutputType::Metadata); + let path = base_path.as_path().with_extension("dot"); + let out_file = File::create(path)?; + let mut writer = BufWriter::new(out_file); + writeln!(writer, "digraph ReachabilityGraph {{")?; + if target.is_empty() { + self.dump_all(&mut writer)?; + } else { + // Only dump nodes that led the reachability analysis to the target node. + self.dump_reason(&mut writer, &target)?; } + writeln!(writer, "}}")?; } - /// Print the graph in DOT format to a file. - /// See for more information. - pub fn dump_dot(&self, tcx: TyCtxt) -> std::io::Result<()> { - if let Ok(target) = std::env::var("KANI_REACH_DEBUG") { - debug!(?target, "dump_dot"); - let outputs = tcx.output_filenames(()); - let base_path = outputs.path(OutputType::Metadata); - let path = base_path.as_path().with_extension("dot"); - let out_file = File::create(path)?; - let mut writer = BufWriter::new(out_file); - writeln!(writer, "digraph ReachabilityGraph {{")?; - if target.is_empty() { - self.dump_all(&mut writer)?; - } else { - // Only dump nodes that led the reachability analysis to the target node. - self.dump_reason(&mut writer, &target)?; - } - writeln!(writer, "}}")?; - } + Ok(()) + } - Ok(()) + /// Write all notes to the given writer. + fn dump_all(&self, writer: &mut W) -> std::io::Result<()> { + tracing::info!(nodes=?self.nodes.len(), edges=?self.edges.len(), "dump_all"); + for node in &self.nodes { + writeln!(writer, r#""{node}""#)?; + for succ in self.edges.get(node).unwrap() { + let reason = succ.0.reason; + writeln!(writer, r#""{node}" -> "{succ}" [label={reason:?}] "#)?; + } } + Ok(()) + } - /// Write all notes to the given writer. - fn dump_all(&self, writer: &mut W) -> std::io::Result<()> { - tracing::info!(nodes=?self.nodes.len(), edges=?self.edges.len(), "dump_all"); - for node in &self.nodes { - writeln!(writer, r#""{node}""#)?; - for succ in self.edges.get(node).unwrap() { - writeln!(writer, r#""{node}" -> "{succ}" "#)?; - } + /// Write all notes that may have led to the discovery of the given target. + fn dump_reason(&self, writer: &mut W, target: &str) -> std::io::Result<()> { + let mut queue: Vec = + self.nodes.iter().filter(|item| item.to_string().contains(target)).cloned().collect(); + let mut visited: HashSet = HashSet::default(); + tracing::info!(target=?queue, nodes=?self.nodes.len(), edges=?self.edges.len(), "dump_reason"); + while let Some(to_visit) = queue.pop() { + if !visited.contains(&to_visit) { + visited.insert(to_visit.clone()); + queue.extend( + self.back_edges + .get(&to_visit) + .unwrap() + .iter() + .map(|item| Node::from(item.clone())), + ); } - Ok(()) } - /// Write all notes that may have led to the discovery of the given target. - fn dump_reason(&self, writer: &mut W, target: &str) -> std::io::Result<()> { - let mut queue = self - .nodes - .iter() - .filter(|item| item.to_string().contains(target)) - .collect::>(); - let mut visited: HashSet<&Node> = HashSet::default(); - tracing::info!(target=?queue, nodes=?self.nodes.len(), edges=?self.edges.len(), "dump_reason"); - while let Some(to_visit) = queue.pop() { - if !visited.contains(to_visit) { - visited.insert(to_visit); - queue.extend(self.back_edges.get(to_visit).unwrap()); - } + for node in &visited { + writeln!(writer, r#""{node}""#)?; + let edges = self.edges.get(node).unwrap(); + for succ in edges.iter().filter(|item| { + let node = Node::from((*item).clone()); + visited.contains(&node) + }) { + let reason = succ.0.reason; + writeln!(writer, r#""{node}" -> "{succ}" [label={reason:?}] "#)?; } + } + Ok(()) + } +} - for node in &visited { - writeln!(writer, r#""{node}""#)?; - for succ in - self.edges.get(node).unwrap().iter().filter(|item| visited.contains(item)) - { - writeln!(writer, r#""{node}" -> "{succ}" "#)?; - } - } - Ok(()) +impl Display for Node { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match &self.0 { + MonoItem::Fn(instance) => write!(f, "{}", instance.name()), + MonoItem::Static(def) => write!(f, "{}", def.name()), + MonoItem::GlobalAsm(asm) => write!(f, "{asm:?}"), } } +} - impl Display for Node { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match &self.0 { - MonoItem::Fn(instance) => write!(f, "{}", instance.name()), - MonoItem::Static(def) => write!(f, "{}", def.name()), - MonoItem::GlobalAsm(asm) => write!(f, "{asm:?}"), - } +impl Display for CollectedNode { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match &self.0.item { + MonoItem::Fn(instance) => write!(f, "{}", instance.name()), + MonoItem::Static(def) => write!(f, "{}", def.name()), + MonoItem::GlobalAsm(asm) => write!(f, "{asm:?}"), } } } + +impl From for Node { + fn from(value: CollectedNode) -> Self { + Node(value.0.item) + } +} diff --git a/kani-compiler/src/kani_middle/transform/dump_mir_pass.rs b/kani-compiler/src/kani_middle/transform/dump_mir_pass.rs new file mode 100644 index 000000000000..9393ec0d88c9 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/dump_mir_pass.rs @@ -0,0 +1,69 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Global transformation pass, which does not modify bodies but dumps MIR whenever the appropriate debug flag is passed. + +use crate::kani_middle::reachability::CallGraph; +use crate::kani_middle::transform::GlobalPass; +use crate::kani_queries::QueryDb; +use kani_metadata::ArtifactType; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::OutputType; +use stable_mir::mir::mono::{Instance, MonoItem}; +use std::fs::File; +use std::io::BufWriter; +use std::io::Write; + +use super::BodyTransformation; + +/// Dump all MIR bodies. +#[derive(Debug)] +pub struct DumpMirPass { + enabled: bool, +} + +impl DumpMirPass { + pub fn new(tcx: TyCtxt) -> Self { + Self { enabled: tcx.sess.opts.output_types.contains_key(&OutputType::Mir) } + } +} + +impl GlobalPass for DumpMirPass { + fn is_enabled(&self, _query_db: &QueryDb) -> bool { + self.enabled + } + + fn transform( + &mut self, + tcx: TyCtxt, + _call_graph: &CallGraph, + starting_items: &[MonoItem], + instances: Vec, + transformer: &mut BodyTransformation, + ) { + // Create output buffer. + let file_path = { + let base_path = tcx.output_filenames(()).path(OutputType::Object); + let base_name = base_path.as_path(); + let entry_point = (starting_items.len() == 1).then_some(starting_items[0].clone()); + // If there is a single entry point, use it as a file name. + if let Some(MonoItem::Fn(starting_instance)) = entry_point { + let mangled_name = starting_instance.mangled_name(); + let file_stem = + format!("{}_{mangled_name}", base_name.file_stem().unwrap().to_str().unwrap()); + base_name.with_file_name(file_stem).with_extension(ArtifactType::SymTabGoto) + } else { + // Otherwise, use the object output path from the compiler. + base_name.with_extension(ArtifactType::SymTabGoto) + } + }; + let out_file = File::create(file_path.with_extension("kani.mir")).unwrap(); + let mut writer = BufWriter::new(out_file); + + // For each def_id, dump their MIR. + for instance in instances.iter() { + writeln!(writer, "// Item: {} ({})", instance.name(), instance.mangled_name()).unwrap(); + let _ = transformer.body(tcx, *instance).dump(&mut writer, &instance.name()); + } + } +} diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index 26a95978fcaf..5b497b09619d 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -17,6 +17,7 @@ //! For all instrumentation passes, always use exhaustive matches to ensure soundness in case a new //! case is added. use crate::kani_middle::codegen_units::CodegenUnit; +use crate::kani_middle::reachability::CallGraph; use crate::kani_middle::transform::body::CheckType; use crate::kani_middle::transform::check_uninit::UninitPass; use crate::kani_middle::transform::check_values::ValidValuePass; @@ -24,8 +25,9 @@ use crate::kani_middle::transform::contracts::AnyModifiesPass; use crate::kani_middle::transform::kani_intrinsics::IntrinsicGeneratorPass; use crate::kani_middle::transform::stubs::{ExternFnStubPass, FnStubPass}; use crate::kani_queries::QueryDb; +use dump_mir_pass::DumpMirPass; use rustc_middle::ty::TyCtxt; -use stable_mir::mir::mono::Instance; +use stable_mir::mir::mono::{Instance, MonoItem}; use stable_mir::mir::Body; use std::collections::HashMap; use std::fmt::Debug; @@ -34,6 +36,7 @@ pub(crate) mod body; mod check_uninit; mod check_values; mod contracts; +mod dump_mir_pass; mod kani_intrinsics; mod stubs; @@ -90,7 +93,8 @@ impl BodyTransformation { transformer } - /// Retrieve the body of an instance. + /// Retrieve the body of an instance. This does not apply global passes, but will retrieve the + /// body after global passes running if they were previously applied. /// /// Note that this assumes that the instance does have a body since existing consumers already /// assume that. Use `instance.has_body()` to check if an instance has a body. @@ -152,6 +156,23 @@ pub(crate) trait TransformPass: Debug { fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body); } +/// A trait to represent transformation passes that operate on the whole codegen unit. +pub(crate) trait GlobalPass: Debug { + fn is_enabled(&self, query_db: &QueryDb) -> bool + where + Self: Sized; + + /// Run a transformation pass on the whole codegen unit. + fn transform( + &mut self, + tcx: TyCtxt, + call_graph: &CallGraph, + starting_items: &[MonoItem], + instances: Vec, + transformer: &mut BodyTransformation, + ); +} + /// The transformation result. /// We currently only cache the body of functions that were instrumented. #[derive(Clone, Debug)] @@ -159,3 +180,37 @@ enum TransformationResult { Modified(Body), NotModified, } + +pub struct GlobalPasses { + /// The passes that operate on the whole codegen unit, they run after all previous passes are + /// done. + global_passes: Vec>, +} + +impl GlobalPasses { + pub fn new(queries: &QueryDb, tcx: TyCtxt) -> Self { + let mut global_passes = GlobalPasses { global_passes: vec![] }; + global_passes.add_global_pass(queries, DumpMirPass::new(tcx)); + global_passes + } + + fn add_global_pass(&mut self, query_db: &QueryDb, pass: P) { + if pass.is_enabled(&query_db) { + self.global_passes.push(Box::new(pass)) + } + } + + /// Run all global passes and store the results in a cache that can later be queried by `body`. + pub fn run_global_passes( + &mut self, + transformer: &mut BodyTransformation, + tcx: TyCtxt, + starting_items: &[MonoItem], + instances: Vec, + call_graph: CallGraph, + ) { + for global_pass in self.global_passes.iter_mut() { + global_pass.transform(tcx, &call_graph, starting_items, instances.clone(), transformer); + } + } +} From 9f230baeeaf59ffdf44cdf73d627e9f67d4606e1 Mon Sep 17 00:00:00 2001 From: Qinheping Hu Date: Mon, 22 Jul 2024 03:14:49 -0500 Subject: [PATCH 194/225] Upgrade toolchain to 2024-07-19 (#3364) Resolves #3358 Related upstream commits: - https://github.com/rust-lang/rust/commit/21dc49c587 ptr::metadata: avoid references to extern types --- .../codegen_cprover_gotoc/codegen/rvalue.rs | 28 +++++++++++++++---- rust-toolchain.toml | 2 +- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index e01ffbe462ba..f84708b9bdd5 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -703,8 +703,10 @@ impl<'tcx> GotocCtx<'tcx> { if meta.typ().sizeof(&self.symbol_table) == 0 { data_cast } else { - let vtable_expr = - meta.member("_vtable_ptr", &self.symbol_table).cast_to( + let vtable_expr = meta + .member("_vtable_ptr", &self.symbol_table) + .member("pointer", &self.symbol_table) + .cast_to( typ.lookup_field_type("vtable", &self.symbol_table).unwrap(), ); dynamic_fat_ptr(typ, data_cast, vtable_expr, &self.symbol_table) @@ -835,13 +837,29 @@ impl<'tcx> GotocCtx<'tcx> { dst_goto_typ.lookup_components(&self.symbol_table).unwrap(); assert_eq!(dst_components.len(), 2); assert_eq!(dst_components[0].name(), "_vtable_ptr"); - assert!(dst_components[0].typ().is_pointer()); + assert!(dst_components[0].typ().is_struct_like()); assert_eq!(dst_components[1].name(), "_phantom"); self.assert_is_rust_phantom_data_like(&dst_components[1].typ()); + // accessing pointer type of _vtable_ptr, which is wrapped in NonNull + let vtable_ptr_typ = dst_goto_typ + .lookup_field_type("_vtable_ptr", &self.symbol_table) + .unwrap() + .lookup_components(&self.symbol_table) + .unwrap()[0] + .typ(); Expr::struct_expr( - dst_goto_typ, + dst_goto_typ.clone(), btree_string_map![ - ("_vtable_ptr", vtable_expr.cast_to(dst_components[0].typ())), + ( + "_vtable_ptr", + Expr::struct_expr_from_values( + dst_goto_typ + .lookup_field_type("_vtable_ptr", &self.symbol_table) + .unwrap(), + vec![vtable_expr.clone().cast_to(vtable_ptr_typ)], + &self.symbol_table + ) + ), ( "_phantom", Expr::struct_expr( diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c04db242fd5b..adc62ae8e8db 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-07-18" +channel = "nightly-2024-07-19" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From bf1126a7b2db230fc7497f10d87f0e438a2c95b7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 12:26:53 +0200 Subject: [PATCH 195/225] Automatic toolchain upgrade to nightly-2024-07-20 (#3366) Update Rust toolchain from nightly-2024-07-19 to nightly-2024-07-20 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/5affbb17153bc69a9d5d8d2faa4e399a014a211e up to https://github.com/rust-lang/rust/commit/9057c3ffec44926d5e149dc13ff3ce1613b69cce. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index adc62ae8e8db..a7a9ca6ca0e5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-07-19" +channel = "nightly-2024-07-20" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From d6806795e0f501090787b0860126870dd79f7157 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:05:55 +0000 Subject: [PATCH 196/225] Automatic cargo update to 2024-07-22 (#3365) Dependency upgrade resulting from `cargo update`. --- Cargo.lock | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe420de27a88..0fd31f73278b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,7 +169,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -482,7 +482,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -801,9 +801,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ "bitflags", ] @@ -924,7 +924,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1030,7 +1030,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1045,9 +1045,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.71" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -1068,22 +1068,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1098,9 +1098,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28" dependencies = [ "serde", "serde_spanned", @@ -1119,9 +1119,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.15" +version = "0.22.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" +checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" dependencies = [ "indexmap", "serde", @@ -1149,7 +1149,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1384,9 +1384,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +checksum = "374ec40a2d767a3c1b4972d9475ecd557356637be906f2cb3f7fe17a6eb5e22f" dependencies = [ "memchr", ] @@ -1414,5 +1414,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] From 59371f358d1fff009846dc9554a7f9c72415d93e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 10:22:01 -0700 Subject: [PATCH 197/225] Bump tests/perf/s2n-quic from `71f8d9f` to `f568f26` (#3368) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `71f8d9f` to `f568f26`.
Commits
  • f568f26 feat(s2n-quic-dc): return from ConfirmComplete when no dc version negotiated ...
  • 518c0a9 fix(s2n-quic-dc): reorder wire version to preserve wire invariants (#2277)
  • 59564ff feat(s2n-quic-dc): add ClientConfirm subscriber (#2274)
  • fcd9a1b feat(s2n-quic-dc): import 7/18 version (#2275)
  • 2c95dd9 feat(s2n-quic-dc): Periodically re-handshake existing path secrets (#2276)
  • d55d258 feat(s2n-quic-transport): Support de-duplicating concurrent connections to th...
  • b3eb094 fix(s2n-quic): implement Debug and Default for the disabled event Subscriber ...
  • 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 71f8d9f5aafb..f568f269ee5c 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 71f8d9f5aafbf59f31ad85eeb7b4b67a7564a685 +Subproject commit f568f269ee5c9896f4936089c26dfbb3f87f4dab From 5d6bf6974055b53c1878ff1718c98c0ab4f98aed Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 18:16:24 +0000 Subject: [PATCH 198/225] Automatic toolchain upgrade to nightly-2024-07-21 (#3367) Update Rust toolchain from nightly-2024-07-20 to nightly-2024-07-21 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/9057c3ffec44926d5e149dc13ff3ce1613b69cce up to https://github.com/rust-lang/rust/commit/5069856495870486134dd2ca0b0e2516308c5c2a. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index a7a9ca6ca0e5..27c29bb820e2 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-07-20" +channel = "nightly-2024-07-21" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 7ad4d1cc3109defea1131876f427f13599243ed8 Mon Sep 17 00:00:00 2001 From: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Date: Mon, 22 Jul 2024 16:18:06 -0400 Subject: [PATCH 199/225] Enable an `#[safety_constraint(...)]` attribute helper for the `Arbitrary` and `Invariant` macros (#3283) This PR enables an `#[safety_constraint(...)]` attribute helper for the `#[derive(Arbitrary)]` and `#[derive(Invariant)]` macro. For the `Invariant` derive macro, this allows users to derive more sophisticated invariants for their data types by annotating individual named fields with the `#[safety_constraint()]` attribute, where `` represents the predicate to be evaluated for the corresponding field. In addition, the implementation always checks `#field.is_safe()` for each field. For example, let's say we are working with the `Point` type from #3250 ```rs #[derive(kani::Invariant)] struct Point { x: X, y: Y, } ``` and we need to extend it to only allow positive values for both `x` and `y`. With the `[safety_constraint(...)]` attribute, we can achieve this without explicitly writing the `impl Invariant for ...` as follows: ```rs #[derive(kani::Invariant)] struct PositivePoint { #[safety_constraint(*x >= 0)] x: i32, #[safety_constraint(*y >= 0)] y: i32, } ``` For the `Arbitrary` derive macro, this allows users to derive more sophisticated `kani::any()` generators that respect the specified invariants. In other words, the `kani::any()` will assume any invariants specified through the `#[safety_constraint(...)]` attribute helper. Going back to the `PositivePoint` example, we'd expect this harness to be successful: ```rs #[kani::proof] fn check_invariant_helper_ok() { let pos_point: PositivePoint = kani::any(); assert!(pos_point.x >= 0); assert!(pos_point.y >= 0); } ``` The PR includes tests to ensure it's working as expected, in addition to UI tests checking for cases where the arguments provided to the macro are incorrect. Happy to add any other cases that you feel are missing. Related #3095 --- library/kani_macros/src/derive.rs | 202 ++++++++++++++++-- library/kani_macros/src/lib.rs | 112 +++++++++- .../safety_constraint_helper/expected | 17 ++ .../safety_constraint_helper.rs | 31 +++ .../attrs_cfg_guard/attrs_cfg_guard.rs | 24 +++ .../derive-invariant/attrs_cfg_guard/expected | 9 + .../attrs_mixed/attrs_mixed.rs | 24 +++ .../derive-invariant/attrs_mixed/expected | 9 + .../safety_constraint_helper/expected | 13 ++ .../safety_constraint_helper.rs | 43 ++++ .../safety_constraint_helper_funs/expected | 13 ++ .../safety_constraint_helper_funs.rs | 48 +++++ .../expected | 0 .../safety_invariant_fail.rs} | 0 .../safety_invariant_fail_mut/expected | 11 + .../safety_invariant_fail_mut.rs | 28 +++ .../ui/derive-invariant/helper-empty/expected | 6 + .../helper-empty/helper-empty.rs | 17 ++ .../derive-invariant/helper-no-expr/expected | 6 + .../helper-no-expr/helper-no-expr.rs | 17 ++ .../helper-side-effect/expected | 9 + .../helper-side-effect/helper-side-effect.rs | 22 ++ .../helper-wrong-expr/expected | 9 + .../helper-wrong-expr/helper-wrong-expr.rs | 18 ++ 24 files changed, 668 insertions(+), 20 deletions(-) create mode 100644 tests/expected/derive-arbitrary/safety_constraint_helper/expected create mode 100644 tests/expected/derive-arbitrary/safety_constraint_helper/safety_constraint_helper.rs create mode 100644 tests/expected/derive-invariant/attrs_cfg_guard/attrs_cfg_guard.rs create mode 100644 tests/expected/derive-invariant/attrs_cfg_guard/expected create mode 100644 tests/expected/derive-invariant/attrs_mixed/attrs_mixed.rs create mode 100644 tests/expected/derive-invariant/attrs_mixed/expected create mode 100644 tests/expected/derive-invariant/safety_constraint_helper/expected create mode 100644 tests/expected/derive-invariant/safety_constraint_helper/safety_constraint_helper.rs create mode 100644 tests/expected/derive-invariant/safety_constraint_helper_funs/expected create mode 100644 tests/expected/derive-invariant/safety_constraint_helper_funs/safety_constraint_helper_funs.rs rename tests/expected/derive-invariant/{invariant_fail => safety_invariant_fail}/expected (100%) rename tests/expected/derive-invariant/{invariant_fail/invariant_fail.rs => safety_invariant_fail/safety_invariant_fail.rs} (100%) create mode 100644 tests/expected/derive-invariant/safety_invariant_fail_mut/expected create mode 100644 tests/expected/derive-invariant/safety_invariant_fail_mut/safety_invariant_fail_mut.rs create mode 100644 tests/ui/derive-invariant/helper-empty/expected create mode 100644 tests/ui/derive-invariant/helper-empty/helper-empty.rs create mode 100644 tests/ui/derive-invariant/helper-no-expr/expected create mode 100644 tests/ui/derive-invariant/helper-no-expr/helper-no-expr.rs create mode 100644 tests/ui/derive-invariant/helper-side-effect/expected create mode 100644 tests/ui/derive-invariant/helper-side-effect/helper-side-effect.rs create mode 100644 tests/ui/derive-invariant/helper-wrong-expr/expected create mode 100644 tests/ui/derive-invariant/helper-wrong-expr/helper-wrong-expr.rs diff --git a/library/kani_macros/src/derive.rs b/library/kani_macros/src/derive.rs index 4e99590fc6a3..a7aaa8ae334e 100644 --- a/library/kani_macros/src/derive.rs +++ b/library/kani_macros/src/derive.rs @@ -28,11 +28,30 @@ pub fn expand_derive_arbitrary(item: proc_macro::TokenStream) -> proc_macro::Tok let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let body = fn_any_body(&item_name, &derive_item.data); - let expanded = quote! { - // The generated implementation. - impl #impl_generics kani::Arbitrary for #item_name #ty_generics #where_clause { - fn any() -> Self { - #body + + // Get the safety constraints (if any) to produce type-safe values + let safety_conds_opt = safety_conds(&item_name, &derive_item.data); + + let expanded = if let Some(safety_cond) = safety_conds_opt { + let field_refs = field_refs(&item_name, &derive_item.data); + quote! { + // The generated implementation. + impl #impl_generics kani::Arbitrary for #item_name #ty_generics #where_clause { + fn any() -> Self { + let obj = #body; + #field_refs + kani::assume(#safety_cond); + obj + } + } + } + } else { + quote! { + // The generated implementation. + impl #impl_generics kani::Arbitrary for #item_name #ty_generics #where_clause { + fn any() -> Self { + #body + } } } }; @@ -75,6 +94,103 @@ fn fn_any_body(ident: &Ident, data: &Data) -> TokenStream { } } +/// Parse the condition expressions in `#[safety_constraint()]` attached to struct +/// fields and, it at least one was found, generate a conjunction to be assumed. +/// +/// For example, if we're deriving implementations for the struct +/// ``` +/// #[derive(Arbitrary)] +/// #[derive(Invariant)] +/// struct PositivePoint { +/// #[safety_constraint(*x >= 0)] +/// x: i32, +/// #[safety_constraint(*y >= 0)] +/// y: i32, +/// } +/// ``` +/// this function will generate the `TokenStream` +/// ``` +/// *x >= 0 && *y >= 0 +/// ``` +/// which can be passed to `kani::assume` to constrain the values generated +/// through the `Arbitrary` impl so that they are type-safe by construction. +fn safety_conds(ident: &Ident, data: &Data) -> Option { + match data { + Data::Struct(struct_data) => safety_conds_inner(ident, &struct_data.fields), + Data::Enum(_) => None, + Data::Union(_) => None, + } +} + +/// Generates an expression resulting from the conjunction of conditions +/// specified as safety constraints for each field. See `safety_conds` for more details. +fn safety_conds_inner(ident: &Ident, fields: &Fields) -> Option { + match fields { + Fields::Named(ref fields) => { + let conds: Vec = + fields.named.iter().filter_map(|field| parse_safety_expr(ident, field)).collect(); + if !conds.is_empty() { Some(quote! { #(#conds)&&* }) } else { None } + } + Fields::Unnamed(_) => None, + Fields::Unit => None, + } +} + +/// Generates the sequence of expressions to initialize the variables used as +/// references to the struct fields. +/// +/// For example, if we're deriving implementations for the struct +/// ``` +/// #[derive(Arbitrary)] +/// #[derive(Invariant)] +/// struct PositivePoint { +/// #[safety_constraint(*x >= 0)] +/// x: i32, +/// #[safety_constraint(*y >= 0)] +/// y: i32, +/// } +/// ``` +/// this function will generate the `TokenStream` +/// ``` +/// let x = &obj.x; +/// let y = &obj.y; +/// ``` +/// which allows us to refer to the struct fields without using `self`. +/// Note that the actual stream is generated in the `field_refs_inner` function. +fn field_refs(ident: &Ident, data: &Data) -> TokenStream { + match data { + Data::Struct(struct_data) => field_refs_inner(ident, &struct_data.fields), + Data::Enum(_) => unreachable!(), + Data::Union(_) => unreachable!(), + } +} + +/// Generates the sequence of expressions to initialize the variables used as +/// references to the struct fields. See `field_refs` for more details. +fn field_refs_inner(_ident: &Ident, fields: &Fields) -> TokenStream { + match fields { + Fields::Named(ref fields) => { + let field_refs: Vec = fields + .named + .iter() + .map(|field| { + let name = &field.ident; + quote_spanned! {field.span()=> + let #name = &obj.#name; + } + }) + .collect(); + if !field_refs.is_empty() { + quote! { #( #field_refs )* } + } else { + quote! {} + } + } + Fields::Unnamed(_) => quote! {}, + Fields::Unit => quote! {}, + } +} + /// Generate an item initialization where an item can be a struct or a variant. /// For named fields, this will generate: `Item { field1: kani::any(), field2: kani::any(), .. }` /// For unnamed fields, this will generate: `Item (kani::any(), kani::any(), ..)` @@ -115,6 +231,42 @@ fn init_symbolic_item(ident: &Ident, fields: &Fields) -> TokenStream { } } +/// Extract, parse and return the expression `cond` (i.e., `Some(cond)`) in the +/// `#[safety_constraint()]` attribute helper associated with a given field. +/// Return `None` if the attribute isn't specified. +fn parse_safety_expr(ident: &Ident, field: &syn::Field) -> Option { + let name = &field.ident; + let mut safety_helper_attr = None; + + // Keep the helper attribute if we find it + for attr in &field.attrs { + if attr.path().is_ident("safety_constraint") { + safety_helper_attr = Some(attr); + } + } + + // Parse the arguments in the `#[safety_constraint(...)]` attribute + if let Some(attr) = safety_helper_attr { + let expr_args: Result = attr.parse_args(); + + // Check if there was an error parsing the arguments + if let Err(err) = expr_args { + abort!(Span::call_site(), "Cannot derive impl for `{}`", ident; + note = attr.span() => + "safety constraint in field `{}` could not be parsed: {}", name.as_ref().unwrap().to_string(), err + ) + } + + // Return the expression associated to the safety constraint + let safety_expr = expr_args.unwrap(); + Some(quote_spanned! {field.span()=> + #safety_expr + }) + } else { + None + } +} + /// Generate the body of the function `any()` for enums. The cases are: /// 1. For zero-variants enumerations, this will encode a `panic!()` statement. /// 2. For one or more variants, the code will be something like: @@ -176,10 +328,14 @@ pub fn expand_derive_invariant(item: proc_macro::TokenStream) -> proc_macro::Tok let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let body = is_safe_body(&item_name, &derive_item.data); + let field_refs = field_refs(&item_name, &derive_item.data); + let expanded = quote! { // The generated implementation. impl #impl_generics kani::Invariant for #item_name #ty_generics #where_clause { fn is_safe(&self) -> bool { + let obj = self; + #field_refs #body } } @@ -199,7 +355,7 @@ fn add_trait_bound_invariant(mut generics: Generics) -> Generics { fn is_safe_body(ident: &Ident, data: &Data) -> TokenStream { match data { - Data::Struct(struct_data) => struct_safe_conjunction(ident, &struct_data.fields), + Data::Struct(struct_data) => struct_invariant_conjunction(ident, &struct_data.fields), Data::Enum(_) => { abort!(Span::call_site(), "Cannot derive `Invariant` for `{}` enum", ident; note = ident.span() => @@ -215,21 +371,35 @@ fn is_safe_body(ident: &Ident, data: &Data) -> TokenStream { } } -/// Generates an expression that is the conjunction of `is_safe` calls for each field in the struct. -fn struct_safe_conjunction(_ident: &Ident, fields: &Fields) -> TokenStream { +/// Generates an expression that is the conjunction of safety constraints for each field in the struct. +fn struct_invariant_conjunction(ident: &Ident, fields: &Fields) -> TokenStream { match fields { // Expands to the expression + // `true && && && ..` + // where `safety_condN` is + // - `self.fieldN.is_safe() && ` if a condition `` was + // specified through the `#[safety_constraint()]` helper attribute, or + // - `self.fieldN.is_safe()` otherwise + // + // Therefore, if `#[safety_constraint()]` isn't specified for any field, this expands to // `true && self.field1.is_safe() && self.field2.is_safe() && ..` Fields::Named(ref fields) => { - let safe_calls = fields.named.iter().map(|field| { - let name = &field.ident; - quote_spanned! {field.span()=> - self.#name.is_safe() - } - }); + let safety_conds: Vec = fields + .named + .iter() + .map(|field| { + let name = &field.ident; + let default_expr = quote_spanned! {field.span()=> + #name.is_safe() + }; + parse_safety_expr(ident, field) + .map(|expr| quote! { #expr && #default_expr}) + .unwrap_or(default_expr) + }) + .collect(); // An initial value is required for empty structs - safe_calls.fold(quote! { true }, |acc, call| { - quote! { #acc && #call } + safety_conds.iter().fold(quote! { true }, |acc, cond| { + quote! { #acc && #cond } }) } Fields::Unnamed(ref fields) => { diff --git a/library/kani_macros/src/lib.rs b/library/kani_macros/src/lib.rs index b10b8a74cdc5..4e3a8d6f9f5b 100644 --- a/library/kani_macros/src/lib.rs +++ b/library/kani_macros/src/lib.rs @@ -100,16 +100,120 @@ pub fn unstable_feature(attr: TokenStream, item: TokenStream) -> TokenStream { attr_impl::unstable(attr, item) } -/// Allow users to auto generate Arbitrary implementations by using `#[derive(Arbitrary)]` macro. +/// Allow users to auto generate `Arbitrary` implementations by using +/// `#[derive(Arbitrary)]` macro. +/// +/// When using `#[derive(Arbitrary)]` on a struct, the `#[safety_constraint()]` +/// attribute can be added to its fields to indicate a type safety invariant +/// condition ``. Since `kani::any()` is always expected to produce +/// type-safe values, **adding `#[safety_constraint(...)]` to any fields will further +/// constrain the objects generated with `kani::any()`**. +/// +/// For example, the `check_positive` harness in this code is expected to +/// pass: +/// +/// ```rs +/// #[derive(kani::Arbitrary)] +/// struct AlwaysPositive { +/// #[safety_constraint(*inner >= 0)] +/// inner: i32, +/// } +/// +/// #[kani::proof] +/// fn check_positive() { +/// let val: AlwaysPositive = kani::any(); +/// assert!(val.inner >= 0); +/// } +/// ``` +/// +/// Therefore, using the `#[safety_constraint(...)]` attribute can lead to vacuous +/// results when the values are over-constrained. For example, in this code +/// the `check_positive` harness will pass too: +/// +/// ```rs +/// #[derive(kani::Arbitrary)] +/// struct AlwaysPositive { +/// #[safety_constraint(*inner >= 0 && *inner < i32::MIN)] +/// inner: i32, +/// } +/// +/// #[kani::proof] +/// fn check_positive() { +/// let val: AlwaysPositive = kani::any(); +/// assert!(val.inner >= 0); +/// } +/// ``` +/// +/// Unfortunately, we made a mistake when specifying the condition because +/// `*inner >= 0 && *inner < i32::MIN` is equivalent to `false`. This results +/// in the relevant assertion being unreachable: +/// +/// ``` +/// Check 1: check_positive.assertion.1 +/// - Status: UNREACHABLE +/// - Description: "assertion failed: val.inner >= 0" +/// - Location: src/main.rs:22:5 in function check_positive +/// ``` +/// +/// As usual, we recommend users to defend against these behaviors by using +/// `kani::cover!(...)` checks and watching out for unreachable assertions in +/// their project's code. #[proc_macro_error] -#[proc_macro_derive(Arbitrary)] +#[proc_macro_derive(Arbitrary, attributes(safety_constraint))] pub fn derive_arbitrary(item: TokenStream) -> TokenStream { derive::expand_derive_arbitrary(item) } -/// Allow users to auto generate Invariant implementations by using `#[derive(Invariant)]` macro. +/// Allow users to auto generate `Invariant` implementations by using +/// `#[derive(Invariant)]` macro. +/// +/// When using `#[derive(Invariant)]` on a struct, the `#[safety_constraint()]` +/// attribute can be added to its fields to indicate a type safety invariant +/// condition ``. This will ensure that the gets additionally checked when +/// using the `is_safe()` method generated by the `#[derive(Invariant)]` macro. +/// +/// For example, the `check_positive` harness in this code is expected to +/// fail: +/// +/// ```rs +/// #[derive(kani::Invariant)] +/// struct AlwaysPositive { +/// #[safety_constraint(*inner >= 0)] +/// inner: i32, +/// } +/// +/// #[kani::proof] +/// fn check_positive() { +/// let val = AlwaysPositive { inner: -1 }; +/// assert!(val.is_safe()); +/// } +/// ``` +/// +/// This is not too surprising since the type safety invariant that we indicated +/// is not being taken into account when we create the `AlwaysPositive` object. +/// +/// As mentioned, the `is_safe()` methods generated by the +/// `#[derive(Invariant)]` macro check the corresponding `is_safe()` method for +/// each field in addition to any type safety invariants specified through the +/// `#[safety_constraint(...)]` attribute. +/// +/// For example, for the `AlwaysPositive` struct from above, we will generate +/// the following implementation: +/// +/// ```rs +/// impl kani::Invariant for AlwaysPositive { +/// fn is_safe(&self) -> bool { +/// let obj = self; +/// let inner = &obj.inner; +/// true && *inner >= 0 && inner.is_safe() +/// } +/// } +/// ``` +/// +/// Note: the assignments to `obj` and `inner` are made so that we can treat the +/// fields as if they were references. #[proc_macro_error] -#[proc_macro_derive(Invariant)] +#[proc_macro_derive(Invariant, attributes(safety_constraint))] pub fn derive_invariant(item: TokenStream) -> TokenStream { derive::expand_derive_invariant(item) } diff --git a/tests/expected/derive-arbitrary/safety_constraint_helper/expected b/tests/expected/derive-arbitrary/safety_constraint_helper/expected new file mode 100644 index 000000000000..f35f18084911 --- /dev/null +++ b/tests/expected/derive-arbitrary/safety_constraint_helper/expected @@ -0,0 +1,17 @@ +Check 1: check_invariant_helper_ok.assertion.1\ + - Status: SUCCESS\ + - Description: "assertion failed: pos_point.x >= 0" + +Check 2: check_invariant_helper_ok.assertion.2\ + - Status: SUCCESS\ + - Description: "assertion failed: pos_point.y >= 0" + +Check 1: check_invariant_helper_fail.assertion.1\ + - Status: FAILURE\ + - Description: "assertion failed: pos_point.x >= 0" + +Check 2: check_invariant_helper_fail.assertion.2\ + - Status: FAILURE\ + - Description: "assertion failed: pos_point.y >= 0" + +Complete - 2 successfully verified harnesses, 0 failures, 2 total. diff --git a/tests/expected/derive-arbitrary/safety_constraint_helper/safety_constraint_helper.rs b/tests/expected/derive-arbitrary/safety_constraint_helper/safety_constraint_helper.rs new file mode 100644 index 000000000000..5b608aa2a3c0 --- /dev/null +++ b/tests/expected/derive-arbitrary/safety_constraint_helper/safety_constraint_helper.rs @@ -0,0 +1,31 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that the `#[safety_constraint(...)]` attribute helper adds the conditions provided to +//! the attribute to the derived `Arbitrary` implementation. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +struct PositivePoint { + #[safety_constraint(*x >= 0)] + x: i32, + #[safety_constraint(*y >= 0)] + y: i32, +} + +#[kani::proof] +fn check_invariant_helper_ok() { + let pos_point: PositivePoint = kani::any(); + assert!(pos_point.x >= 0); + assert!(pos_point.y >= 0); +} + +#[kani::proof] +#[kani::should_panic] +fn check_invariant_helper_fail() { + let pos_point: PositivePoint = PositivePoint { x: kani::any(), y: kani::any() }; + assert!(pos_point.x >= 0); + assert!(pos_point.y >= 0); +} diff --git a/tests/expected/derive-invariant/attrs_cfg_guard/attrs_cfg_guard.rs b/tests/expected/derive-invariant/attrs_cfg_guard/attrs_cfg_guard.rs new file mode 100644 index 000000000000..546695bf3731 --- /dev/null +++ b/tests/expected/derive-invariant/attrs_cfg_guard/attrs_cfg_guard.rs @@ -0,0 +1,24 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that the `#[safety_constraint(...)]` attribute helper is picked up +//! when it's used with `cfg_attr(kani, ...)]`. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct PositivePoint { + #[cfg_attr(kani, safety_constraint(*x >= 0))] + x: i32, + #[cfg_attr(kani, safety_constraint(*y >= 0))] + y: i32, +} + +#[kani::proof] +fn check_safety_constraint_cfg() { + let pos_point: PositivePoint = kani::any(); + assert!(pos_point.x >= 0); + assert!(pos_point.y >= 0); +} diff --git a/tests/expected/derive-invariant/attrs_cfg_guard/expected b/tests/expected/derive-invariant/attrs_cfg_guard/expected new file mode 100644 index 000000000000..edb3e256975d --- /dev/null +++ b/tests/expected/derive-invariant/attrs_cfg_guard/expected @@ -0,0 +1,9 @@ +Check 1: check_safety_constraint_cfg.assertion.1\ + - Status: SUCCESS\ + - Description: "assertion failed: pos_point.x >= 0" + +Check 2: check_safety_constraint_cfg.assertion.2\ + - Status: SUCCESS\ + - Description: "assertion failed: pos_point.y >= 0" + +Complete - 1 successfully verified harnesses, 0 failures, 1 total. diff --git a/tests/expected/derive-invariant/attrs_mixed/attrs_mixed.rs b/tests/expected/derive-invariant/attrs_mixed/attrs_mixed.rs new file mode 100644 index 000000000000..ff58ff846c67 --- /dev/null +++ b/tests/expected/derive-invariant/attrs_mixed/attrs_mixed.rs @@ -0,0 +1,24 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that in the `#[safety_constraint(...)]` attribute helper it is +//! possible to refer to other struct fields, not just the one associated with +//! the attribute. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct PositivePoint { + #[safety_constraint(*x >= 0 && *y >= 0)] + x: i32, + y: i32, +} + +#[kani::proof] +fn check_safety_constraint_cfg() { + let pos_point: PositivePoint = kani::any(); + assert!(pos_point.x >= 0); + assert!(pos_point.y >= 0); +} diff --git a/tests/expected/derive-invariant/attrs_mixed/expected b/tests/expected/derive-invariant/attrs_mixed/expected new file mode 100644 index 000000000000..edb3e256975d --- /dev/null +++ b/tests/expected/derive-invariant/attrs_mixed/expected @@ -0,0 +1,9 @@ +Check 1: check_safety_constraint_cfg.assertion.1\ + - Status: SUCCESS\ + - Description: "assertion failed: pos_point.x >= 0" + +Check 2: check_safety_constraint_cfg.assertion.2\ + - Status: SUCCESS\ + - Description: "assertion failed: pos_point.y >= 0" + +Complete - 1 successfully verified harnesses, 0 failures, 1 total. diff --git a/tests/expected/derive-invariant/safety_constraint_helper/expected b/tests/expected/derive-invariant/safety_constraint_helper/expected new file mode 100644 index 000000000000..31b6de54c647 --- /dev/null +++ b/tests/expected/derive-invariant/safety_constraint_helper/expected @@ -0,0 +1,13 @@ +Check 1: check_invariant_helper_ok.assertion.1\ + - Status: SUCCESS\ + - Description: "assertion failed: pos_point.is_safe()" + +Check 1: check_invariant_helper_ok_manual.assertion.1\ + - Status: SUCCESS\ + - Description: "assertion failed: pos_point.is_safe()" + +Check 1: check_invariant_helper_fail.assertion.1\ + - Status: FAILURE\ + - Description: "assertion failed: pos_point.is_safe()" + +Complete - 3 successfully verified harnesses, 0 failures, 3 total. diff --git a/tests/expected/derive-invariant/safety_constraint_helper/safety_constraint_helper.rs b/tests/expected/derive-invariant/safety_constraint_helper/safety_constraint_helper.rs new file mode 100644 index 000000000000..44a218f8fa63 --- /dev/null +++ b/tests/expected/derive-invariant/safety_constraint_helper/safety_constraint_helper.rs @@ -0,0 +1,43 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that the `#[safety_constraint(...)]` attribute helper adds the conditions provided to +//! the attribute to the derived `Invariant` implementation. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct PositivePoint { + #[safety_constraint(*x >= 0)] + x: i32, + #[safety_constraint(*y >= 0)] + y: i32, +} + +#[kani::proof] +fn check_invariant_helper_ok() { + let pos_point: PositivePoint = kani::any(); + assert!(pos_point.is_safe()); +} + +#[kani::proof] +#[kani::should_panic] +fn check_invariant_helper_fail() { + // In this case, we build the struct from unconstrained arbitrary values + // that do not respect `PositivePoint`'s safety constraints. + let pos_point: PositivePoint = PositivePoint { x: kani::any(), y: kani::any() }; + assert!(pos_point.is_safe()); +} + +#[kani::proof] +fn check_invariant_helper_ok_manual() { + // In this case, we build the struct from unconstrained arbitrary values + // that do not respect `PositivePoint`'s safety constraints. However, we + // manually constrain them later. + let pos_point: PositivePoint = PositivePoint { x: kani::any(), y: kani::any() }; + kani::assume(pos_point.x >= 0); + kani::assume(pos_point.y >= 0); + assert!(pos_point.is_safe()); +} diff --git a/tests/expected/derive-invariant/safety_constraint_helper_funs/expected b/tests/expected/derive-invariant/safety_constraint_helper_funs/expected new file mode 100644 index 000000000000..31b6de54c647 --- /dev/null +++ b/tests/expected/derive-invariant/safety_constraint_helper_funs/expected @@ -0,0 +1,13 @@ +Check 1: check_invariant_helper_ok.assertion.1\ + - Status: SUCCESS\ + - Description: "assertion failed: pos_point.is_safe()" + +Check 1: check_invariant_helper_ok_manual.assertion.1\ + - Status: SUCCESS\ + - Description: "assertion failed: pos_point.is_safe()" + +Check 1: check_invariant_helper_fail.assertion.1\ + - Status: FAILURE\ + - Description: "assertion failed: pos_point.is_safe()" + +Complete - 3 successfully verified harnesses, 0 failures, 3 total. diff --git a/tests/expected/derive-invariant/safety_constraint_helper_funs/safety_constraint_helper_funs.rs b/tests/expected/derive-invariant/safety_constraint_helper_funs/safety_constraint_helper_funs.rs new file mode 100644 index 000000000000..a2c4600eb208 --- /dev/null +++ b/tests/expected/derive-invariant/safety_constraint_helper_funs/safety_constraint_helper_funs.rs @@ -0,0 +1,48 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that functions can be called in the `#[safety_constraint(...)]` attribute helpers. +//! This is like the `invariant_helper` test but using a function instead +//! of passing in a predicate. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct PositivePoint { + #[safety_constraint(is_coordinate_safe(x))] + x: i32, + #[safety_constraint(is_coordinate_safe(y))] + y: i32, +} + +fn is_coordinate_safe(val: &i32) -> bool { + *val >= 0 +} + +#[kani::proof] +fn check_invariant_helper_ok() { + let pos_point: PositivePoint = kani::any(); + assert!(pos_point.is_safe()); +} + +#[kani::proof] +#[kani::should_panic] +fn check_invariant_helper_fail() { + // In this case, we build the struct from unconstrained arbitrary values + // that do not respect `PositivePoint`'s safety constraints. + let pos_point: PositivePoint = PositivePoint { x: kani::any(), y: kani::any() }; + assert!(pos_point.is_safe()); +} + +#[kani::proof] +fn check_invariant_helper_ok_manual() { + // In this case, we build the struct from unconstrained arbitrary values + // that do not respect `PositivePoint`'s safety constraints. However, we + // manually constrain them later. + let pos_point: PositivePoint = PositivePoint { x: kani::any(), y: kani::any() }; + kani::assume(pos_point.x >= 0); + kani::assume(pos_point.y >= 0); + assert!(pos_point.is_safe()); +} diff --git a/tests/expected/derive-invariant/invariant_fail/expected b/tests/expected/derive-invariant/safety_invariant_fail/expected similarity index 100% rename from tests/expected/derive-invariant/invariant_fail/expected rename to tests/expected/derive-invariant/safety_invariant_fail/expected diff --git a/tests/expected/derive-invariant/invariant_fail/invariant_fail.rs b/tests/expected/derive-invariant/safety_invariant_fail/safety_invariant_fail.rs similarity index 100% rename from tests/expected/derive-invariant/invariant_fail/invariant_fail.rs rename to tests/expected/derive-invariant/safety_invariant_fail/safety_invariant_fail.rs diff --git a/tests/expected/derive-invariant/safety_invariant_fail_mut/expected b/tests/expected/derive-invariant/safety_invariant_fail_mut/expected new file mode 100644 index 000000000000..0853a68fa79e --- /dev/null +++ b/tests/expected/derive-invariant/safety_invariant_fail_mut/expected @@ -0,0 +1,11 @@ +Check 1: check_invariant_fail_mut.assertion.1\ + - Status: SUCCESS\ + - Description: "assertion failed: pos_point.is_safe()" + +Check 2: check_invariant_fail_mut.assertion.2\ + - Status: FAILURE\ + - Description: "assertion failed: pos_point.is_safe()" + +VERIFICATION:- SUCCESSFUL (encountered one or more panics as expected) + +Complete - 1 successfully verified harnesses, 0 failures, 1 total. diff --git a/tests/expected/derive-invariant/safety_invariant_fail_mut/safety_invariant_fail_mut.rs b/tests/expected/derive-invariant/safety_invariant_fail_mut/safety_invariant_fail_mut.rs new file mode 100644 index 000000000000..dc659ec66dd6 --- /dev/null +++ b/tests/expected/derive-invariant/safety_invariant_fail_mut/safety_invariant_fail_mut.rs @@ -0,0 +1,28 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that a verification failure is triggered if we check the invariant +//! after mutating an object to violate it. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct PositivePoint { + #[safety_constraint(*x >= 0)] + x: i32, + #[safety_constraint(*y >= 0)] + y: i32, +} + +#[kani::proof] +#[kani::should_panic] +fn check_invariant_fail_mut() { + let mut pos_point: PositivePoint = kani::any(); + assert!(pos_point.is_safe()); + // Set the `x` field to an unsafe value + pos_point.x = -1; + // The object's invariant isn't preserved anymore so the next check fails + assert!(pos_point.is_safe()); +} diff --git a/tests/ui/derive-invariant/helper-empty/expected b/tests/ui/derive-invariant/helper-empty/expected new file mode 100644 index 000000000000..d8590a9d22b8 --- /dev/null +++ b/tests/ui/derive-invariant/helper-empty/expected @@ -0,0 +1,6 @@ +error: Cannot derive impl for `PositivePoint` + | +| #[derive(kani::Invariant)] + | ^^^^^^^^^^^^^^^ + | +note: safety constraint in field `x` could not be parsed: expected attribute arguments in parentheses: #[safety_constraint(...)] diff --git a/tests/ui/derive-invariant/helper-empty/helper-empty.rs b/tests/ui/derive-invariant/helper-empty/helper-empty.rs new file mode 100644 index 000000000000..3086603cf5db --- /dev/null +++ b/tests/ui/derive-invariant/helper-empty/helper-empty.rs @@ -0,0 +1,17 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check the compilation error for the `#[safety_constraint(...)]` attribute helper when an +//! argument isn't provided. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct PositivePoint { + #[safety_constraint] + x: i32, + #[safety_constraint(*y >= 0)] + y: i32, +} diff --git a/tests/ui/derive-invariant/helper-no-expr/expected b/tests/ui/derive-invariant/helper-no-expr/expected new file mode 100644 index 000000000000..e9ac7e3e1124 --- /dev/null +++ b/tests/ui/derive-invariant/helper-no-expr/expected @@ -0,0 +1,6 @@ +error: Cannot derive impl for `PositivePoint` + | +| #[derive(kani::Invariant)] + | ^^^^^^^^^^^^^^^ + | +note: safety constraint in field `x` could not be parsed: unexpected end of input, expected an expression diff --git a/tests/ui/derive-invariant/helper-no-expr/helper-no-expr.rs b/tests/ui/derive-invariant/helper-no-expr/helper-no-expr.rs new file mode 100644 index 000000000000..080c94371257 --- /dev/null +++ b/tests/ui/derive-invariant/helper-no-expr/helper-no-expr.rs @@ -0,0 +1,17 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check the compilation error for the `#[safety_constraint(...)]` attribute helper when the +//! argument is not a proper expression. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct PositivePoint { + #[safety_constraint()] + x: i32, + #[safety_constraint(*y >= 0)] + y: i32, +} diff --git a/tests/ui/derive-invariant/helper-side-effect/expected b/tests/ui/derive-invariant/helper-side-effect/expected new file mode 100644 index 000000000000..20b3d17efd38 --- /dev/null +++ b/tests/ui/derive-invariant/helper-side-effect/expected @@ -0,0 +1,9 @@ +error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference + | +| #[safety_constraint({*(x.as_mut()) = 0; true})] + | ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable + | +help: consider specifying this binding's type + | +| x: &mut std::boxed::Box: Box, + | +++++++++++++++++++++++++++ diff --git a/tests/ui/derive-invariant/helper-side-effect/helper-side-effect.rs b/tests/ui/derive-invariant/helper-side-effect/helper-side-effect.rs new file mode 100644 index 000000000000..e80f2ff25796 --- /dev/null +++ b/tests/ui/derive-invariant/helper-side-effect/helper-side-effect.rs @@ -0,0 +1,22 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that side effect expressions in the `#[safety_constraint(...)]` +//! attribute helpers are not allowed. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct PositivePoint { + #[safety_constraint({*(x.as_mut()) = 0; true})] + x: Box, + y: i32, +} + +#[kani::proof] +fn check_invariant_helper_ok() { + let pos_point: PositivePoint = kani::any(); + assert!(pos_point.is_safe()); +} diff --git a/tests/ui/derive-invariant/helper-wrong-expr/expected b/tests/ui/derive-invariant/helper-wrong-expr/expected new file mode 100644 index 000000000000..3f661bce9cbb --- /dev/null +++ b/tests/ui/derive-invariant/helper-wrong-expr/expected @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + | +| #[safety_constraint(x >= 0)] + | ^ expected `&i32`, found integer + | +help: consider dereferencing the borrow + | +| #[safety_constraint(*x >= 0)] + | diff --git a/tests/ui/derive-invariant/helper-wrong-expr/helper-wrong-expr.rs b/tests/ui/derive-invariant/helper-wrong-expr/helper-wrong-expr.rs new file mode 100644 index 000000000000..66b42e02fd68 --- /dev/null +++ b/tests/ui/derive-invariant/helper-wrong-expr/helper-wrong-expr.rs @@ -0,0 +1,18 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check the compilation error for the `#[safety_constraint(...)]` attribute helper when the +//! argument cannot be evaluated in the struct's context. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +struct PositivePoint { + // Note: `x` is a reference in this context, we should refer to `*x` + #[safety_constraint(x >= 0)] + x: i32, + #[safety_constraint(*y >= 0)] + y: i32, +} From ea0f54a6ddf9e1d449c6c96662e02bc339c6bb0c Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Tue, 23 Jul 2024 14:25:24 -0400 Subject: [PATCH 200/225] Test that boxed fn parameters are implemented. (#3361) This regression test ensures that issue #2874 does not reoccur, which was last encountered in commit 9190831. Resolves #2874 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: Jacob Salzberg Co-authored-by: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> --- tests/kani/Closure/boxed_closure.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/kani/Closure/boxed_closure.rs diff --git a/tests/kani/Closure/boxed_closure.rs b/tests/kani/Closure/boxed_closure.rs new file mode 100644 index 000000000000..4071978d28b1 --- /dev/null +++ b/tests/kani/Closure/boxed_closure.rs @@ -0,0 +1,22 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// compile-flags: -Zmir-opt-level=2 + +// The main function of this test moves an integer into a closure, +// boxes the value, then passes the closure to a function that calls it. +// This test covers the issue +// https://github.com/model-checking/kani/issues/2874 . + +fn call_boxed_closure(f: Box ()>) -> () { + f() +} + +// #[kani::proof] +fn main() { + let x = 1; + let closure = move || { + let _ = x; + () + }; + call_boxed_closure(Box::new(closure)); +} From dfd05f7d3082526e1d555385b98662b64ccf0930 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Tue, 23 Jul 2024 13:57:44 -0700 Subject: [PATCH 201/225] Fix contract handling of promoted constants and constant static (#3305) When verifying contracts, CBMC initializes all static variables to non-deterministic values, except for those with constant types or with types / values annotated with `ID_C_no_nondet_initialization`. Kani compiler never set these flags, which caused spurious failures when verification depended on promoted constants or constant static variables. This fix changes that. First, I did a bit of refactoring since we may need to set this `Symbol` property at a later time for static variables. I also got rid of the initialization function, since the allocation initialization can be done directly from an expression. Then, I added the new property to the `Symbol` type. In CBMC, this is a property of the type or expression. However, I decided to add it to `Symbol` to avoid having to add this attribute to all variants of `Type` and `Expr`. Resolves #3228 --- cprover_bindings/src/goto_program/symbol.rs | 26 +++++ .../src/goto_program/symbol_table.rs | 5 + cprover_bindings/src/irep/to_irep.rs | 4 + .../codegen_cprover_gotoc/codegen/operand.rs | 105 +++++++++--------- .../codegen_cprover_gotoc/codegen/rvalue.rs | 8 +- .../codegen/statement.rs | 5 +- .../codegen/static_var.rs | 16 ++- .../codegen_cprover_gotoc/context/goto_ctx.rs | 84 ++++++++------ kani-compiler/src/kani_middle/mod.rs | 34 +++++- .../uninit/access-static-padding.expected | 12 ++ .../expected/uninit/access-static-padding.rs | 34 ++++++ tests/kani/CodegenStatic/main.rs | 2 +- tests/kani/CodegenStatic/struct.rs | 20 +++- .../fixme_static_interior_mut.rs | 35 ++++++ .../FunctionContracts/fixme_static_mut.rs | 46 ++++++++ .../FunctionContracts/promoted_constants.rs | 54 +++++++++ .../promoted_constants_enum.rs | 32 ++++++ .../FunctionContracts/static_interior_mut.rs | 47 ++++++++ 18 files changed, 461 insertions(+), 108 deletions(-) create mode 100644 tests/expected/uninit/access-static-padding.expected create mode 100644 tests/expected/uninit/access-static-padding.rs create mode 100644 tests/kani/FunctionContracts/fixme_static_interior_mut.rs create mode 100644 tests/kani/FunctionContracts/fixme_static_mut.rs create mode 100644 tests/kani/FunctionContracts/promoted_constants.rs create mode 100644 tests/kani/FunctionContracts/promoted_constants_enum.rs create mode 100644 tests/kani/FunctionContracts/static_interior_mut.rs diff --git a/cprover_bindings/src/goto_program/symbol.rs b/cprover_bindings/src/goto_program/symbol.rs index ad71b0f84346..457be1163a3a 100644 --- a/cprover_bindings/src/goto_program/symbol.rs +++ b/cprover_bindings/src/goto_program/symbol.rs @@ -8,6 +8,8 @@ use std::fmt::Display; /// Based off the CBMC symbol implementation here: /// +/// +/// TODO: We should consider using BitFlags for all the boolean flags. #[derive(Clone, Debug)] pub struct Symbol { /// Unique identifier. Mangled name from compiler `foo12_bar17_x@1` @@ -46,6 +48,14 @@ pub struct Symbol { pub is_thread_local: bool, pub is_volatile: bool, pub is_weak: bool, + + /// This flag marks a variable as constant (IrepId: `ID_C_constant`). + /// + /// In CBMC, this is a property of the type or expression. However, we keep it here to avoid + /// having to propagate the attribute to all variants of `Type` and `Expr`. + /// + /// During contract verification, CBMC will not havoc static variables marked as constant. + pub is_static_const: bool, } /// The equivalent of a "mathematical function" in CBMC. Semantically this is an @@ -157,6 +167,7 @@ impl Symbol { is_lvalue: false, is_parameter: false, is_static_lifetime: false, + is_static_const: false, is_thread_local: false, is_volatile: false, is_weak: false, @@ -363,6 +374,11 @@ impl Symbol { self } + pub fn set_is_static_const(&mut self, v: bool) -> &mut Symbol { + self.is_static_const = v; + self + } + pub fn with_is_state_var(mut self, v: bool) -> Symbol { self.is_state_var = v; self @@ -383,11 +399,21 @@ impl Symbol { self } + pub fn set_pretty_name>(&mut self, pretty_name: T) -> &mut Symbol { + self.pretty_name = Some(pretty_name.into()); + self + } + pub fn with_is_hidden(mut self, hidden: bool) -> Symbol { self.is_auxiliary = hidden; self } + pub fn set_is_hidden(&mut self, hidden: bool) -> &mut Symbol { + self.is_auxiliary = hidden; + self + } + /// Set `is_property`. pub fn with_is_property(mut self, v: bool) -> Self { self.is_property = v; diff --git a/cprover_bindings/src/goto_program/symbol_table.rs b/cprover_bindings/src/goto_program/symbol_table.rs index 8125c8df3cd9..cd5bd8a6d967 100644 --- a/cprover_bindings/src/goto_program/symbol_table.rs +++ b/cprover_bindings/src/goto_program/symbol_table.rs @@ -107,6 +107,11 @@ impl SymbolTable { self.symbol_table.get(&name) } + pub fn lookup_mut>(&mut self, name: T) -> Option<&mut Symbol> { + let name = name.into(); + self.symbol_table.get_mut(&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 d1e84669121d..05774fdf8b43 100644 --- a/cprover_bindings/src/irep/to_irep.rs +++ b/cprover_bindings/src/irep/to_irep.rs @@ -598,6 +598,10 @@ impl goto_program::Symbol { Irep::just_sub(contract.assigns.iter().map(|req| req.to_irep(mm)).collect()), ); } + if self.is_static_const { + // Add a `const` to the type. + typ = typ.with_named_sub(IrepId::CConstant, Irep::just_id(IrepId::from_int(1))) + } super::Symbol { typ, value: match &self.value { diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs index 5549edcced25..85bb8292ec67 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs @@ -3,13 +3,13 @@ use crate::codegen_cprover_gotoc::utils::slice_fat_ptr; use crate::codegen_cprover_gotoc::GotocCtx; use crate::unwrap_or_return_codegen_unimplemented; -use cbmc::goto_program::{DatatypeComponent, Expr, ExprValue, Location, Stmt, Symbol, Type}; +use cbmc::goto_program::{DatatypeComponent, Expr, ExprValue, Location, Symbol, Type}; use rustc_middle::ty::Const as ConstInternal; use rustc_smir::rustc_internal; use rustc_span::Span as SpanInternal; use stable_mir::mir::alloc::{AllocId, GlobalAlloc}; use stable_mir::mir::mono::{Instance, StaticDef}; -use stable_mir::mir::Operand; +use stable_mir::mir::{Mutability, Operand}; use stable_mir::ty::{ Allocation, ConstantKind, FloatTy, FnDef, GenericArgs, IntTy, MirConst, RigidTy, Size, Ty, TyConst, TyConstKind, TyKind, UintTy, @@ -470,11 +470,17 @@ impl<'tcx> GotocCtx<'tcx> { name: Option, loc: Location, ) -> Expr { - debug!(?name, "codegen_const_allocation"); + debug!(?name, ?alloc, "codegen_const_allocation"); let alloc_name = match self.alloc_map.get(alloc) { None => { let alloc_name = if let Some(name) = name { name } else { self.next_global_name() }; - self.codegen_alloc_in_memory(alloc.clone(), alloc_name.clone(), loc); + let has_interior_mutabity = false; // Constants cannot be mutated. + self.codegen_alloc_in_memory( + alloc.clone(), + alloc_name.clone(), + loc, + has_interior_mutabity, + ); alloc_name } Some(name) => name.clone(), @@ -484,13 +490,18 @@ impl<'tcx> GotocCtx<'tcx> { mem_place.address_of() } - /// Insert an allocation into the goto symbol table, and generate a goto function that will - /// initialize it. + /// Insert an allocation into the goto symbol table, and generate an init value. /// - /// This function is ultimately responsible for creating new statically initialized global variables - /// in our goto binaries. - pub fn codegen_alloc_in_memory(&mut self, alloc: Allocation, name: String, loc: Location) { - debug!(?alloc, ?name, "codegen_alloc_in_memory"); + /// This function is ultimately responsible for creating new statically initialized global + /// variables. + pub fn codegen_alloc_in_memory( + &mut self, + alloc: Allocation, + name: String, + loc: Location, + has_interior_mutabity: bool, + ) { + debug!(?name, ?alloc, "codegen_alloc_in_memory"); let struct_name = &format!("{name}::struct"); // The declaration of a static variable may have one type and the constant initializer for @@ -513,50 +524,40 @@ impl<'tcx> GotocCtx<'tcx> { .collect() }); + // Create the allocation from a byte array. + let init_fn = |gcx: &mut GotocCtx, var: Symbol| { + let val = Expr::struct_expr_from_values( + alloc_typ_ref.clone(), + alloc_data + .iter() + .map(|d| match d { + AllocData::Bytes(bytes) => Expr::array_expr( + Type::unsigned_int(8).array_of(bytes.len()), + bytes + .iter() + // We should consider adding a poison / undet where we have none + // This mimics the behaviour before StableMIR though. + .map(|b| Expr::int_constant(b.unwrap_or(0), Type::unsigned_int(8))) + .collect(), + ), + AllocData::Expr(e) => e.clone(), + }) + .collect(), + &gcx.symbol_table, + ); + if val.typ() == &var.typ { val } else { val.transmute_to(var.typ, &gcx.symbol_table) } + }; + // The global static variable may not be in the symbol table if we are dealing - // with a literal that can be statically allocated. - // We need to make a constructor whether it was in the table or not, so we can't use the - // closure argument to ensure_global_var to do that here. - let var = self.ensure_global_var( + // with a promoted constant. + let _var = self.ensure_global_var_init( &name, false, //TODO is this correct? + alloc.mutability == Mutability::Not && !has_interior_mutabity, alloc_typ_ref.clone(), loc, - |_, _| None, - ); - let var_typ = var.typ().clone(); - - // Assign the initial value `val` to `var` via an intermediate `temp_var` to allow for - // transmuting the allocation type to the global static variable type. - let val = Expr::struct_expr_from_values( - alloc_typ_ref.clone(), - alloc_data - .iter() - .map(|d| match d { - AllocData::Bytes(bytes) => Expr::array_expr( - Type::unsigned_int(8).array_of(bytes.len()), - bytes - .iter() - // We should consider adding a poison / undet where we have none - // This mimics the behaviour before StableMIR though. - .map(|b| Expr::int_constant(b.unwrap_or(0), Type::unsigned_int(8))) - .collect(), - ), - AllocData::Expr(e) => e.clone(), - }) - .collect(), - &self.symbol_table, - ); - let fn_name = Self::initializer_fn_name(&name); - let temp_var = self.gen_function_local_variable(0, &fn_name, alloc_typ_ref, loc).to_expr(); - let body = Stmt::block( - vec![ - Stmt::decl(temp_var.clone(), Some(val), loc), - var.assign(temp_var.transmute_to(var_typ, &self.symbol_table), loc), - ], - loc, + init_fn, ); - self.register_initializer(&name, body); self.alloc_map.insert(alloc, name); } @@ -663,12 +664,6 @@ impl<'tcx> GotocCtx<'tcx> { let fn_item_struct_ty = self.codegen_fndef_type_stable(instance); // This zero-sized object that a function name refers to in Rust is globally unique, so we create such a global object. let fn_singleton_name = format!("{mangled_name}::FnDefSingleton"); - self.ensure_global_var( - &fn_singleton_name, - false, - fn_item_struct_ty, - loc, - |_, _| None, // zero-sized, so no initialization necessary - ) + self.ensure_global_var(&fn_singleton_name, false, fn_item_struct_ty, loc).to_expr() } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index f84708b9bdd5..4883c608f482 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -1444,9 +1444,10 @@ impl<'tcx> GotocCtx<'tcx> { let vtable_name = self.vtable_name_stable(trait_type).intern(); let vtable_impl_name = format!("{vtable_name}_impl_for_{src_name}"); - self.ensure_global_var( + self.ensure_global_var_init( vtable_impl_name, true, + true, Type::struct_tag(vtable_name), loc, |ctx, var| { @@ -1487,11 +1488,10 @@ impl<'tcx> GotocCtx<'tcx> { vtable_fields, &ctx.symbol_table, ); - let body = var.assign(vtable, loc); - let block = Stmt::block(vec![size_assert, body], loc); - Some(block) + Expr::statement_expression(vec![size_assert, vtable.as_stmt(loc)], var.typ, loc) }, ) + .to_expr() } /// Cast a pointer to a fat pointer. diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index c606ae13d095..697cacd3b191 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -317,9 +317,8 @@ impl<'tcx> GotocCtx<'tcx> { fn codegen_ret_unit(&mut self, loc: Location) -> Stmt { let is_file_local = false; let ty = self.codegen_ty_unit(); - let var = - self.ensure_global_var(FN_RETURN_VOID_VAR_NAME, is_file_local, ty, loc, |_, _| None); - Stmt::ret(Some(var), loc) + let var = self.ensure_global_var(FN_RETURN_VOID_VAR_NAME, is_file_local, ty, loc); + Stmt::ret(Some(var.to_expr()), loc) } /// Generates Goto-C for MIR [TerminatorKind::Drop] calls. We only handle code _after_ Rust's "drop elaboration" diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/static_var.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/static_var.rs index e05da3f7f622..537fbcb63fbb 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/static_var.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/static_var.rs @@ -4,7 +4,7 @@ //! This file contains functions related to codegenning MIR static variables into gotoc use crate::codegen_cprover_gotoc::GotocCtx; -use cbmc::goto_program::Symbol; +use crate::kani_middle::is_interior_mut; use stable_mir::mir::mono::{Instance, StaticDef}; use stable_mir::CrateDef; use tracing::debug; @@ -19,7 +19,12 @@ impl<'tcx> GotocCtx<'tcx> { debug!("codegen_static"); let alloc = def.eval_initializer().unwrap(); let symbol_name = Instance::from(def).mangled_name(); - self.codegen_alloc_in_memory(alloc, symbol_name, self.codegen_span_stable(def.span())); + self.codegen_alloc_in_memory( + alloc, + symbol_name, + self.codegen_span_stable(def.span()), + is_interior_mut(self.tcx, def.ty()), + ); } /// Mutates the Goto-C symbol table to add a forward-declaration of the static variable. @@ -37,9 +42,8 @@ impl<'tcx> GotocCtx<'tcx> { // havoc static variables. Kani uses the location and pretty name to identify // the correct variables. If the wrong name is used, CBMC may fail silently. // More details at https://github.com/diffblue/cbmc/issues/8225. - let symbol = Symbol::static_variable(symbol_name.clone(), symbol_name, typ, location) - .with_is_hidden(false) // Static items are always user defined. - .with_pretty_name(pretty_name); - self.symbol_table.insert(symbol); + self.ensure_global_var(symbol_name, false, typ, location) + .set_is_hidden(false) // Static items are always user defined. + .set_pretty_name(pretty_name); } } 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 7d51cff037c6..e360cd491edd 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs @@ -21,7 +21,9 @@ use crate::codegen_cprover_gotoc::utils::full_crate_name; use crate::codegen_cprover_gotoc::UnsupportedConstructs; use crate::kani_middle::transform::BodyTransformation; use crate::kani_queries::QueryDb; -use cbmc::goto_program::{DatatypeComponent, Expr, Location, Stmt, Symbol, SymbolTable, Type}; +use cbmc::goto_program::{ + DatatypeComponent, Expr, Location, Stmt, Symbol, SymbolTable, SymbolValues, Type, +}; use cbmc::utils::aggr_tag; use cbmc::{InternedString, MachineModel}; use rustc_data_structures::fx::FxHashMap; @@ -38,6 +40,7 @@ use rustc_target::abi::{HasDataLayout, TargetDataLayout}; use stable_mir::mir::mono::Instance; use stable_mir::mir::Body; use stable_mir::ty::Allocation; +use std::fmt::Debug; pub struct GotocCtx<'tcx> { /// the typing context @@ -200,34 +203,58 @@ impl<'tcx> GotocCtx<'tcx> { self.symbol_table.lookup(name).unwrap() } + /// Ensures that a global variable `name` appears in the Symbol table and is initialized. + /// + /// This will add the symbol to the Symbol Table if not inserted yet. + /// This will register the initialization function if not initialized yet. + /// - This case can happen for static variables, since they are declared first. + pub fn ensure_global_var_init( + &mut self, + name: T, + is_file_local: bool, + is_const: bool, + t: Type, + loc: Location, + init: F, + ) -> &mut Symbol + where + T: Into + Clone + Debug, + F: Fn(&mut GotocCtx, Symbol) -> Expr, + { + let sym = self.ensure_global_var(name.clone(), is_file_local, t, loc); + sym.set_is_static_const(is_const); + if matches!(sym.value, SymbolValues::None) { + // Clone sym so we can use `&mut self`. + let sym = sym.clone(); + let init_expr = SymbolValues::Expr(init(self, sym)); + // Need to lookup again since symbol table might've changed. + let sym = self.symbol_table.lookup_mut(name).unwrap(); + sym.value = init_expr; + sym + } else { + self.symbol_table.lookup_mut(name).unwrap() + } + } + /// Ensures that a global variable `name` appears in the Symbol table. - /// If it doesn't, inserts it. - /// If `init_fn` returns `Some(body)`, creates an initializer for the variable using `body`. - /// Otherwise, leaves the variable uninitialized . - pub fn ensure_global_var< - F: FnOnce(&mut GotocCtx<'tcx>, Expr) -> Option, - T: Into, - >( + /// + /// This will add the symbol to the Symbol Table if not inserted yet. + pub fn ensure_global_var + Clone>( &mut self, name: T, is_file_local: bool, t: Type, loc: Location, - init_fn: F, - ) -> Expr { - let name = name.into(); - if !self.symbol_table.contains(name) { - tracing::debug!(?name, "Ensure global variable"); - let sym = Symbol::static_variable(name, name, t, loc) + ) -> &mut Symbol { + let sym_name = name.clone().into(); + if !self.symbol_table.contains(sym_name) { + tracing::debug!(?sym_name, "ensure_global_var insert"); + let sym = Symbol::static_variable(sym_name, sym_name, t, loc) .with_is_file_local(is_file_local) .with_is_hidden(false); - let var = sym.to_expr(); - self.symbol_table.insert(sym); - if let Some(body) = init_fn(self, var) { - self.register_initializer(&name.to_string(), body); - } + self.symbol_table.insert(sym.clone()); } - self.symbol_table.lookup(name).unwrap().to_expr() + self.symbol_table.lookup_mut(sym_name).unwrap() } /// Ensures that a struct with name `struct_name` appears in the symbol table. @@ -284,23 +311,6 @@ impl<'tcx> GotocCtx<'tcx> { } Type::union_tag(union_name) } - - /// Makes a `__attribute__((constructor)) fnname() {body}` initalizer function - pub fn register_initializer(&mut self, var_name: &str, body: Stmt) -> &Symbol { - let fn_name = Self::initializer_fn_name(var_name); - let pretty_name = format!("{var_name}::init"); - let loc = *body.location(); - self.ensure(&fn_name, |_tcx, _| { - Symbol::function( - &fn_name, - Type::code(vec![], Type::constructor()), - Some(Stmt::block(vec![body], loc)), //TODO is this block needed? - &pretty_name, - loc, - ) - .with_is_file_local(true) - }) - } } /// Mutators diff --git a/kani-compiler/src/kani_middle/mod.rs b/kani-compiler/src/kani_middle/mod.rs index 3c300e9da52c..a7a512c86de3 100644 --- a/kani-compiler/src/kani_middle/mod.rs +++ b/kani-compiler/src/kani_middle/mod.rs @@ -19,8 +19,10 @@ use rustc_span::Span; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{HasDataLayout, TargetDataLayout}; use stable_mir::mir::mono::MonoItem; -use stable_mir::ty::{FnDef, RigidTy, Span as SpanStable, TyKind}; +use stable_mir::ty::{FnDef, RigidTy, Span as SpanStable, Ty, TyKind}; +use stable_mir::visitor::{Visitable, Visitor as TyVisitor}; use stable_mir::CrateDef; +use std::ops::ControlFlow; use self::attributes::KaniAttributes; @@ -62,6 +64,36 @@ pub fn check_crate_items(tcx: TyCtxt, ignore_asm: bool) { tcx.dcx().abort_if_errors(); } +/// Traverse the type definition to see if the type contains interior mutability. +/// +/// See for more details. +pub fn is_interior_mut(tcx: TyCtxt, ty: Ty) -> bool { + let mut visitor = FindUnsafeCell { tcx }; + visitor.visit_ty(&ty) == ControlFlow::Break(()) +} + +struct FindUnsafeCell<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> TyVisitor for FindUnsafeCell<'tcx> { + type Break = (); + fn visit_ty(&mut self, ty: &Ty) -> ControlFlow { + match ty.kind() { + TyKind::RigidTy(RigidTy::Adt(def, _)) + if rustc_internal::internal(self.tcx, def).is_unsafe_cell() => + { + ControlFlow::Break(()) + } + TyKind::RigidTy(RigidTy::Ref(..) | RigidTy::RawPtr(..)) => { + // We only care about the current memory space. + ControlFlow::Continue(()) + } + _ => ty.super_visit(self), + } + } +} + /// Check that all given items are supported and there's no misconfiguration. /// This method will exhaustively print any error / warning and it will abort at the end if any /// error was found. diff --git a/tests/expected/uninit/access-static-padding.expected b/tests/expected/uninit/access-static-padding.expected new file mode 100644 index 000000000000..5577aa3684c5 --- /dev/null +++ b/tests/expected/uninit/access-static-padding.expected @@ -0,0 +1,12 @@ +Checking harness check_read_assoc_const_padding_fails... +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const [u8; 32]` + +Checking harness check_read_static_padding_fails... +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const [u8; 8]` + +Checking harness check_read_const_padding_fails... +Failed Checks: Undefined Behavior: Reading from an uninitialized pointer of type `*const [u8; 4]` + +Verification failed for - check_read_assoc_const_padding_fails +Verification failed for - check_read_static_padding_fails +Verification failed for - check_read_const_padding_fails diff --git a/tests/expected/uninit/access-static-padding.rs b/tests/expected/uninit/access-static-padding.rs new file mode 100644 index 000000000000..5e9edb5ff9fc --- /dev/null +++ b/tests/expected/uninit/access-static-padding.rs @@ -0,0 +1,34 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Z uninit-checks +//! Add a check to ensure that we correctly detect reading the padding values of a static and +//! also of a constant. + +#![feature(generic_const_exprs)] + +/// Check if all the values in the buffer is equals to zero. +unsafe fn is_zeroed(orig: *const T) -> bool +where + [(); size_of::()]:, +{ + let buf = orig as *const [u8; size_of::()]; + unsafe { &*buf }.iter().all(|val| *val == 0) +} + +const CONST_PADDING: (u8, u16) = (0, 0); +static STATIC_PADDING: (u8, char) = (0, '\0'); + +#[kani::proof] +fn check_read_const_padding_fails() { + assert!(unsafe { is_zeroed(&CONST_PADDING) }); +} + +#[kani::proof] +fn check_read_static_padding_fails() { + assert!(unsafe { is_zeroed(&STATIC_PADDING) }); +} + +#[kani::proof] +fn check_read_assoc_const_padding_fails() { + assert!(unsafe { is_zeroed(&(0u128, 0u16)) }); +} diff --git a/tests/kani/CodegenStatic/main.rs b/tests/kani/CodegenStatic/main.rs index 9731ca4fe080..7d2791268c7d 100644 --- a/tests/kani/CodegenStatic/main.rs +++ b/tests/kani/CodegenStatic/main.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT static STATIC: [&str; 1] = ["FOO"]; #[kani::proof] -fn main() { +fn check_static() { let x = STATIC[0]; let bytes = x.as_bytes(); assert!(bytes.len() == 3); diff --git a/tests/kani/CodegenStatic/struct.rs b/tests/kani/CodegenStatic/struct.rs index 441e118175b1..1801de3c7613 100644 --- a/tests/kani/CodegenStatic/struct.rs +++ b/tests/kani/CodegenStatic/struct.rs @@ -8,6 +8,24 @@ pub struct Foo { const x: Foo<3> = Foo { bytes: [1, 2, 3] }; #[kani::proof] -fn main() { +fn simple_struct() { assert!(x.bytes[0] == 1); } + +pub struct Outer { + data: char, + inner: Inner, +} + +pub struct Inner { + a: char, + b: char, + c: char, +} + +static OUTER: Outer = Outer { data: 'a', inner: Inner { a: 'a', b: 'b', c: 'c' } }; + +#[kani::proof] +fn nested_struct() { + assert!(OUTER.inner.c == 'c'); +} diff --git a/tests/kani/FunctionContracts/fixme_static_interior_mut.rs b/tests/kani/FunctionContracts/fixme_static_interior_mut.rs new file mode 100644 index 000000000000..1b18472b5590 --- /dev/null +++ b/tests/kani/FunctionContracts/fixme_static_interior_mut.rs @@ -0,0 +1,35 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts +//! This file is a duplicate of `static_interior_mut.rs` that captures the current over-approx +//! we perform for static variables with `UnsafeCell`. +//! When you fix this, please delete this file and enable the `regular_field` harness in the +//! original file. + +extern crate kani; + +use std::cell::UnsafeCell; + +pub struct WithMut { + regular_field: u8, + mut_field: UnsafeCell, +} + +/// Just for test purpose. +unsafe impl Sync for WithMut {} + +/// A static definition of `WithMut` +static ZERO_VAL: WithMut = WithMut { regular_field: 0, mut_field: UnsafeCell::new(0) }; + +/// The regular field should be 0. +#[kani::ensures(|result| *result == 0)] +pub fn regular_field() -> u8 { + ZERO_VAL.regular_field +} + +/// This harness is a copy from `static_interior_mut.rs`. +/// Once this gets fixed, please delete this file and enable the original one. +#[kani::proof_for_contract(regular_field)] +fn check_regular_field_is_const() { + assert_eq!(regular_field(), 0); // ** This should succeed since this field is constant. +} diff --git a/tests/kani/FunctionContracts/fixme_static_mut.rs b/tests/kani/FunctionContracts/fixme_static_mut.rs new file mode 100644 index 000000000000..0ee88a3e5ad5 --- /dev/null +++ b/tests/kani/FunctionContracts/fixme_static_mut.rs @@ -0,0 +1,46 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts +//! This test checks that contracts correctly handles mutable static. + +static mut WRAP_COUNTER: Option = None; + +/// This function is safe and should never crash. Counter starts at 0. +#[kani::modifies(std::ptr::addr_of!(WRAP_COUNTER))] +#[kani::ensures(|_| true)] +pub fn next() -> u32 { + // Safe in single-threaded. + unsafe { + match &WRAP_COUNTER { + Some(val) => { + WRAP_COUNTER = Some(val.wrapping_add(1)); + *val + } + None => { + WRAP_COUNTER = Some(0); + 0 + } + } + } +} + +/// This harness should succeed. +/// +/// Today, CBMC havocs WRAP_COUNTER, which includes invalid discriminants triggering UB. +#[kani::proof_for_contract(next)] +fn check_next() { + let _ret = next(); +} + +/// Without contracts, we can safely verify `next`. +#[kani::proof] +fn check_next_directly() { + // First check that initial iteration returns 0 (base case). + let first = next(); + assert_eq!(first, 0); + + // Havoc WRAP_COUNTER and invoke next. + unsafe { WRAP_COUNTER = kani::any() }; + let ret = next(); + kani::cover!(ret == 0); +} diff --git a/tests/kani/FunctionContracts/promoted_constants.rs b/tests/kani/FunctionContracts/promoted_constants.rs new file mode 100644 index 000000000000..75202f1bfedd --- /dev/null +++ b/tests/kani/FunctionContracts/promoted_constants.rs @@ -0,0 +1,54 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts +//! This test checks that contracts does not havoc +//! [promoted constants](https://github.com/rust-lang/const-eval/blob/master/promotion.md). +//! Related issue: + +extern crate kani; + +#[derive(PartialEq, Eq, kani::Arbitrary)] +pub struct Foo(u8); + +/// A named constant static should work the same way as a promoted constant. +static FOO: Foo = Foo(1); + +/// A mutable static that should be havocked before contract validation. +static mut FOO_MUT: Foo = Foo(1); + +/// Add a contract using a temporary variable that is lifted to a const static. +#[kani::requires(foo == Foo(1))] +pub fn foo_promoted(foo: Foo) -> Foo { + assert!(foo.0 == 1); + foo +} + +/// Add a contract using a const static. +#[kani::requires(foo == FOO)] +pub fn foo_static(foo: Foo) -> Foo { + assert!(foo.0 == 1); + foo +} + +/// Add a contract using a mutable static. +#[kani::requires(&foo == unsafe { &FOO_MUT })] +pub fn foo_mut_static(foo: Foo) -> Foo { + assert!(foo.0 == 1); + foo +} + +#[kani::proof_for_contract(foo_promoted)] +fn check_promoted() { + foo_promoted(kani::any()); +} + +#[kani::proof_for_contract(foo_static)] +fn check_static() { + foo_static(kani::any()); +} + +#[kani::proof_for_contract(foo_mut_static)] +#[kani::should_panic] +fn check_mut_static() { + foo_mut_static(kani::any()); +} diff --git a/tests/kani/FunctionContracts/promoted_constants_enum.rs b/tests/kani/FunctionContracts/promoted_constants_enum.rs new file mode 100644 index 000000000000..12878eee8044 --- /dev/null +++ b/tests/kani/FunctionContracts/promoted_constants_enum.rs @@ -0,0 +1,32 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts +//! This test checks that contracts does not havoc +//! [promoted constants](https://github.com/rust-lang/const-eval/blob/master/promotion.md) +//! that represents an enum variant. +//! +//! Related issue: + +extern crate kani; +#[derive(PartialEq, Eq, kani::Arbitrary)] +pub enum Foo { + A, + B, +} + +#[kani::ensures(|result: &Foo| *result == Foo::A)] +pub fn foo_a() -> Foo { + Foo::A +} + +#[kani::proof_for_contract(foo_a)] +fn check() { + let _ = foo_a(); +} + +#[kani::proof] +#[kani::stub_verified(foo_a)] +fn check_stub() { + let val = foo_a(); + assert!(val == Foo::A) +} diff --git a/tests/kani/FunctionContracts/static_interior_mut.rs b/tests/kani/FunctionContracts/static_interior_mut.rs new file mode 100644 index 000000000000..deb20e739fb3 --- /dev/null +++ b/tests/kani/FunctionContracts/static_interior_mut.rs @@ -0,0 +1,47 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts +//! This test checks that contracts havoc static variables with interior mutability. +//! For now, we over-approximate and havoc the entire static. + +extern crate kani; + +use std::cell::UnsafeCell; + +pub struct WithMut { + regular_field: u8, + mut_field: UnsafeCell, +} + +/// Just for test purpose. +unsafe impl Sync for WithMut {} + +/// A static definition of `WithMut` +static ZERO_VAL: WithMut = WithMut { regular_field: 0, mut_field: UnsafeCell::new(0) }; + +/// The regular field should be 0. +#[kani::ensures(|result| *result == 0)] +pub fn regular_field() -> u8 { + ZERO_VAL.regular_field +} + +/// The mutable field can be anything. +#[kani::ensures(|result| *result == old(unsafe { *ZERO_VAL.mut_field.get() }))] +pub unsafe fn mut_field() -> u8 { + unsafe { *ZERO_VAL.mut_field.get() } +} + +/// This harness is duplicated in `fixme_static_interior_mut.rs`. +/// Issue <> +#[cfg(fixme)] +#[kani::proof_for_contract(regular_field)] +fn check_regular_field_is_const() { + assert_eq!(regular_field(), 0); // ** This should succeed since this field is constant. +} + +// Ensure that Kani havoc the mutable field. +#[kani::should_panic] +#[kani::proof_for_contract(mut_field)] +fn check_regular_field_is_const() { + assert_eq!(unsafe { mut_field() }, 0); // ** This must fail since Kani havoc the mutable field. +} From 5a5044a3e8744c6a14730b101292c5b2fec10689 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Tue, 23 Jul 2024 23:40:46 +0200 Subject: [PATCH 202/225] Bump CBMC Viewer to 3.9 (#3373) This is in preparation of updating to CBMC 6, which emits some properties in ways that CBMC Viewer 3.8 did not adequately parse. --- kani-dependencies | 4 ++-- scripts/setup/macos/install_deps.sh | 4 ++-- src/setup.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kani-dependencies b/kani-dependencies index 200755839284..844d83c23dc7 100644 --- a/kani-dependencies +++ b/kani-dependencies @@ -4,7 +4,7 @@ CBMC_VERSION="5.95.1" # If you update this version number, remember to bump it in `src/setup.rs` too CBMC_VIEWER_MAJOR="3" -CBMC_VIEWER_MINOR="8" -CBMC_VIEWER_VERSION="3.8" +CBMC_VIEWER_MINOR="9" +CBMC_VIEWER_VERSION="3.9" KISSAT_VERSION="3.1.1" diff --git a/scripts/setup/macos/install_deps.sh b/scripts/setup/macos/install_deps.sh index 429eb200541a..179bf8c1237f 100755 --- a/scripts/setup/macos/install_deps.sh +++ b/scripts/setup/macos/install_deps.sh @@ -4,10 +4,10 @@ set -eux -# Github promises weekly build image updates, so we can skip the update step and +# Github promises weekly build image updates, so we could skip the update step and # worst case we should only be 1-2 weeks behind upstream brew. # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-software -#brew update +brew update # Install Python separately to workround recurring homebrew CI issue. # See https://github.com/actions/runner-images/issues/9471 for more details. diff --git a/src/setup.rs b/src/setup.rs index 730679f06c10..58387f7031b7 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -189,7 +189,7 @@ fn setup_python_deps(kani_dir: &Path) -> Result<()> { let pyroot = kani_dir.join("pyroot"); // TODO: this is a repetition of versions from kani/kani-dependencies - let pkg_versions = &["cbmc-viewer==3.8"]; + let pkg_versions = &["cbmc-viewer==3.9"]; Command::new("python3") .args(["-m", "pip", "install", "--target"]) From 1b1a9b6714038382bc9a592da056dd202e57d8e4 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 24 Jul 2024 17:53:45 +0200 Subject: [PATCH 203/225] Import apply_closure into kani_core (#3375) This enables use of `|result|` when verifying the standard library. --- library/kani_core/src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index 68a8e79658f1..a07ba7ca8ec4 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -384,6 +384,13 @@ macro_rules! kani_intrinsics { #[doc(hidden)] #[rustc_diagnostic_item = "KaniInitContracts"] pub fn init_contracts() {} + + /// This should only be used within contracts. The intent is to + /// perform type inference on a closure's argument + #[doc(hidden)] + pub fn apply_closure bool>(f: U, x: &T) -> bool { + f(x) + } } }; } From f37944524306a2708c2f352b76d627d44ec70894 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2024 16:12:30 -0400 Subject: [PATCH 204/225] Bump tests/perf/s2n-quic from `f568f26` to `75afd77` (#3378) --- 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 f568f269ee5c..75afd77dfa88 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit f568f269ee5c9896f4936089c26dfbb3f87f4dab +Subproject commit 75afd77dfa88d696900f12ee747409ddb208a745 From d66f0c22ea43b3d88b843746f8975372ef22f722 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 24 Jul 2024 22:54:14 +0200 Subject: [PATCH 205/225] compiletest: ensure extra arguments are appended at the end (#3379) We must not mix arguments to be passed to Kani with those parsed by compiletest. Will enable use of `--cbmc-args` to increase CBMC's verbosity as needed for certain tests. --- tools/compiletest/src/runtest.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/compiletest/src/runtest.rs b/tools/compiletest/src/runtest.rs index 7925ed83e6e5..50f1e3035ac8 100644 --- a/tools/compiletest/src/runtest.rs +++ b/tools/compiletest/src/runtest.rs @@ -272,14 +272,14 @@ impl<'test> TestCx<'test> { .arg("kani") .arg("--target-dir") .arg(self.output_base_dir().join("target")) - .current_dir(parent_dir) - .args(&self.config.extra_args); + .current_dir(parent_dir); if test { cargo.arg("--tests"); } if "expected" != self.testpaths.file.file_name().unwrap() { cargo.args(["--harness", function_name]); } + cargo.args(&self.config.extra_args); let proc_res = self.compose_and_run(cargo); self.verify_output(&proc_res, &self.testpaths.file); From ae3bdb2b1879a7831944e016578fae7ebd72a00f Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 24 Jul 2024 23:28:43 +0200 Subject: [PATCH 206/225] Prepare CBMC v6+ build on Ubuntu 18.04 (#3377) Use PPA with GCC-9 to build CBMC v6+ on Ubuntu 18.04 (to have full C++17 support) and build static binaries so that they can still be used on systems that might have libstdc++ from said PPA. --- .github/workflows/release.yml | 6 +++++- scripts/setup/ubuntu/install_cbmc.sh | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9fb35033d2d1..72ef4e2de889 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -104,10 +104,14 @@ jobs: apt-get install -y software-properties-common apt-utils add-apt-repository ppa:git-core/ppa add-apt-repository ppa:deadsnakes/ppa + add-apt-repository ppa:ubuntu-toolchain-r/test apt-get update apt-get install -y \ - build-essential bash-completion curl lsb-release sudo g++ gcc flex \ + build-essential bash-completion curl lsb-release sudo g++-9 gcc-9 flex \ bison make patch git python3.7 python3.7-dev python3.7-distutils + update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 110 \ + --slave /usr/bin/g++ g++ /usr/bin/g++-9 + ln -sf cpp-9 /usr/bin/cpp update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 1 curl -s https://bootstrap.pypa.io/pip/3.7/get-pip.py -o get-pip.py python3 get-pip.py --force-reinstall diff --git a/scripts/setup/ubuntu/install_cbmc.sh b/scripts/setup/ubuntu/install_cbmc.sh index f37aafcd6327..31015ab9de7a 100755 --- a/scripts/setup/ubuntu/install_cbmc.sh +++ b/scripts/setup/ubuntu/install_cbmc.sh @@ -42,7 +42,8 @@ pushd "${WORK_DIR}" mkdir build git submodule update --init -cmake -S . -Bbuild -DWITH_JBMC=OFF -Dsat_impl="minisat2;cadical" +cmake -S . -Bbuild -DWITH_JBMC=OFF -Dsat_impl="minisat2;cadical" \ + -DBUILD_SHARED_LIBS=OFF -DCMAKE_EXE_LINKER_FLAGS=-static make -C build -j$(nproc) cpack -G DEB --config build/CPackConfig.cmake sudo dpkg -i ./cbmc-*.deb From abcb54e7d5447e7385371438d546fa26eca9ea2d Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Thu, 25 Jul 2024 09:27:10 +0200 Subject: [PATCH 207/225] Maintain test execution performance with newer CaDiCaL versions (#3381) CBMC using CaDiCaL 1.9.2 and later have substantially worse performance on these tests than CaDiCaL 1.7.2, which CBMC 5.95.1 uses. Using MiniSat will make sure performance remains consistent. --- tests/kani/FloatingPoint/main.rs | 1 + tests/kani/Intrinsics/Math/Rounding/Ceil/ceilf64.rs | 2 ++ tests/kani/Intrinsics/Math/Rounding/Floor/floorf64.rs | 2 ++ tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf32.rs | 2 ++ tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf64.rs | 2 ++ tests/kani/Intrinsics/Math/Rounding/RInt/rintf32.rs | 2 ++ tests/kani/Intrinsics/Math/Rounding/RInt/rintf64.rs | 2 ++ tests/kani/Intrinsics/Math/Rounding/Round/roundf32.rs | 2 ++ tests/kani/Intrinsics/Math/Rounding/Round/roundf64.rs | 2 ++ tests/kani/Intrinsics/Math/Rounding/Trunc/truncf64.rs | 2 ++ 10 files changed, 19 insertions(+) diff --git a/tests/kani/FloatingPoint/main.rs b/tests/kani/FloatingPoint/main.rs index f8ebccdac02a..93a29f169f27 100644 --- a/tests/kani/FloatingPoint/main.rs +++ b/tests/kani/FloatingPoint/main.rs @@ -26,6 +26,7 @@ macro_rules! test_floats { } #[kani::proof] +#[kani::solver(minisat)] fn main() { assert!(1.1 == 1.1 * 1.0); assert!(1.1 != 1.11 / 1.0); diff --git a/tests/kani/Intrinsics/Math/Rounding/Ceil/ceilf64.rs b/tests/kani/Intrinsics/Math/Rounding/Ceil/ceilf64.rs index 09c630aa94a7..642d984a7e2b 100644 --- a/tests/kani/Intrinsics/Math/Rounding/Ceil/ceilf64.rs +++ b/tests/kani/Intrinsics/Math/Rounding/Ceil/ceilf64.rs @@ -45,6 +45,7 @@ fn test_conc_sci() { } #[kani::proof] +#[kani::solver(minisat)] fn test_towards_inf() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); @@ -53,6 +54,7 @@ fn test_towards_inf() { } #[kani::proof] +#[kani::solver(minisat)] fn test_diff_one() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/Floor/floorf64.rs b/tests/kani/Intrinsics/Math/Rounding/Floor/floorf64.rs index 0560a2c55064..54ad74c33430 100644 --- a/tests/kani/Intrinsics/Math/Rounding/Floor/floorf64.rs +++ b/tests/kani/Intrinsics/Math/Rounding/Floor/floorf64.rs @@ -45,6 +45,7 @@ fn test_conc_sci() { } #[kani::proof] +#[kani::solver(minisat)] fn test_towards_neg_inf() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); @@ -53,6 +54,7 @@ fn test_towards_neg_inf() { } #[kani::proof] +#[kani::solver(minisat)] fn test_diff_one() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf32.rs b/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf32.rs index 25e02f45a943..7ffdb5f28747 100644 --- a/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf32.rs +++ b/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf32.rs @@ -50,6 +50,7 @@ fn test_conc_sci() { } #[kani::proof] +#[kani::solver(minisat)] fn test_towards_nearest() { let x: f32 = kani::any(); kani::assume(!x.is_nan()); @@ -88,6 +89,7 @@ fn test_towards_nearest() { } #[kani::proof] +#[kani::solver(minisat)] fn test_diff_half_one() { let x: f32 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf64.rs b/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf64.rs index 589a44a4d1ac..bf656512562e 100644 --- a/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf64.rs +++ b/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf64.rs @@ -50,6 +50,7 @@ fn test_conc_sci() { } #[kani::proof] +#[kani::solver(minisat)] fn test_towards_nearest() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); @@ -88,6 +89,7 @@ fn test_towards_nearest() { } #[kani::proof] +#[kani::solver(minisat)] fn test_diff_half_one() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/RInt/rintf32.rs b/tests/kani/Intrinsics/Math/Rounding/RInt/rintf32.rs index 79a0a4f9be2c..53d9d7d5fc73 100644 --- a/tests/kani/Intrinsics/Math/Rounding/RInt/rintf32.rs +++ b/tests/kani/Intrinsics/Math/Rounding/RInt/rintf32.rs @@ -55,6 +55,7 @@ fn test_conc_sci() { } #[kani::proof] +#[kani::solver(minisat)] fn test_towards_nearest() { let x: f32 = kani::any(); kani::assume(!x.is_nan()); @@ -93,6 +94,7 @@ fn test_towards_nearest() { } #[kani::proof] +#[kani::solver(minisat)] fn test_diff_half_one() { let x: f32 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/RInt/rintf64.rs b/tests/kani/Intrinsics/Math/Rounding/RInt/rintf64.rs index 8c8ea583a2d5..de608e7cdb9f 100644 --- a/tests/kani/Intrinsics/Math/Rounding/RInt/rintf64.rs +++ b/tests/kani/Intrinsics/Math/Rounding/RInt/rintf64.rs @@ -55,6 +55,7 @@ fn test_conc_sci() { } #[kani::proof] +#[kani::solver(minisat)] fn test_towards_nearest() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); @@ -93,6 +94,7 @@ fn test_towards_nearest() { } #[kani::proof] +#[kani::solver(minisat)] fn test_diff_half_one() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/Round/roundf32.rs b/tests/kani/Intrinsics/Math/Rounding/Round/roundf32.rs index 8a8780878925..04893727dcfa 100644 --- a/tests/kani/Intrinsics/Math/Rounding/Round/roundf32.rs +++ b/tests/kani/Intrinsics/Math/Rounding/Round/roundf32.rs @@ -39,6 +39,7 @@ fn test_conc_sci() { } #[kani::proof] +#[kani::solver(minisat)] fn test_towards_closer() { let x: f32 = kani::any(); kani::assume(!x.is_nan()); @@ -61,6 +62,7 @@ fn test_towards_closer() { } #[kani::proof] +#[kani::solver(minisat)] fn test_diff_half_one() { let x: f32 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/Round/roundf64.rs b/tests/kani/Intrinsics/Math/Rounding/Round/roundf64.rs index ddafc45a2e9e..cd61e9607646 100644 --- a/tests/kani/Intrinsics/Math/Rounding/Round/roundf64.rs +++ b/tests/kani/Intrinsics/Math/Rounding/Round/roundf64.rs @@ -39,6 +39,7 @@ fn test_conc_sci() { } #[kani::proof] +#[kani::solver(minisat)] fn test_towards_closer() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); @@ -61,6 +62,7 @@ fn test_towards_closer() { } #[kani::proof] +#[kani::solver(minisat)] fn test_diff_half_one() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/Trunc/truncf64.rs b/tests/kani/Intrinsics/Math/Rounding/Trunc/truncf64.rs index 5fcc8c80606d..a8a80672e36b 100644 --- a/tests/kani/Intrinsics/Math/Rounding/Trunc/truncf64.rs +++ b/tests/kani/Intrinsics/Math/Rounding/Trunc/truncf64.rs @@ -38,6 +38,7 @@ fn test_conc_sci() { } #[kani::proof] +#[kani::solver(minisat)] fn test_towards_zero() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); @@ -50,6 +51,7 @@ fn test_towards_zero() { } #[kani::proof] +#[kani::solver(minisat)] fn test_diff_one() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); From 114beea036b95b82ee7a5ca051b078aa3408919e Mon Sep 17 00:00:00 2001 From: Artem Agvanian Date: Thu, 25 Jul 2024 10:14:25 -0700 Subject: [PATCH 208/225] Better explanations and extra functionality in `MutableBody` (#3382) `MutableBody` is the core data structure for MIR manipulation in instrumentation passes. However, its public methods have limited documentation and unclear semantics. This slowed down the development of instrumentation passes. This PR aims to fix that by: - Clarifying how source instruction shifts when the methods are called; - Adding functionality for inserting basic blocks; - Expanding the support for different terminator types. 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/transform/body.rs | 159 ++++++++++++------ .../kani_middle/transform/check_uninit/mod.rs | 18 +- .../transform/check_uninit/uninit_visitor.rs | 2 +- .../src/kani_middle/transform/check_values.rs | 44 ++--- .../kani_middle/transform/kani_intrinsics.rs | 16 +- 5 files changed, 146 insertions(+), 93 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index d82dda80cc05..d3f2afc15d31 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -88,16 +88,20 @@ impl MutableBody { pub fn new_str_operand(&mut self, msg: &str, span: Span) -> Operand { let literal = MirConst::from_str(msg); - Operand::Constant(ConstOperand { span, user_ty: None, const_: literal }) + self.new_const_operand(literal, span) } - pub fn new_const_operand(&mut self, val: u128, uint_ty: UintTy, span: Span) -> Operand { + pub fn new_uint_operand(&mut self, val: u128, uint_ty: UintTy, span: Span) -> Operand { let literal = MirConst::try_from_uint(val, uint_ty).unwrap(); + self.new_const_operand(literal, span) + } + + fn new_const_operand(&mut self, literal: MirConst, span: Span) -> Operand { Operand::Constant(ConstOperand { span, user_ty: None, const_: literal }) } /// Create a raw pointer of `*mut type` and return a new local where that value is stored. - pub fn new_cast_ptr( + pub fn insert_ptr_cast( &mut self, from: Operand, pointee_ty: Ty, @@ -108,13 +112,13 @@ impl MutableBody { assert!(from.ty(self.locals()).unwrap().kind().is_raw_ptr()); let target_ty = Ty::new_ptr(pointee_ty, mutability); let rvalue = Rvalue::Cast(CastKind::PtrToPtr, from, target_ty); - self.new_assignment(rvalue, source, position) + self.insert_assignment(rvalue, source, position) } /// Add a new assignment for the given binary operation. /// /// Return the local where the result is saved. - pub fn new_binary_op( + pub fn insert_binary_op( &mut self, bin_op: BinOp, lhs: Operand, @@ -123,13 +127,13 @@ impl MutableBody { position: InsertPosition, ) -> Local { let rvalue = Rvalue::BinaryOp(bin_op, lhs, rhs); - self.new_assignment(rvalue, source, position) + self.insert_assignment(rvalue, source, position) } /// Add a new assignment. /// - /// Return local where the result is saved. - pub fn new_assignment( + /// Return the local where the result is saved. + pub fn insert_assignment( &mut self, rvalue: Rvalue, source: &mut SourceInstruction, @@ -146,9 +150,10 @@ impl MutableBody { /// Add a new assert to the basic block indicated by the given index. /// /// The new assertion will have the same span as the source instruction, and the basic block - /// will be split. The source instruction will be adjusted to point to the first instruction in - /// the new basic block. - pub fn add_check( + /// will be split. If `InsertPosition` is `InsertPosition::Before`, `source` will point to the + /// same instruction as before. If `InsertPosition` is `InsertPosition::After`, `source` will + /// point to the new terminator. + pub fn insert_check( &mut self, tcx: TyCtxt, check_type: &CheckType, @@ -183,7 +188,7 @@ impl MutableBody { unwind: UnwindAction::Terminate, }; let terminator = Terminator { kind, span }; - self.split_bb(source, position, terminator); + self.insert_terminator(source, position, terminator); } CheckType::Panic | CheckType::NoCore => { tcx.sess @@ -199,10 +204,11 @@ impl MutableBody { /// Add a new call to the basic block indicated by the given index. /// - /// The new call will have the same span as the source instruction, and the basic block - /// will be split. The source instruction will be adjusted to point to the first instruction in - /// the new basic block. - pub fn add_call( + /// The new call will have the same span as the source instruction, and the basic block will be + /// split. If `InsertPosition` is `InsertPosition::Before`, `source` will point to the same + /// instruction as before. If `InsertPosition` is `InsertPosition::After`, `source` will point + /// to the new terminator. + pub fn insert_call( &mut self, callee: &Instance, source: &mut SourceInstruction, @@ -222,13 +228,14 @@ impl MutableBody { unwind: UnwindAction::Terminate, }; let terminator = Terminator { kind, span }; - self.split_bb(source, position, terminator); + self.insert_terminator(source, position, terminator); } - /// Split a basic block and use the new terminator in the basic block that was split. - /// - /// The source is updated to point to the same instruction which is now in the new basic block. - pub fn split_bb( + /// Split a basic block and use the new terminator in the basic block that was split. If + /// `InsertPosition` is `InsertPosition::Before`, `source` will point to the same instruction as + /// before. If `InsertPosition` is `InsertPosition::After`, `source` will point to the new + /// terminator. + fn split_bb( &mut self, source: &mut SourceInstruction, position: InsertPosition, @@ -245,6 +252,7 @@ impl MutableBody { } /// Split a basic block right before the source location. + /// `source` will point to the same instruction as before after the function is done. fn split_bb_before(&mut self, source: &mut SourceInstruction, new_term: Terminator) { let new_bb_idx = self.blocks.len(); let (idx, bb) = match source { @@ -268,6 +276,7 @@ impl MutableBody { } /// Split a basic block right after the source location. + /// `source` will point to the new terminator after the function is done. fn split_bb_after(&mut self, source: &mut SourceInstruction, mut new_term: Terminator) { let new_bb_idx = self.blocks.len(); match source { @@ -275,39 +284,69 @@ impl MutableBody { // and move the remaining statements into the new one. SourceInstruction::Statement { idx, bb } => { let (orig_idx, orig_bb) = (*idx, *bb); - *idx = 0; - *bb = new_bb_idx; let old_term = mem::replace(&mut self.blocks[orig_bb].terminator, new_term); let bb_stmts = &mut self.blocks[orig_bb].statements; let remaining = bb_stmts.split_off(orig_idx + 1); let new_bb = BasicBlock { statements: remaining, terminator: old_term }; self.blocks.push(new_bb); + // Update the source to point at the terminator. + *source = SourceInstruction::Terminator { bb: orig_bb }; } // Make the terminator at `source` point at the new block, // the terminator of which is a simple Goto instruction. SourceInstruction::Terminator { bb } => { - let current_terminator = &mut self.blocks.get_mut(*bb).unwrap().terminator; - // Kani can only instrument function calls like this. - match (&mut current_terminator.kind, &mut new_term.kind) { - ( - TerminatorKind::Call { target: Some(target_bb), .. }, - TerminatorKind::Call { target: Some(new_target_bb), .. }, - ) => { - // Set the new terminator to point where previous terminator pointed. - *new_target_bb = *target_bb; - // Point the current terminator to the new terminator's basic block. - *target_bb = new_bb_idx; - // Update the current poisition. - *bb = new_bb_idx; - self.blocks.push(BasicBlock { statements: vec![], terminator: new_term }); - } - _ => unimplemented!("Kani can only split blocks after calls."), - }; + let current_term = &mut self.blocks.get_mut(*bb).unwrap().terminator; + let target_bb = get_mut_target_ref(current_term); + let new_target_bb = get_mut_target_ref(&mut new_term); + // Set the new terminator to point where previous terminator pointed. + *new_target_bb = *target_bb; + // Point the current terminator to the new terminator's basic block. + *target_bb = new_bb_idx; + // Update the source to point at the terminator. + *bb = new_bb_idx; + self.blocks.push(BasicBlock { statements: vec![], terminator: new_term }); } }; } - /// Insert statement before or after the source instruction and update the source as needed. + /// Insert basic block before or after the source instruction and update `source` accordingly. If + /// `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. + pub fn insert_bb( + &mut self, + mut bb: BasicBlock, + source: &mut SourceInstruction, + position: InsertPosition, + ) { + // Splitting adds 1 block, so the added block index is len + 1; + let split_bb_idx = self.blocks().len(); + let inserted_bb_idx = self.blocks().len() + 1; + // Update the terminator of the basic block to point at the remaining part of the split + // basic block. + let target = get_mut_target_ref(&mut bb.terminator); + *target = split_bb_idx; + let new_term = Terminator { + kind: TerminatorKind::Goto { target: inserted_bb_idx }, + span: source.span(&self.blocks), + }; + self.split_bb(source, position, new_term); + self.blocks.push(bb); + } + + pub fn insert_terminator( + &mut self, + source: &mut SourceInstruction, + position: InsertPosition, + terminator: Terminator, + ) { + self.split_bb(source, position, terminator); + } + + /// Insert statement before or after the source instruction and update the source as needed. If + /// `InsertPosition` is `InsertPosition::Before`, `source` will point to the same instruction as + /// before. If `InsertPosition` is `InsertPosition::After`, `source` will point to the + /// newly inserted statement. pub fn insert_stmt( &mut self, new_stmt: Statement, @@ -338,22 +377,18 @@ impl MutableBody { SourceInstruction::Terminator { bb } => { // Create a new basic block, as we need to append a statement after the terminator. let current_terminator = &mut self.blocks.get_mut(*bb).unwrap().terminator; - // Kani can only instrument function calls in this way. - match &mut current_terminator.kind { - TerminatorKind::Call { target: Some(target_bb), .. } => { - *source = SourceInstruction::Statement { idx: 0, bb: new_bb_idx }; - let new_bb = BasicBlock { - statements: vec![new_stmt], - terminator: Terminator { - kind: TerminatorKind::Goto { target: *target_bb }, - span, - }, - }; - *target_bb = new_bb_idx; - self.blocks.push(new_bb); - } - _ => unimplemented!("Kani can only insert statements after calls."), + // Update target of the terminator. + let target_bb = get_mut_target_ref(current_terminator); + *source = SourceInstruction::Statement { idx: 0, bb: new_bb_idx }; + let new_bb = BasicBlock { + statements: vec![new_stmt], + terminator: Terminator { + kind: TerminatorKind::Goto { target: *target_bb }, + span, + }, }; + *target_bb = new_bb_idx; + self.blocks.push(new_bb); } } } @@ -574,3 +609,15 @@ pub trait MutMirVisitor { } } } + +fn get_mut_target_ref(terminator: &mut Terminator) -> &mut BasicBlockIdx { + match &mut terminator.kind { + TerminatorKind::Assert { target, .. } + | TerminatorKind::Drop { target, .. } + | TerminatorKind::Goto { target } + | TerminatorKind::Call { target: Some(target), .. } => target, + _ => unimplemented!( + "Kani can only insert instructions after terminators that have a `target` field." + ), + } +} 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 4f94f94d17f1..f46c143f16d1 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/mod.rs @@ -230,7 +230,7 @@ impl UninitPass { *pointee_info.ty(), ); collect_skipped(&operation, body, skip_first); - body.add_call( + body.insert_call( &is_ptr_initialized_instance, source, operation.position(), @@ -257,7 +257,7 @@ impl UninitPass { let layout_operand = mk_layout_operand(body, source, operation.position(), &element_layout); collect_skipped(&operation, body, skip_first); - body.add_call( + body.insert_call( &is_ptr_initialized_instance, source, operation.position(), @@ -276,7 +276,7 @@ impl UninitPass { // Make sure all non-padding bytes are initialized. collect_skipped(&operation, body, skip_first); let ptr_operand_ty = ptr_operand.ty(body.locals()).unwrap(); - body.add_check( + body.insert_check( tcx, &self.check_type, source, @@ -345,7 +345,7 @@ impl UninitPass { *pointee_info.ty(), ); collect_skipped(&operation, body, skip_first); - body.add_call( + body.insert_call( &set_ptr_initialized_instance, source, operation.position(), @@ -372,7 +372,7 @@ impl UninitPass { let layout_operand = mk_layout_operand(body, source, operation.position(), &element_layout); collect_skipped(&operation, body, skip_first); - body.add_call( + body.insert_call( &set_ptr_initialized_instance, source, operation.position(), @@ -408,8 +408,8 @@ impl UninitPass { span, user_ty: None, })); - let result = body.new_assignment(rvalue, source, position); - body.add_check(tcx, &self.check_type, source, position, result, reason); + let result = body.insert_assignment(rvalue, source, position); + body.insert_check(tcx, &self.check_type, source, position, result, reason); } } @@ -432,7 +432,7 @@ pub fn mk_layout_operand( layout_byte_mask: &[bool], ) -> Operand { Operand::Move(Place { - local: body.new_assignment( + local: body.insert_assignment( Rvalue::Aggregate( AggregateKind::Array(Ty::bool_ty()), layout_byte_mask @@ -531,7 +531,7 @@ fn inject_memory_init_setup( ) .unwrap(); - new_body.add_call( + new_body.insert_call( &memory_initialization_init, &mut source, InsertPosition::Before, diff --git a/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs b/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs index 4c768aa2ee81..10a93b727a77 100644 --- a/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs +++ b/kani-compiler/src/kani_middle/transform/check_uninit/uninit_visitor.rs @@ -57,7 +57,7 @@ impl MemoryInitOp { Operand::Copy(place) | Operand::Move(place) => place, Operand::Constant(_) => unreachable!(), }; - body.new_assignment( + body.insert_assignment( Rvalue::AddressOf(Mutability::Not, place.clone()), source, self.position(), diff --git a/kani-compiler/src/kani_middle/transform/check_values.rs b/kani-compiler/src/kani_middle/transform/check_values.rs index a7d0f14d270f..a81f76d25393 100644 --- a/kani-compiler/src/kani_middle/transform/check_values.rs +++ b/kani-compiler/src/kani_middle/transform/check_values.rs @@ -85,13 +85,13 @@ impl ValidValuePass { for operation in instruction.operations { match operation { SourceOp::BytesValidity { ranges, target_ty, rvalue } => { - let value = body.new_assignment(rvalue, &mut source, InsertPosition::Before); + let value = body.insert_assignment(rvalue, &mut source, InsertPosition::Before); let rvalue_ptr = Rvalue::AddressOf(Mutability::Not, Place::from(value)); for range in ranges { let result = build_limits(body, &range, rvalue_ptr.clone(), &mut source); let msg = format!("Undefined Behavior: Invalid value of type `{target_ty}`",); - body.add_check( + body.insert_check( tcx, &self.check_type, &mut source, @@ -106,7 +106,7 @@ impl ValidValuePass { let result = build_limits(body, &range, rvalue.clone(), &mut source); let msg = format!("Undefined Behavior: Invalid value of type `{pointee_ty}`",); - body.add_check( + body.insert_check( tcx, &self.check_type, &mut source, @@ -139,8 +139,8 @@ impl ValidValuePass { span, user_ty: None, })); - let result = body.new_assignment(rvalue, source, InsertPosition::Before); - body.add_check(tcx, &self.check_type, source, InsertPosition::Before, result, reason); + let result = body.insert_assignment(rvalue, source, InsertPosition::Before); + body.insert_check(tcx, &self.check_type, source, InsertPosition::Before, result, reason); } } @@ -771,24 +771,25 @@ pub fn build_limits( let span = source.span(body.blocks()); debug!(?req, ?rvalue_ptr, ?span, "build_limits"); let primitive_ty = uint_ty(req.size.bytes()); - let start_const = body.new_const_operand(req.valid_range.start, primitive_ty, span); - let end_const = body.new_const_operand(req.valid_range.end, primitive_ty, span); + let start_const = body.new_uint_operand(req.valid_range.start, primitive_ty, span); + let end_const = body.new_uint_operand(req.valid_range.end, primitive_ty, span); let orig_ptr = if req.offset != 0 { - let start_ptr = move_local(body.new_assignment(rvalue_ptr, source, InsertPosition::Before)); - let byte_ptr = move_local(body.new_cast_ptr( + let start_ptr = + move_local(body.insert_assignment(rvalue_ptr, source, InsertPosition::Before)); + let byte_ptr = move_local(body.insert_ptr_cast( start_ptr, Ty::unsigned_ty(UintTy::U8), Mutability::Not, source, InsertPosition::Before, )); - let offset_const = body.new_const_operand(req.offset as _, UintTy::Usize, span); - let offset = move_local(body.new_assignment( + let offset_const = body.new_uint_operand(req.offset as _, UintTy::Usize, span); + let offset = move_local(body.insert_assignment( Rvalue::Use(offset_const), source, InsertPosition::Before, )); - move_local(body.new_binary_op( + move_local(body.insert_binary_op( BinOp::Offset, byte_ptr, offset, @@ -796,9 +797,9 @@ pub fn build_limits( InsertPosition::Before, )) } else { - move_local(body.new_assignment(rvalue_ptr, source, InsertPosition::Before)) + move_local(body.insert_assignment(rvalue_ptr, source, InsertPosition::Before)) }; - let value_ptr = body.new_cast_ptr( + let value_ptr = body.insert_ptr_cast( orig_ptr, Ty::unsigned_ty(primitive_ty), Mutability::Not, @@ -806,13 +807,18 @@ pub fn build_limits( InsertPosition::Before, ); let value = Operand::Copy(Place { local: value_ptr, projection: vec![ProjectionElem::Deref] }); - let start_result = - body.new_binary_op(BinOp::Ge, value.clone(), start_const, source, InsertPosition::Before); + let start_result = body.insert_binary_op( + BinOp::Ge, + value.clone(), + start_const, + source, + InsertPosition::Before, + ); let end_result = - body.new_binary_op(BinOp::Le, value, end_const, source, InsertPosition::Before); + body.insert_binary_op(BinOp::Le, value, end_const, source, InsertPosition::Before); if req.valid_range.wraps_around() { // valid >= start || valid <= end - body.new_binary_op( + body.insert_binary_op( BinOp::BitOr, move_local(start_result), move_local(end_result), @@ -821,7 +827,7 @@ pub fn build_limits( ) } else { // valid >= start && valid <= end - body.new_binary_op( + body.insert_binary_op( BinOp::BitAnd, move_local(start_result), move_local(end_result), diff --git a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs index c4534bf11b4d..d6475465d1b1 100644 --- a/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs +++ b/kani-compiler/src/kani_middle/transform/kani_intrinsics.rs @@ -136,11 +136,11 @@ impl IntrinsicGeneratorPass { user_ty: None, })); let result = - new_body.new_assignment(rvalue, &mut terminator, InsertPosition::Before); + 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.add_check( + new_body.insert_check( tcx, &self.check_type, &mut terminator, @@ -212,7 +212,7 @@ impl IntrinsicGeneratorPass { InsertPosition::Before, &layout, ); - new_body.add_call( + new_body.insert_call( &is_ptr_initialized_instance, &mut terminator, InsertPosition::Before, @@ -242,7 +242,7 @@ impl IntrinsicGeneratorPass { InsertPosition::Before, &element_layout, ); - new_body.add_call( + new_body.insert_call( &is_ptr_initialized_instance, &mut terminator, InsertPosition::Before, @@ -256,14 +256,14 @@ impl IntrinsicGeneratorPass { span, user_ty: None, })); - let result = new_body.new_assignment( + let result = new_body.insert_assignment( rvalue, &mut terminator, InsertPosition::Before, ); let reason: &str = "Kani does not support reasoning about memory initialization of pointers to trait objects."; - new_body.add_check( + new_body.insert_check( tcx, &self.check_type, &mut terminator, @@ -282,11 +282,11 @@ impl IntrinsicGeneratorPass { user_ty: None, })); let result = - new_body.new_assignment(rvalue, &mut terminator, InsertPosition::Before); + new_body.insert_assignment(rvalue, &mut terminator, InsertPosition::Before); let reason = format!( "Kani currently doesn't support checking memory initialization of `{target_ty}`. {msg}" ); - new_body.add_check( + new_body.insert_check( tcx, &self.check_type, &mut terminator, From b18698f0e01b9d7d678a0ced8c2553a8d56e7767 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Thu, 25 Jul 2024 10:58:31 -0700 Subject: [PATCH 209/225] Add missing functionalities to kani_core (#3384) - Add Arbitrary for array - Add Arbitrary for tuples - Add missing changes from modifies slices (#3295) Note that for adding `any_array` I had to cleanup the unnecessary usage of constant parameters from `kani::any_raw`. --- library/kani/src/arbitrary.rs | 24 +---- library/kani/src/concrete_playback.rs | 12 +-- library/kani/src/lib.rs | 17 ++-- library/kani/src/vec.rs | 2 - library/kani_core/src/arbitrary.rs | 70 ++++++++------ library/kani_core/src/lib.rs | 93 +++++++++++++------ .../verify_std_cmd/verify_core.rs | 75 +++++++++++++++ .../verify_std_cmd/verify_std.expected | 8 +- .../verify_std_cmd/verify_std.sh | 42 +-------- 9 files changed, 209 insertions(+), 134 deletions(-) create mode 100644 tests/script-based-pre/verify_std_cmd/verify_core.rs diff --git a/library/kani/src/arbitrary.rs b/library/kani/src/arbitrary.rs index 424ca2485d57..ef6e2ef23dd4 100644 --- a/library/kani/src/arbitrary.rs +++ b/library/kani/src/arbitrary.rs @@ -16,12 +16,7 @@ where Self: Sized, { fn any() -> Self; - fn any_array() -> [Self; MAX_ARRAY_LENGTH] - // the requirement defined in the where clause must appear on the `impl`'s method `any_array` - // but also on the corresponding trait's method - where - [(); std::mem::size_of::<[Self; MAX_ARRAY_LENGTH]>()]:, - { + fn any_array() -> [Self; MAX_ARRAY_LENGTH] { [(); MAX_ARRAY_LENGTH].map(|_| Self::any()) } } @@ -33,20 +28,10 @@ macro_rules! trivial_arbitrary { #[inline(always)] fn any() -> Self { // This size_of call does not use generic_const_exprs feature. It's inside a macro, and Self isn't generic. - unsafe { crate::any_raw_internal::() }>() } + unsafe { crate::any_raw_internal::() } } - fn any_array() -> [Self; MAX_ARRAY_LENGTH] - where - // `generic_const_exprs` requires all potential errors to be reflected in the signature/header. - // We must repeat the expression in the header, to make sure that if the body can fail the header will also fail. - [(); { std::mem::size_of::<[$type; MAX_ARRAY_LENGTH]>() }]:, - { - unsafe { - crate::any_raw_internal::< - [Self; MAX_ARRAY_LENGTH], - { std::mem::size_of::<[Self; MAX_ARRAY_LENGTH]>() }, - >() - } + fn any_array() -> [Self; MAX_ARRAY_LENGTH] { + unsafe { crate::any_raw_internal::<[Self; MAX_ARRAY_LENGTH]>() } } } }; @@ -128,7 +113,6 @@ nonzero_arbitrary!(NonZeroIsize, isize); impl Arbitrary for [T; N] where T: Arbitrary, - [(); std::mem::size_of::<[T; N]>()]:, { fn any() -> Self { T::any_array() diff --git a/library/kani/src/concrete_playback.rs b/library/kani/src/concrete_playback.rs index 711b9b005624..aa6cd7e4018d 100644 --- a/library/kani/src/concrete_playback.rs +++ b/library/kani/src/concrete_playback.rs @@ -47,19 +47,17 @@ pub fn concrete_playback_run(mut local_concrete_vals: Vec>, pro /// # Safety /// /// The semantics of this function require that SIZE_T equals the size of type T. -pub(crate) unsafe fn any_raw_internal() -> T { +pub(crate) unsafe fn any_raw_internal() -> T { + let sz = size_of::(); let mut next_concrete_val: Vec = Vec::new(); CONCRETE_VALS.with(|glob_concrete_vals| { let mut_ref_glob_concrete_vals = &mut *glob_concrete_vals.borrow_mut(); - next_concrete_val = if SIZE_T > 0 { + next_concrete_val = if sz > 0 { mut_ref_glob_concrete_vals.pop().expect("Not enough det vals found") } else { vec![] }; }); - let next_concrete_val_len = next_concrete_val.len(); - let bytes_t: [u8; SIZE_T] = next_concrete_val.try_into().expect(&format!( - "Expected {SIZE_T} bytes instead of {next_concrete_val_len} bytes in the following det vals vec" - )); - std::mem::transmute_copy::<[u8; SIZE_T], T>(&bytes_t) + assert_eq!(next_concrete_val.len(), sz, "Expected {sz} bytes in the following det vals vec"); + unsafe { *(next_concrete_val.as_ptr() as *mut T) } } diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index 7487cc26b186..3cf46bd7af07 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -7,11 +7,10 @@ // Used for rustc_diagnostic_item. // Note: We could use a kanitool attribute instead. #![feature(rustc_attrs)] -// This is required for the optimized version of `any_array()` -#![feature(generic_const_exprs)] -#![allow(incomplete_features)] // Used to model simd. #![feature(repr_simd)] +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] // Features used for tests only. #![cfg_attr(test, feature(core_intrinsics, portable_simd))] // Required for `rustc_diagnostic_item` and `core_intrinsics` @@ -21,6 +20,9 @@ #![feature(f16)] #![feature(f128)] +// Allow us to use `kani::` to access crate features. +extern crate self as kani; + pub mod arbitrary; #[cfg(feature = "concrete_playback")] mod concrete_playback; @@ -48,6 +50,7 @@ pub use invariant::Invariant; pub fn concrete_playback_run(_: Vec>, _: F) { unreachable!("Concrete playback does not work during verification") } + pub use futures::{block_on, block_on_with_spawn, spawn, yield_now, RoundRobin}; /// Creates an assumption that will be valid after this statement run. Note that the assumption @@ -246,21 +249,21 @@ pub fn any_where bool>(f: F) -> T { /// Note that SIZE_T must be equal the size of type T in bytes. #[inline(never)] #[cfg(not(feature = "concrete_playback"))] -pub(crate) unsafe fn any_raw_internal() -> T { +pub(crate) unsafe fn any_raw_internal() -> T { any_raw_inner::() } #[inline(never)] #[cfg(feature = "concrete_playback")] -pub(crate) unsafe fn any_raw_internal() -> T { - concrete_playback::any_raw_internal::() +pub(crate) unsafe fn any_raw_internal() -> T { + concrete_playback::any_raw_internal::() } /// This low-level function returns nondet bytes of size T. #[rustc_diagnostic_item = "KaniAnyRaw"] #[inline(never)] #[allow(dead_code)] -fn any_raw_inner() -> T { +fn any_raw_inner() -> T { kani_intrinsic() } diff --git a/library/kani/src/vec.rs b/library/kani/src/vec.rs index 626d152f02d4..a3ec05a9c953 100644 --- a/library/kani/src/vec.rs +++ b/library/kani/src/vec.rs @@ -6,7 +6,6 @@ use crate::{any, any_where, Arbitrary}; pub fn any_vec() -> Vec where T: Arbitrary, - [(); std::mem::size_of::<[T; MAX_LENGTH]>()]:, { let real_length: usize = any_where(|sz| *sz <= MAX_LENGTH); match real_length { @@ -26,7 +25,6 @@ where pub fn exact_vec() -> Vec where T: Arbitrary, - [(); std::mem::size_of::<[T; EXACT_LENGTH]>()]:, { let boxed_array: Box<[T; EXACT_LENGTH]> = Box::new(any()); <[T]>::into_vec(boxed_array) diff --git a/library/kani_core/src/arbitrary.rs b/library/kani_core/src/arbitrary.rs index a8271ad758cf..7cfb649b11a0 100644 --- a/library/kani_core/src/arbitrary.rs +++ b/library/kani_core/src/arbitrary.rs @@ -8,6 +8,7 @@ //! //! TODO: Use this inside kani library so that we dont have to maintain two copies of the same proc macro for arbitrary. #[macro_export] +#[allow(clippy::crate_in_macro_def)] macro_rules! generate_arbitrary { ($core:path) => { use core_path::marker::{PhantomData, PhantomPinned}; @@ -18,13 +19,7 @@ macro_rules! generate_arbitrary { Self: Sized, { fn any() -> Self; - #[cfg(kani_sysroot)] - fn any_array() -> [Self; MAX_ARRAY_LENGTH] - // the requirement defined in the where clause must appear on the `impl`'s method `any_array` - // but also on the corresponding trait's method - where - [(); core_path::mem::size_of::<[Self; MAX_ARRAY_LENGTH]>()]:, - { + fn any_array() -> [Self; MAX_ARRAY_LENGTH] { [(); MAX_ARRAY_LENGTH].map(|_| Self::any()) } } @@ -36,22 +31,10 @@ macro_rules! generate_arbitrary { #[inline(always)] fn any() -> Self { // This size_of call does not use generic_const_exprs feature. It's inside a macro, and Self isn't generic. - unsafe { any_raw_internal::() }>() } + unsafe { crate::kani::any_raw_internal::() } } - // Disable this for standard library since we cannot enable generic constant expr. - #[cfg(kani_sysroot)] - fn any_array() -> [Self; MAX_ARRAY_LENGTH] - where - // `generic_const_exprs` requires all potential errors to be reflected in the signature/header. - // We must repeat the expression in the header, to make sure that if the body can fail the header will also fail. - [(); { core_path::mem::size_of::<[$type; MAX_ARRAY_LENGTH]>() }]:, - { - unsafe { - any_raw_internal::< - [Self; MAX_ARRAY_LENGTH], - { core_path::mem::size_of::<[Self; MAX_ARRAY_LENGTH]>() }, - >() - } + fn any_array() -> [Self; MAX_ARRAY_LENGTH] { + unsafe { crate::kani::any_raw_internal::<[Self; MAX_ARRAY_LENGTH]>() } } } }; @@ -134,6 +117,15 @@ macro_rules! generate_arbitrary { } } + impl Arbitrary for [T; N] + where + T: Arbitrary, + { + fn any() -> Self { + T::any_array::() + } + } + impl Arbitrary for Option where T: Arbitrary, @@ -165,15 +157,33 @@ macro_rules! generate_arbitrary { } } - #[cfg(kani_sysroot)] - impl Arbitrary for [T; N] - where - T: Arbitrary, - [(); core_path::mem::size_of::<[T; N]>()]:, - { + arbitrary_tuple!(A); + arbitrary_tuple!(A, B); + arbitrary_tuple!(A, B, C); + arbitrary_tuple!(A, B, C, D); + arbitrary_tuple!(A, B, C, D, E); + arbitrary_tuple!(A, B, C, D, E, F); + arbitrary_tuple!(A, B, C, D, E, F, G); + arbitrary_tuple!(A, B, C, D, E, F, G, H); + arbitrary_tuple!(A, B, C, D, E, F, G, H, I); + arbitrary_tuple!(A, B, C, D, E, F, G, H, I, J); + arbitrary_tuple!(A, B, C, D, E, F, G, H, I, J, K); + arbitrary_tuple!(A, B, C, D, E, F, G, H, I, J, K, L); + }; +} + +/// This macro implements `kani::Arbitrary` on a tuple whose elements +/// already implement `kani::Arbitrary` by running `kani::any()` on +/// each index of the tuple. +#[allow(clippy::crate_in_macro_def)] +#[macro_export] +macro_rules! arbitrary_tuple { + ($($type:ident),*) => { + impl<$($type : Arbitrary),*> Arbitrary for ($($type,)*) { + #[inline(always)] fn any() -> Self { - T::any_array() + ($(crate::kani::any::<$type>(),)*) } } - }; + } } diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index a07ba7ca8ec4..016c805e8f8e 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -60,6 +60,7 @@ macro_rules! kani_lib { /// such as core in rust's std library itself. /// /// TODO: Use this inside kani library so that we dont have to maintain two copies of the same intrinsics. +#[allow(clippy::crate_in_macro_def)] #[macro_export] macro_rules! kani_intrinsics { ($core:tt) => { @@ -180,7 +181,7 @@ macro_rules! kani_intrinsics { /// under all possible `NonZeroU8` input values, i.e., all possible `u8` values except zero. /// /// ```rust - /// let inputA = kani::any::(); + /// let inputA = kani::any::(); /// fn_under_verification(inputA); /// ``` /// @@ -247,21 +248,21 @@ macro_rules! kani_intrinsics { /// Note that SIZE_T must be equal the size of type T in bytes. #[inline(never)] #[cfg(not(feature = "concrete_playback"))] - pub(crate) unsafe fn any_raw_internal() -> T { + pub(crate) unsafe fn any_raw_internal() -> T { any_raw_inner::() } #[inline(never)] #[cfg(feature = "concrete_playback")] - pub(crate) unsafe fn any_raw_internal() -> T { - concrete_playback::any_raw_internal::() + pub(crate) unsafe fn any_raw_internal() -> T { + concrete_playback::any_raw_internal::() } /// This low-level function returns nondet bytes of size T. #[rustc_diagnostic_item = "KaniAnyRaw"] #[inline(never)] #[allow(dead_code)] - pub fn any_raw_inner() -> T { + pub fn any_raw_inner() -> T { kani_intrinsic() } @@ -269,7 +270,7 @@ macro_rules! kani_intrinsics { /// supported by Kani display. /// /// During verification this will get replaced by `assert(false)`. For concrete executions, we just - /// invoke the regular `std::panic!()` function. This function is used by our standard library + /// invoke the regular `core::panic!()` function. This function is used by our standard library /// overrides, but not the other way around. #[inline(never)] #[rustc_diagnostic_item = "KaniPanic"] @@ -294,6 +295,8 @@ macro_rules! kani_intrinsics { } pub mod internal { + use crate::kani::Arbitrary; + use core::ptr; /// Helper trait for code generation for `modifies` contracts. /// @@ -301,7 +304,7 @@ macro_rules! kani_intrinsics { #[doc(hidden)] pub trait Pointer<'a> { /// Type of the pointed-to data - type Inner; + type Inner: ?Sized; /// Used for checking assigns contracts where we pass immutable references to the function. /// @@ -309,56 +312,52 @@ macro_rules! kani_intrinsics { /// argument, for instance one of type `&mut _`, in the `modifies` clause which would move it. unsafe fn decouple_lifetime(&self) -> &'a Self::Inner; - /// used for havocking on replecement of a `modifies` clause. - unsafe fn assignable(self) -> &'a mut Self::Inner; + unsafe fn assignable(self) -> *mut Self::Inner; } - impl<'a, 'b, T> Pointer<'a> for &'b T { + impl<'a, 'b, T: ?Sized> Pointer<'a> for &'b T { type Inner = T; unsafe fn decouple_lifetime(&self) -> &'a Self::Inner { - $core::mem::transmute(*self) + core::mem::transmute(*self) } - #[allow(clippy::transmute_ptr_to_ref)] - unsafe fn assignable(self) -> &'a mut Self::Inner { - $core::mem::transmute(self as *const T) + unsafe fn assignable(self) -> *mut Self::Inner { + core::mem::transmute(self as *const T) } } - impl<'a, 'b, T> Pointer<'a> for &'b mut T { + impl<'a, 'b, T: ?Sized> Pointer<'a> for &'b mut T { type Inner = T; #[allow(clippy::transmute_ptr_to_ref)] unsafe fn decouple_lifetime(&self) -> &'a Self::Inner { - $core::mem::transmute::<_, &&'a T>(self) + core::mem::transmute::<_, &&'a T>(self) } - unsafe fn assignable(self) -> &'a mut Self::Inner { - $core::mem::transmute(self) + unsafe fn assignable(self) -> *mut Self::Inner { + self as *mut T } } - impl<'a, T> Pointer<'a> for *const T { + impl<'a, T: ?Sized> Pointer<'a> for *const T { type Inner = T; unsafe fn decouple_lifetime(&self) -> &'a Self::Inner { &**self as &'a T } - #[allow(clippy::transmute_ptr_to_ref)] - unsafe fn assignable(self) -> &'a mut Self::Inner { - $core::mem::transmute(self) + unsafe fn assignable(self) -> *mut Self::Inner { + core::mem::transmute(self) } } - impl<'a, T> Pointer<'a> for *mut T { + impl<'a, T: ?Sized> Pointer<'a> for *mut T { type Inner = T; unsafe fn decouple_lifetime(&self) -> &'a Self::Inner { &**self as &'a T } - #[allow(clippy::transmute_ptr_to_ref)] - unsafe fn assignable(self) -> &'a mut Self::Inner { - $core::mem::transmute(self) + unsafe fn assignable(self) -> *mut Self::Inner { + self } } @@ -391,6 +390,48 @@ macro_rules! kani_intrinsics { pub fn apply_closure bool>(f: U, x: &T) -> bool { f(x) } + + /// Recieves a reference to a pointer-like object and assigns kani::any_modifies to that object. + /// Only for use within function contracts and will not be replaced if the recursive or function stub + /// replace contracts are not used. + #[rustc_diagnostic_item = "KaniWriteAny"] + #[inline(never)] + #[doc(hidden)] + pub unsafe fn write_any(_pointer: *mut T) { + // This function should not be reacheable. + // Users must include `#[kani::recursion]` in any function contracts for recursive functions; + // otherwise, this might not be properly instantiate. We mark this as unreachable to make + // sure Kani doesn't report any false positives. + unreachable!() + } + + /// Fill in a slice with kani::any. + /// Intended as a post compilation replacement for write_any + #[rustc_diagnostic_item = "KaniWriteAnySlice"] + #[inline(always)] + pub unsafe fn write_any_slice(slice: *mut [T]) { + (*slice).fill_with(T::any) + } + + /// Fill in a pointer with kani::any. + /// Intended as a post compilation replacement for write_any + #[rustc_diagnostic_item = "KaniWriteAnySlim"] + #[inline(always)] + pub unsafe fn write_any_slim(pointer: *mut T) { + ptr::write(pointer, T::any()) + } + + /// Fill in a str with kani::any. + /// Intended as a post compilation replacement for write_any. + /// Not yet implemented + #[rustc_diagnostic_item = "KaniWriteAnyStr"] + #[inline(always)] + pub unsafe fn write_any_str(_s: *mut str) { + //TODO: strings introduce new UB + //(*s).as_bytes_mut().fill_with(u8::any) + //TODO: String validation + unimplemented!("Kani does not support creating arbitrary `str`") + } } }; } diff --git a/tests/script-based-pre/verify_std_cmd/verify_core.rs b/tests/script-based-pre/verify_std_cmd/verify_core.rs new file mode 100644 index 000000000000..28cf113a9210 --- /dev/null +++ b/tests/script-based-pre/verify_std_cmd/verify_core.rs @@ -0,0 +1,75 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +/// Dummy code that gets injected into `core` for basic tests for `verify-std` subcommand. +#[cfg(kani)] +kani_core::kani_lib!(core); + +#[cfg(kani)] +#[unstable(feature = "kani", issue = "none")] +pub mod verify { + use crate::kani; + use core::num::NonZeroU8; + + #[kani::proof] + pub fn harness() { + kani::assert(true, "yay"); + } + + #[kani::proof_for_contract(fake_function)] + fn dummy_proof() { + fake_function(true); + } + + /// Add a `rustc_diagnostic_item` to ensure this works. + /// See for more details. + #[kani::requires(x == true)] + #[rustc_diagnostic_item = "fake_function"] + fn fake_function(x: bool) -> bool { + x + } + + #[kani::proof_for_contract(dummy_read)] + fn check_dummy_read() { + let val: char = kani::any(); + assert_eq!(unsafe { dummy_read(&val) }, val); + } + + /// Ensure we can verify constant functions. + #[kani::requires(kani::mem::can_dereference(ptr))] + #[rustc_diagnostic_item = "dummy_read"] + const unsafe fn dummy_read(ptr: *const T) -> T { + *ptr + } + + #[kani::proof_for_contract(swap_tuple)] + fn check_swap_tuple() { + let initial: (char, NonZeroU8) = kani::any(); + let _swapped = swap_tuple(initial); + } + + #[kani::ensures(| result | result.0 == second && result.1 == first)] + fn swap_tuple((first, second): (char, NonZeroU8)) -> (NonZeroU8, char) { + (second, first) + } + + #[kani::proof_for_contract(add_one)] + fn check_add_one() { + let mut initial: [u32; 4] = kani::any(); + unsafe { add_one(&mut initial) }; + } + + /// Function with a more elaborated contract that uses `old` and `modifies`. + #[kani::modifies(inout)] + #[kani::requires(kani::mem::can_dereference(inout) + && unsafe { inout.as_ref_unchecked().iter().all(| i | * i < u32::MAX) })] + #[kani::requires(kani::mem::can_write(inout))] + #[kani::ensures(| result | { + let (orig, i) = old({ + let i = kani::any_where(| i: & usize | * i < unsafe { inout.len() }); + (unsafe { inout.as_ref_unchecked()[i] }, i)}); + unsafe { inout.as_ref_unchecked()[i] > orig } + })] + unsafe fn add_one(inout: *mut [u32]) { + inout.as_mut_unchecked().iter_mut().for_each(|e| *e += 1) + } +} diff --git a/tests/script-based-pre/verify_std_cmd/verify_std.expected b/tests/script-based-pre/verify_std_cmd/verify_std.expected index aa30da375dd3..22e8fb2e6375 100644 --- a/tests/script-based-pre/verify_std_cmd/verify_std.expected +++ b/tests/script-based-pre/verify_std_cmd/verify_std.expected @@ -9,7 +9,13 @@ VERIFICATION:- SUCCESSFUL Checking harness verify::check_dummy_read... VERIFICATION:- SUCCESSFUL +Checking harness verify::check_add_one... +VERIFICATION:- SUCCESSFUL + +Checking harness verify::check_swap_tuple... +VERIFICATION:- SUCCESSFUL + Checking harness num::verify::check_non_zero... VERIFICATION:- SUCCESSFUL -Complete - 4 successfully verified harnesses, 0 failures, 4 total. +Complete - 6 successfully verified harnesses, 0 failures, 6 total. diff --git a/tests/script-based-pre/verify_std_cmd/verify_std.sh b/tests/script-based-pre/verify_std_cmd/verify_std.sh index 3253ad29756e..91454c4b65e7 100755 --- a/tests/script-based-pre/verify_std_cmd/verify_std.sh +++ b/tests/script-based-pre/verify_std_cmd/verify_std.sh @@ -21,47 +21,7 @@ STD_PATH="${SYSROOT}/lib/rustlib/src/rust/library" cp -r "${STD_PATH}" "${TMP_DIR}" # Insert a small harness in one of the standard library modules. -CORE_CODE=' -#[cfg(kani)] -kani_core::kani_lib!(core); - -#[cfg(kani)] -#[unstable(feature = "kani", issue = "none")] -pub mod verify { - use crate::kani; - - #[kani::proof] - pub fn harness() { - kani::assert(true, "yay"); - } - - #[kani::proof_for_contract(fake_function)] - fn dummy_proof() { - fake_function(true); - } - - /// Add a `rustc_diagnostic_item` to ensure this works. - /// See for more details. - #[kani::requires(x == true)] - #[rustc_diagnostic_item = "fake_function"] - fn fake_function(x: bool) -> bool { - x - } - - #[kani::proof_for_contract(dummy_read)] - fn check_dummy_read() { - let val: char = kani::any(); - assert_eq!(unsafe { dummy_read(&val) }, val); - } - - /// Ensure we can verify constant functions. - #[kani::requires(kani::mem::can_dereference(ptr))] - #[rustc_diagnostic_item = "dummy_read"] - const unsafe fn dummy_read(ptr: *const T) -> T { - *ptr - } -} -' +CORE_CODE=$(cat verify_core.rs) STD_CODE=' #[cfg(kani)] From 70bd0215da3183db04796c93f070d805cc93343b Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Fri, 26 Jul 2024 11:04:59 -0700 Subject: [PATCH 210/225] Add two new CI workflows to check Kani against `verify-rust-std` (#3386) This PR adds the following workflows: 1. `verify-std-check`: This workflow will run either in a PR or via workflow call. It pulls the `main` branch of `verify-rust-std` repository, and see if it can verify the repo with the new Kani version from HEAD. - If verification succeeds, the workflow will succeed. - If the verification fails and this is a PR, the workflow will compare the verification result against the base of the PR. The workflow will succeed if the results match*. 2. `verify-std-update`: This workflow will run the `verify-std-check` workflow. If it succeeds, it will update the `verify-rust-std` branch from HEAD. This workflow will fail if `features/verify-rust-std` branch diverges, and a merge is required. The motivation for this PR is to help us identify any changes to Kani that may break the Rust verification repository, and to keep the `verify-rust-std` up to date as much as possible. * The fallback logic is needed since toolchain upgrades can potentially break the std verification. This will happen in cases where the new toolchain version is incompatible with the library version from the `verify-rust-std`. Those cases should not interfere with Kani development, and they should be fixed when we update the `verify-rust-std` repo. --- .github/workflows/verify-std-check.yml | 86 +++++++++++++++++++++++++ .github/workflows/verify-std-update.yml | 35 ++++++++++ 2 files changed, 121 insertions(+) create mode 100644 .github/workflows/verify-std-check.yml create mode 100644 .github/workflows/verify-std-update.yml diff --git a/.github/workflows/verify-std-check.yml b/.github/workflows/verify-std-check.yml new file mode 100644 index 000000000000..667be6b340c5 --- /dev/null +++ b/.github/workflows/verify-std-check.yml @@ -0,0 +1,86 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# This workflow will try to build and run the `verify-rust-std` repository. +# +# This check should be optional, but it has been added to help provide +# visibility to when a PR may break the `verify-rust-std` repository. +# +# We expect toolchain updates to potentially break this workflow in cases where +# the version tracked in the `verify-rust-std` is not compatible with newer +# versions of the Rust toolchain. +# +# Changes unrelated to the toolchain should match the current status of main. + +name: Check Std Verification +on: + pull_request: + workflow_call: + +env: + RUST_BACKTRACE: 1 + +jobs: + verify-std: + runs-on: ${{ matrix.os }} + strategy: + matrix: + # Kani does not support windows. + os: [ ubuntu-22.04, macos-14 ] + steps: + - name: Checkout Library + uses: actions/checkout@v4 + with: + repository: model-checking/verify-rust-std + path: verify-rust-std + submodules: true + + - name: Checkout `Kani` + uses: actions/checkout@v4 + with: + path: kani + + - name: Setup Kani Dependencies + uses: ./kani/.github/actions/setup + with: + os: ${{ matrix.os }} + kani_dir: kani + + - name: Build Kani + working-directory: kani + run: | + cargo build-dev + echo "$(pwd)/scripts" >> $GITHUB_PATH + + - name: Run verification with HEAD + id: check-head + working-directory: verify-rust-std + continue-on-error: true + run: | + kani verify-std -Z unstable-options ./library --target-dir ${{ runner.temp }} -Z function-contracts \ + -Z mem-predicates -Z ptr-to-ref-cast-checks + + # If the head failed, check if it's a new failure. + - 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} + cargo build-dev + + - name: Run verification with BASE + id: check-base + if: steps.check-head.outcome != 'success' && github.event_name == 'pull_request' + working-directory: verify-rust-std + continue-on-error: true + run: | + kani verify-std -Z unstable-options ./library --target-dir ${{ runner.temp }} -Z function-contracts \ + -Z mem-predicates -Z ptr-to-ref-cast-checks + + - name: Compare PR results + if: steps.check-head.outcome != 'success' && steps.check-head.output != github.check-base.output + run: | + echo "New failure introduced by this change" + exit 1 + diff --git a/.github/workflows/verify-std-update.yml b/.github/workflows/verify-std-update.yml new file mode 100644 index 000000000000..518f80ecd1b8 --- /dev/null +++ b/.github/workflows/verify-std-update.yml @@ -0,0 +1,35 @@ +# 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 e4f009978575babe69e341567877e5d23aeddc18 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 11:56:52 +0200 Subject: [PATCH 211/225] Automatic cargo update to 2024-07-29 (#3390) Dependency upgrade resulting from `cargo update`. --- Cargo.lock | 73 +++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0fd31f73278b..01dbccdd546a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,9 +25,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", @@ -40,33 +40,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys", @@ -140,9 +140,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.9" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" +checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" dependencies = [ "clap_builder", "clap_derive", @@ -150,9 +150,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.9" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" +checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" dependencies = [ "anstream", "anstyle", @@ -162,9 +162,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.8" +version = "4.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" dependencies = [ "heck", "proc-macro2", @@ -174,15 +174,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "comfy-table" @@ -384,9 +384,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" @@ -929,20 +929,21 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -1098,9 +1099,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28" +checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c" dependencies = [ "serde", "serde_spanned", @@ -1110,18 +1111,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.16" +version = "0.22.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" +checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16" dependencies = [ "indexmap", "serde", @@ -1237,9 +1238,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wait-timeout" @@ -1384,9 +1385,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.14" +version = "0.6.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374ec40a2d767a3c1b4972d9475ecd557356637be906f2cb3f7fe17a6eb5e22f" +checksum = "b480ae9340fc261e6be3e95a1ba86d54ae3f9171132a73ce8d4bbaf68339507c" dependencies = [ "memchr", ] From 91d182953b46b164daf657641f9be78979bfb566 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 10:06:55 -0700 Subject: [PATCH 212/225] Bump tests/perf/s2n-quic from `75afd77` to `cc4e6d0` (#3393) Bumps [tests/perf/s2n-quic](https://github.com/aws/s2n-quic) from `75afd77` to `cc4e6d0`.
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 75afd77dfa88..cc4e6d023f8e 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 75afd77dfa88d696900f12ee747409ddb208a745 +Subproject commit cc4e6d023f8edf92fea294dcaea2fd5a1132cb47 From 93b3081222a9f54a288979ec393fb4cb0c5ed757 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Mon, 29 Jul 2024 21:07:55 +0200 Subject: [PATCH 213/225] Update the rust toolchain to nightly-2024-07-29 (#3392) Changes required due to: - rust-lang/rust@9db265048e Bump to 1.82 Resolves: #3369 Co-authored-by: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> Co-authored-by: Celina G. Val --- .github/workflows/verify-std-check.yml | 1 + kani-compiler/src/codegen_cprover_gotoc/utils/debug.rs | 2 +- kani-compiler/src/session.rs | 4 ++-- rust-toolchain.toml | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/verify-std-check.yml b/.github/workflows/verify-std-check.yml index 667be6b340c5..2ab65be36053 100644 --- a/.github/workflows/verify-std-check.yml +++ b/.github/workflows/verify-std-check.yml @@ -39,6 +39,7 @@ jobs: uses: actions/checkout@v4 with: path: kani + fetch-depth: 0 - name: Setup Kani Dependencies uses: ./kani/.github/actions/setup diff --git a/kani-compiler/src/codegen_cprover_gotoc/utils/debug.rs b/kani-compiler/src/codegen_cprover_gotoc/utils/debug.rs index aeaf8e0e2d10..0c69cb30b37f 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/utils/debug.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/utils/debug.rs @@ -25,7 +25,7 @@ pub fn init() { /// Custom panic hook to add more information when panic occurs during goto-c codegen. #[allow(clippy::type_complexity)] -static DEFAULT_HOOK: LazyLock) + Sync + Send + 'static>> = +static DEFAULT_HOOK: LazyLock) + Sync + Send + 'static>> = LazyLock::new(|| { let hook = panic::take_hook(); panic::set_hook(Box::new(|info| { diff --git a/kani-compiler/src/session.rs b/kani-compiler/src/session.rs index bc0930eb9e4b..7ec2b79a0469 100644 --- a/kani-compiler/src/session.rs +++ b/kani-compiler/src/session.rs @@ -29,7 +29,7 @@ const BUG_REPORT_URL: &str = // Custom panic hook when running under user friendly message format. #[allow(clippy::type_complexity)] -static PANIC_HOOK: LazyLock) + Sync + Send + 'static>> = +static PANIC_HOOK: LazyLock) + Sync + Send + 'static>> = LazyLock::new(|| { let hook = panic::take_hook(); panic::set_hook(Box::new(|info| { @@ -46,7 +46,7 @@ static PANIC_HOOK: LazyLock) + Sync + Send + 's // Custom panic hook when executing under json error format `--error-format=json`. #[allow(clippy::type_complexity)] -static JSON_PANIC_HOOK: LazyLock) + Sync + Send + 'static>> = +static JSON_PANIC_HOOK: LazyLock) + Sync + Send + 'static>> = LazyLock::new(|| { let hook = panic::take_hook(); panic::set_hook(Box::new(|info| { diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 27c29bb820e2..18eb2aa4836f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-07-21" +channel = "nightly-2024-07-29" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 7cd31aa3fea07f0326f43e62d8b30764f8354dda Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Mon, 29 Jul 2024 14:32:25 -0700 Subject: [PATCH 214/225] Fix verify-std-check outcome check (#3394) It looks like I used `output` instead of `outcome` variable. I noticed that [this PR](https://github.com/model-checking/kani/pull/3392) should've failed the workflow but it didn't because of this mistake. Co-authored-by: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> --- .github/workflows/verify-std-check.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/verify-std-check.yml b/.github/workflows/verify-std-check.yml index 2ab65be36053..1bad09cd3c6d 100644 --- a/.github/workflows/verify-std-check.yml +++ b/.github/workflows/verify-std-check.yml @@ -80,8 +80,10 @@ jobs: -Z mem-predicates -Z ptr-to-ref-cast-checks - name: Compare PR results - if: steps.check-head.outcome != 'success' && steps.check-head.output != github.check-base.output + if: steps.check-head.outcome != 'success' && steps.check-head.outcome != steps.check-base.outcome run: | echo "New failure introduced by this change" + echo "HEAD: ${{ steps.check-head.outcome }}" + echo "BASE: ${{ steps.check-base.outcome }}" exit 1 From 1e0b71dfa44176c32d1ac80bfee375c6ff64d5b8 Mon Sep 17 00:00:00 2001 From: Matias Scharager Date: Tue, 30 Jul 2024 00:11:40 -0400 Subject: [PATCH 215/225] Function Contracts: Interior Mutability Tests (#3351) Test cases for interior mutability using Cell, OnceCell, UnsafeCell, and RefCell. 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: Matias Scharager Co-authored-by: Felipe R. Monteiro --- .../interior-mutability/api/cell.expected | 6 +++ .../interior-mutability/api/cell.rs | 25 ++++++++++++ .../api/cell_stub.expected | 9 +++++ .../interior-mutability/api/cell_stub.rs | 40 +++++++++++++++++++ .../api/unsafecell.expected | 6 +++ .../interior-mutability/api/unsafecell.rs | 24 +++++++++++ .../whole-struct/cell.expected | 6 +++ .../interior-mutability/whole-struct/cell.rs | 25 ++++++++++++ .../whole-struct/oncecell.expected | 6 +++ .../whole-struct/oncecell.rs | 24 +++++++++++ .../whole-struct/refcell.expected | 6 +++ .../whole-struct/refcell.rs | 25 ++++++++++++ .../whole-struct/unsafecell.expected | 6 +++ .../whole-struct/unsafecell.rs | 25 ++++++++++++ 14 files changed, 233 insertions(+) create mode 100644 tests/expected/function-contract/interior-mutability/api/cell.expected create mode 100644 tests/expected/function-contract/interior-mutability/api/cell.rs create mode 100644 tests/expected/function-contract/interior-mutability/api/cell_stub.expected create mode 100644 tests/expected/function-contract/interior-mutability/api/cell_stub.rs create mode 100644 tests/expected/function-contract/interior-mutability/api/unsafecell.expected create mode 100644 tests/expected/function-contract/interior-mutability/api/unsafecell.rs create mode 100644 tests/expected/function-contract/interior-mutability/whole-struct/cell.expected create mode 100644 tests/expected/function-contract/interior-mutability/whole-struct/cell.rs create mode 100644 tests/expected/function-contract/interior-mutability/whole-struct/oncecell.expected create mode 100644 tests/expected/function-contract/interior-mutability/whole-struct/oncecell.rs create mode 100644 tests/expected/function-contract/interior-mutability/whole-struct/refcell.expected create mode 100644 tests/expected/function-contract/interior-mutability/whole-struct/refcell.rs create mode 100644 tests/expected/function-contract/interior-mutability/whole-struct/unsafecell.expected create mode 100644 tests/expected/function-contract/interior-mutability/whole-struct/unsafecell.rs diff --git a/tests/expected/function-contract/interior-mutability/api/cell.expected b/tests/expected/function-contract/interior-mutability/api/cell.expected new file mode 100644 index 000000000000..d9b0f5d51e5c --- /dev/null +++ b/tests/expected/function-contract/interior-mutability/api/cell.expected @@ -0,0 +1,6 @@ +assertion\ +- Status: SUCCESS\ +- Description: "|_| im.x.get() < 101"\ +in function modify + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/interior-mutability/api/cell.rs b/tests/expected/function-contract/interior-mutability/api/cell.rs new file mode 100644 index 000000000000..671b6b206ef3 --- /dev/null +++ b/tests/expected/function-contract/interior-mutability/api/cell.rs @@ -0,0 +1,25 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +/// Mutating Cell via `as_ptr` in contracts +use std::cell::Cell; + +/// This struct contains Cell which can be mutated +struct InteriorMutability { + x: Cell, +} + +#[kani::requires(im.x.get() < 100)] +#[kani::modifies(im.x.as_ptr())] +#[kani::ensures(|_| im.x.get() < 101)] +///im is an immutable reference with interior mutability +fn modify(im: &InteriorMutability) { + im.x.set(im.x.get() + 1) +} + +#[kani::proof_for_contract(modify)] +fn harness_for_modify() { + let im: InteriorMutability = InteriorMutability { x: Cell::new(kani::any()) }; + modify(&im) +} diff --git a/tests/expected/function-contract/interior-mutability/api/cell_stub.expected b/tests/expected/function-contract/interior-mutability/api/cell_stub.expected new file mode 100644 index 000000000000..b8da35411e53 --- /dev/null +++ b/tests/expected/function-contract/interior-mutability/api/cell_stub.expected @@ -0,0 +1,9 @@ +assertion\ +- Status: SUCCESS\ +- Description: "|_| old(im.x.get() + im.x.get()) == im.x.get()"\ + +assertion\ +- Status: SUCCESS\ +- Description: "|_| old(im.x.get() + im.x.get() + im.x.get() + im.x.get()) == im.x.get()"\ + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/interior-mutability/api/cell_stub.rs b/tests/expected/function-contract/interior-mutability/api/cell_stub.rs new file mode 100644 index 000000000000..d01ca379655f --- /dev/null +++ b/tests/expected/function-contract/interior-mutability/api/cell_stub.rs @@ -0,0 +1,40 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +/// The objective of this test is to show that the contracts for double can be replaced as a stub within the contracts for quadruple. +/// This shows that we can generate `kani::any()` for Cell. +use std::cell::Cell; + +/// This struct contains Cell which can be mutated +struct InteriorMutability { + x: Cell, +} + +#[kani::ensures(|_| old(im.x.get() + im.x.get()) == im.x.get())] +#[kani::requires(im.x.get() < 100)] +#[kani::modifies(im.x.as_ptr())] +fn double(im: &InteriorMutability) { + im.x.set(im.x.get() + im.x.get()) +} + +#[kani::proof_for_contract(double)] +fn double_harness() { + let im: InteriorMutability = InteriorMutability { x: Cell::new(kani::any()) }; + double(&im); +} + +#[kani::ensures(|_| old(im.x.get() + im.x.get() + im.x.get() + im.x.get()) == im.x.get())] +#[kani::requires(im.x.get() < 50)] +#[kani::modifies(im.x.as_ptr())] +fn quadruple(im: &InteriorMutability) { + double(im); + double(im) +} + +#[kani::proof_for_contract(quadruple)] +#[kani::stub_verified(double)] +fn quadruple_harness() { + let im: InteriorMutability = InteriorMutability { x: Cell::new(kani::any()) }; + quadruple(&im); +} diff --git a/tests/expected/function-contract/interior-mutability/api/unsafecell.expected b/tests/expected/function-contract/interior-mutability/api/unsafecell.expected new file mode 100644 index 000000000000..1646a8a78e7f --- /dev/null +++ b/tests/expected/function-contract/interior-mutability/api/unsafecell.expected @@ -0,0 +1,6 @@ +assertion\ +- Status: SUCCESS\ +- Description: "|_| unsafe{ *im.x.get() } < 101"\ +in function modify + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/interior-mutability/api/unsafecell.rs b/tests/expected/function-contract/interior-mutability/api/unsafecell.rs new file mode 100644 index 000000000000..8125e3e3c077 --- /dev/null +++ b/tests/expected/function-contract/interior-mutability/api/unsafecell.rs @@ -0,0 +1,24 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +use std::cell::UnsafeCell; + +/// This struct contains UnsafeCell which can be mutated +struct InteriorMutability { + x: UnsafeCell, +} + +#[kani::requires(unsafe{*im.x.get()} < 100)] +#[kani::modifies(im.x.get())] +#[kani::ensures(|_| unsafe{*im.x.get()} < 101)] +/// `im` is an immutable reference with interior mutability +fn modify(im: &InteriorMutability) { + unsafe { *im.x.get() += 1 } +} + +#[kani::proof_for_contract(modify)] +fn harness_for_modify() { + let im: InteriorMutability = InteriorMutability { x: UnsafeCell::new(kani::any()) }; + modify(&im) +} diff --git a/tests/expected/function-contract/interior-mutability/whole-struct/cell.expected b/tests/expected/function-contract/interior-mutability/whole-struct/cell.expected new file mode 100644 index 000000000000..d9b0f5d51e5c --- /dev/null +++ b/tests/expected/function-contract/interior-mutability/whole-struct/cell.expected @@ -0,0 +1,6 @@ +assertion\ +- Status: SUCCESS\ +- Description: "|_| im.x.get() < 101"\ +in function modify + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/interior-mutability/whole-struct/cell.rs b/tests/expected/function-contract/interior-mutability/whole-struct/cell.rs new file mode 100644 index 000000000000..9f42f6fa1f6c --- /dev/null +++ b/tests/expected/function-contract/interior-mutability/whole-struct/cell.rs @@ -0,0 +1,25 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +/// The objective of this test is to check the modification of a Cell used as interior mutability in an immutable struct +use std::cell::Cell; + +/// This struct contains Cell which can be mutated +struct InteriorMutability { + x: Cell, +} + +#[kani::requires(im.x.get() < 100)] +#[kani::modifies(&im.x)] +#[kani::ensures(|_| im.x.get() < 101)] +/// `im` is an immutable reference with interior mutability +fn modify(im: &InteriorMutability) { + im.x.set(im.x.get() + 1) +} + +#[kani::proof_for_contract(modify)] +fn harness_for_modify() { + let im: InteriorMutability = InteriorMutability { x: Cell::new(kani::any()) }; + modify(&im) +} diff --git a/tests/expected/function-contract/interior-mutability/whole-struct/oncecell.expected b/tests/expected/function-contract/interior-mutability/whole-struct/oncecell.expected new file mode 100644 index 000000000000..a367bcd9fb95 --- /dev/null +++ b/tests/expected/function-contract/interior-mutability/whole-struct/oncecell.expected @@ -0,0 +1,6 @@ +assertion\ +- Status: SUCCESS\ +- Description: "|_| im.x.get().is_some()"\ +in function modify + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/interior-mutability/whole-struct/oncecell.rs b/tests/expected/function-contract/interior-mutability/whole-struct/oncecell.rs new file mode 100644 index 000000000000..6ca32251b60c --- /dev/null +++ b/tests/expected/function-contract/interior-mutability/whole-struct/oncecell.rs @@ -0,0 +1,24 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +/// The objective of this test is to check the modification of an OnceCell used as interior mutability in an immutable struct +use std::cell::OnceCell; + +/// This struct contains OnceCell which can be mutated +struct InteriorMutability { + x: OnceCell, +} + +#[kani::requires(im.x.get().is_none())] +#[kani::modifies(&im.x)] +#[kani::ensures(|_| im.x.get().is_some())] +fn modify(im: &InteriorMutability) { + im.x.set(5).expect("") +} + +#[kani::proof_for_contract(modify)] +fn harness_for_modify() { + let im: InteriorMutability = InteriorMutability { x: OnceCell::new() }; + modify(&im) +} diff --git a/tests/expected/function-contract/interior-mutability/whole-struct/refcell.expected b/tests/expected/function-contract/interior-mutability/whole-struct/refcell.expected new file mode 100644 index 000000000000..225c290a171e --- /dev/null +++ b/tests/expected/function-contract/interior-mutability/whole-struct/refcell.expected @@ -0,0 +1,6 @@ +assertion\ +- Status: SUCCESS\ +- Description: "|_| unsafe{ *im.x.as_ptr() } < 101"\ +in function modify + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/interior-mutability/whole-struct/refcell.rs b/tests/expected/function-contract/interior-mutability/whole-struct/refcell.rs new file mode 100644 index 000000000000..1cce5e2364c3 --- /dev/null +++ b/tests/expected/function-contract/interior-mutability/whole-struct/refcell.rs @@ -0,0 +1,25 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +/// The objective of this test is to check the modification of a RefCell used as interior mutability in an immutable struct +use std::cell::RefCell; + +/// This struct contains Cell which can be mutated +struct InteriorMutability { + x: RefCell, +} + +#[kani::requires(unsafe{*im.x.as_ptr()} < 100)] +#[kani::modifies(&im.x)] +#[kani::ensures(|_| unsafe{*im.x.as_ptr()} < 101)] +/// `im` is an immutable reference with interior mutability +fn modify(im: &InteriorMutability) { + im.x.replace_with(|&mut old| old + 1); +} + +#[kani::proof_for_contract(modify)] +fn harness_for_modify() { + let im: InteriorMutability = InteriorMutability { x: RefCell::new(kani::any()) }; + modify(&im) +} diff --git a/tests/expected/function-contract/interior-mutability/whole-struct/unsafecell.expected b/tests/expected/function-contract/interior-mutability/whole-struct/unsafecell.expected new file mode 100644 index 000000000000..1646a8a78e7f --- /dev/null +++ b/tests/expected/function-contract/interior-mutability/whole-struct/unsafecell.expected @@ -0,0 +1,6 @@ +assertion\ +- Status: SUCCESS\ +- Description: "|_| unsafe{ *im.x.get() } < 101"\ +in function modify + +VERIFICATION:- SUCCESSFUL diff --git a/tests/expected/function-contract/interior-mutability/whole-struct/unsafecell.rs b/tests/expected/function-contract/interior-mutability/whole-struct/unsafecell.rs new file mode 100644 index 000000000000..6adb7b12c8b1 --- /dev/null +++ b/tests/expected/function-contract/interior-mutability/whole-struct/unsafecell.rs @@ -0,0 +1,25 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zfunction-contracts + +/// The objective of this test is to check the modification of an UnsafeCell used as interior mutability in an immutable struct +use std::cell::UnsafeCell; + +/// This struct contains UnsafeCell which can be mutated +struct InteriorMutability { + x: UnsafeCell, +} + +#[kani::requires(unsafe{*im.x.get()} < 100)] +#[kani::modifies(im.x.get())] +#[kani::ensures(|_| unsafe{*im.x.get()} < 101)] +/// `im` is an immutable reference with interior mutability +fn modify(im: &InteriorMutability) { + unsafe { *im.x.get() += 1 } +} + +#[kani::proof_for_contract(modify)] +fn harness_for_modify() { + let im: InteriorMutability = InteriorMutability { x: UnsafeCell::new(kani::any()) }; + modify(&im) +} From 1553ae21369ada3a0bb58ef42d61bbdb8676da5a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2024 10:54:17 +0000 Subject: [PATCH 216/225] Automatic toolchain upgrade to nightly-2024-07-30 (#3395) Update Rust toolchain from nightly-2024-07-29 to nightly-2024-07-30 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/2cbbe8b8bb2be672b14cf741a2f0ec24a49f3f0b up to https://github.com/rust-lang/rust/commit/612a33f20b9b2c27380edbc4b26a01433ed114bc. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 18eb2aa4836f..10c7645cf1d7 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-07-29" +channel = "nightly-2024-07-30" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From 39c6bc1cacbd57089e0b0602a14daec412cbb2e7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2024 22:26:24 -0700 Subject: [PATCH 217/225] Automatic toolchain upgrade to nightly-2024-07-31 (#3397) Update Rust toolchain from nightly-2024-07-30 to nightly-2024-07-31 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/612a33f20b9b2c27380edbc4b26a01433ed114bc up to https://github.com/rust-lang/rust/commit/f8060d282d42770fadd73905e3eefb85660d3278. The log for this commit range is: https://github.com/rust-lang/rust/commit/f8060d282d Auto merge of #128083 - Mark-Simulacrum:bump-bootstrap, r=albertlarsan68 https://github.com/rust-lang/rust/commit/006c8df322 Auto merge of #124339 - oli-obk:supports_feature, r=wesleywiser https://github.com/rust-lang/rust/commit/abb1ebaae1 Revert "opt-dist: dont overrwite config.toml when verifying" https://github.com/rust-lang/rust/commit/92f263b792 Make RUSTC_OVERRIDE_VERSION_STRING overwrite the rendered version output, too https://github.com/rust-lang/rust/commit/cbab16feaf Test RUSTC_OVERRIDE_VERSION_STRING https://github.com/rust-lang/rust/commit/595316b400 Auto merge of #127955 - chenyukang:yukang-fix-mismatched-delimiter-issue-12786, r=nnethercote https://github.com/rust-lang/rust/commit/1ddedbaa59 Auto merge of #125929 - Bryanskiy:delegation-generics-3, r=petrochenkov https://github.com/rust-lang/rust/commit/e69c19ea0b Auto merge of #128336 - Bryanskiy:inst-binder-with-fresh, r=lcnr https://github.com/rust-lang/rust/commit/7e3a971870 Auto merge of #128378 - matthiaskrgr:rollup-i3qz9uo, r=matthiaskrgr https://github.com/rust-lang/rust/commit/710ce90fbe Auto merge of #128250 - Amanieu:select_unpredictable, r=nikic https://github.com/rust-lang/rust/commit/c2b085b4d4 Rollup merge of #128339 - GuillaumeGomez:click-code-example, r=notriddle https://github.com/rust-lang/rust/commit/f396a42ed6 Rollup merge of #128315 - zetanumbers:psvita-unsafe-in-unsafe, r=workingjubilee https://github.com/rust-lang/rust/commit/6b23cb5cdf Rollup merge of #128141 - nikic:aarch64-bti, r=DianQK,cuviper https://github.com/rust-lang/rust/commit/ae92125a75 Rollup merge of #127574 - lcnr:coherence-check-supertrait, r=compiler-errors https://github.com/rust-lang/rust/commit/dba8e2d2c2 Auto merge of #128234 - jcsp:retain-empty-case, r=tgross35 https://github.com/rust-lang/rust/commit/368e2fd458 Auto merge of #128360 - matthiaskrgr:rollup-wwy5mkj, r=matthiaskrgr https://github.com/rust-lang/rust/commit/c2616203bc Rollup merge of #128355 - jieyouxu:rename-nora, r=aDotInTheVoid https://github.com/rust-lang/rust/commit/4d78d11bf9 Rollup merge of #128342 - onur-ozkan:ci-env-usage, r=Kobzol https://github.com/rust-lang/rust/commit/a73a025190 Rollup merge of #128284 - GKFX:stabilize-offset-of-nested, r=dtolnay,jieyouxu https://github.com/rust-lang/rust/commit/b02cf4c274 Rollup merge of #128153 - compiler-errors:mdpe, r=cjgillot https://github.com/rust-lang/rust/commit/91b18a058c Rollup merge of #128104 - mu001999-contrib:fix/128053, r=petrochenkov https://github.com/rust-lang/rust/commit/9aedec9313 Rollup merge of #126247 - notriddle:notriddle/word-wrap-item-table, r=GuillaumeGomez https://github.com/rust-lang/rust/commit/99906dc89c Add rustdoc GUI test to check click on code examples https://github.com/rust-lang/rust/commit/a7cb1a5352 Make the buttons remain when code example is clicked https://github.com/rust-lang/rust/commit/ac303df4e2 rustdoc: move the wbr after the underscore, instead of before https://github.com/rust-lang/rust/commit/3bf8bcfbe0 rustdoc: properly handle path wrapping https://github.com/rust-lang/rust/commit/1d339b07ca rustdoc: use `` in sidebar headers https://github.com/rust-lang/rust/commit/f2f9aab380 Delegation: support generics for delegation from free functions https://github.com/rust-lang/rust/commit/f4fa80ff5a triagebot: make sure Nora is called Nora https://github.com/rust-lang/rust/commit/23f46e5b99 Stabilize offset_of_nested https://github.com/rust-lang/rust/commit/f990239b34 Stop using MoveDataParamEnv for places that don't need a param-env https://github.com/rust-lang/rust/commit/9186001f34 rustdoc: avoid redundant HTML when there's already line breaks https://github.com/rust-lang/rust/commit/0d0e18e7f6 rustdoc: use ``-tolerant function to check text contents https://github.com/rust-lang/rust/commit/583bf1e5bf Fix tidy call in runtest with custom HTML element https://github.com/rust-lang/rust/commit/f3661dce09 rustdoc: word wrap CamelCase in the item list table https://github.com/rust-lang/rust/commit/06d64ea4c4 simplify the use of `CiEnv` https://github.com/rust-lang/rust/commit/8a5efd1456 Use Vec in instantiate_binder_with_fresh_vars https://github.com/rust-lang/rust/commit/6a6824a0ab Optimize empty case in Vec::retain https://github.com/rust-lang/rust/commit/649d99b973 Bless a bootstrap-dependent UI test https://github.com/rust-lang/rust/commit/5eca36d27a step cfg(bootstrap) https://github.com/rust-lang/rust/commit/fceb0863d8 Bump stage0 to 1.81 beta https://github.com/rust-lang/rust/commit/e8644f85b8 Update CURRENT_RUSTC_VERSION https://github.com/rust-lang/rust/commit/0a5a84ee34 Add forbid(unsafe_op_in_unsafe_fn) https://github.com/rust-lang/rust/commit/352707da76 fix: psvita's std code https://github.com/rust-lang/rust/commit/4f78f9fbb0 Force LLVM to use CMOV for binary search https://github.com/rust-lang/rust/commit/adf0dff10c Not lint pub structs without pub constructors if containing fields of unit, never type, PhantomData and positional ZST https://github.com/rust-lang/rust/commit/94a3fd7678 add limit for unclosed delimiters in lexer diagnostic https://github.com/rust-lang/rust/commit/9a5e41c56f add testcase for 127868 https://github.com/rust-lang/rust/commit/ea7625f426 Set branch protection function attributes https://github.com/rust-lang/rust/commit/fe0bd76a8b elaborate unknowable goals 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 10c7645cf1d7..f7324e45d06d 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-07-30" +channel = "nightly-2024-07-31" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"] From e4078b4bdb2bb44c048d7880f9220297dd886a44 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 31 Jul 2024 11:55:12 +0200 Subject: [PATCH 218/225] Update to CBMC version 6.1.1 (#2995) Updates to match changes to the GOTO binary format. Resolves: #2952, #2972, #3287, #3391 This includes changes our handling of storage markers to be marking is-alive only rather than treating StorageLive as creating a new object. That is, object instances are now tied to their Mir-provided declarations (which, at present, only appear once per function). To still account for when Rust scopes deem an object to be alive, we use StorageLive and StorageDead to update __CPROVER_dead_object. This (global) variable is used by CBMC's pointer checks to track when a pointer may not be safe to dereference for it could be pointing to an object that no longer is in scope. Resolves: #3099 --- cprover_bindings/src/env.rs | 15 ++++- .../src/irep/goto_binary_serde.rs | 10 ++-- cprover_bindings/src/irep/irep_id.rs | 4 +- cprover_bindings/src/irep/to_irep.rs | 2 +- cprover_bindings/src/lib.rs | 1 + kani-compiler/src/args.rs | 3 - .../codegen/statement.rs | 59 +++++++++++++++++-- .../context/current_fn.rs | 31 +++++++++- kani-dependencies | 6 +- kani-driver/src/args/mod.rs | 8 --- kani-driver/src/call_cbmc.rs | 39 ++++++++---- kani-driver/src/call_goto_instrument.rs | 2 + kani-driver/src/call_single_file.rs | 4 -- kani-driver/src/cbmc_output_parser.rs | 4 ++ .../dead-invalid-access-via-raw/main.expected | 6 -- .../value.expected | 1 - .../shadow/unsupported_num_objects/test.rs | 10 ++-- tests/kani/FloatingPoint/main.rs | 1 - .../Intrinsics/Math/Rounding/Ceil/ceilf64.rs | 2 - .../Math/Rounding/Floor/floorf64.rs | 2 - .../Math/Rounding/NearbyInt/nearbyintf32.rs | 2 - .../Math/Rounding/NearbyInt/nearbyintf64.rs | 2 - .../Intrinsics/Math/Rounding/RInt/rintf32.rs | 2 - .../Intrinsics/Math/Rounding/RInt/rintf64.rs | 2 - .../Math/Rounding/Round/roundf32.rs | 2 - .../Math/Rounding/Round/roundf64.rs | 2 - .../Math/Rounding/Trunc/truncf64.rs | 2 - .../main.rs} | 2 +- tests/perf/btreeset/insert_any/Cargo.toml | 5 -- tests/perf/btreeset/insert_multi/Cargo.toml | 5 -- tests/perf/btreeset/insert_same/Cargo.toml | 5 -- tests/perf/hashset/Cargo.toml | 5 -- tests/perf/s2n-quic | 2 +- .../Strings/copy_empty_string_by_intrinsic.rs | 0 .../{ => slow}/kani/Vectors/any/push_slow.rs | 0 .../signed-overflow/check_message.rs | 1 - tests/ui/cbmc_checks/signed-overflow/expected | 1 - tools/benchcomp/configs/perf-regression.yaml | 4 +- 38 files changed, 152 insertions(+), 102 deletions(-) rename tests/kani/{Spurious/storage_fixme.rs => StorageMarkers/main.rs} (99%) rename tests/{ => slow}/kani/Strings/copy_empty_string_by_intrinsic.rs (100%) rename tests/{ => slow}/kani/Vectors/any/push_slow.rs (100%) diff --git a/cprover_bindings/src/env.rs b/cprover_bindings/src/env.rs index 42080e52119a..d31d213aa7d2 100644 --- a/cprover_bindings/src/env.rs +++ b/cprover_bindings/src/env.rs @@ -8,7 +8,7 @@ //! c.f. CBMC code [src/ansi-c/ansi_c_internal_additions.cpp]. //! One possible invocation of this insertion in CBMC can be found in \[ansi_c_languaget::parse\]. -use super::goto_program::{Expr, Location, Symbol, Type}; +use super::goto_program::{Expr, Location, Symbol, SymbolTable, Type}; use super::MachineModel; use num::bigint::BigInt; fn int_constant(name: &str, value: T) -> Symbol @@ -71,6 +71,8 @@ pub fn machine_model_symbols(mm: &MachineModel) -> Vec { ] } +const DEAD_OBJECT_IDENTIFIER: &str = "__CPROVER_dead_object"; + pub fn additional_env_symbols() -> Vec { vec![ Symbol::builtin_function("__CPROVER_initialize", vec![], Type::empty()), @@ -82,5 +84,16 @@ pub fn additional_env_symbols() -> Vec { Location::none(), ) .with_is_extern(true), + Symbol::static_variable( + DEAD_OBJECT_IDENTIFIER, + DEAD_OBJECT_IDENTIFIER, + Type::void_pointer(), + Location::none(), + ) + .with_is_extern(true), ] } + +pub fn global_dead_object(symbol_table: &SymbolTable) -> Expr { + symbol_table.lookup(DEAD_OBJECT_IDENTIFIER).unwrap().to_expr() +} diff --git a/cprover_bindings/src/irep/goto_binary_serde.rs b/cprover_bindings/src/irep/goto_binary_serde.rs index 324c5e764d1a..6f821768f996 100644 --- a/cprover_bindings/src/irep/goto_binary_serde.rs +++ b/cprover_bindings/src/irep/goto_binary_serde.rs @@ -11,7 +11,7 @@ use std::io::{self, BufReader}; use std::io::{BufWriter, Bytes, Error, ErrorKind, Read, Write}; use std::path::Path; -/// Writes a symbol table to a file in goto binary format in version 5. +/// Writes a symbol table to a file in goto binary format in version 6. /// /// In CBMC, the serialization rules are defined in : /// - src/goto-programs/write_goto_binary.h @@ -26,7 +26,7 @@ pub fn write_goto_binary_file(filename: &Path, source: &crate::goto_program::Sym serializer.write_file(irep_symbol_table); } -/// Reads a symbol table from a file expected to be in goto binary format in version 5. +/// Reads a symbol table from a file expected to be in goto binary format in version 6. // /// In CBMC, the deserialization rules are defined in : /// - src/goto-programs/read_goto_binary.h @@ -540,7 +540,7 @@ where assert!(written == 4); // Write goto binary version - self.write_usize_varenc(5); + self.write_usize_varenc(6); } /// Writes the symbol table using the GOTO binary file format to the byte stream. @@ -921,12 +921,12 @@ where // Read goto binary version let goto_binary_version = self.read_usize_varenc()?; - if goto_binary_version != 5 { + if goto_binary_version != 6 { return Err(Error::new( ErrorKind::Other, format!( "Unsupported GOTO binary version: {}. Supported version: {}", - goto_binary_version, 5 + goto_binary_version, 6 ), )); } diff --git a/cprover_bindings/src/irep/irep_id.rs b/cprover_bindings/src/irep/irep_id.rs index 9d52f4ef32fa..69962edf150e 100644 --- a/cprover_bindings/src/irep/irep_id.rs +++ b/cprover_bindings/src/irep/irep_id.rs @@ -366,7 +366,7 @@ pub enum IrepId { Div, Power, FactorialPower, - PrettyName, + CPrettyName, CClass, CField, CInterface, @@ -1242,7 +1242,7 @@ impl Display for IrepId { IrepId::Div => "/", IrepId::Power => "**", IrepId::FactorialPower => "factorial_power", - IrepId::PrettyName => "pretty_name", + IrepId::CPrettyName => "#pretty_name", IrepId::CClass => "#class", IrepId::CField => "#field", IrepId::CInterface => "#interface", diff --git a/cprover_bindings/src/irep/to_irep.rs b/cprover_bindings/src/irep/to_irep.rs index 05774fdf8b43..bf0b8ce862dd 100644 --- a/cprover_bindings/src/irep/to_irep.rs +++ b/cprover_bindings/src/irep/to_irep.rs @@ -132,7 +132,7 @@ impl ToIrep for DatatypeComponent { match self { DatatypeComponent::Field { name, typ } => Irep::just_named_sub(linear_map![ (IrepId::Name, Irep::just_string_id(name.to_string())), - (IrepId::PrettyName, Irep::just_string_id(name.to_string())), + (IrepId::CPrettyName, Irep::just_string_id(name.to_string())), (IrepId::Type, typ.to_irep(mm)), ]), DatatypeComponent::Padding { name, bits } => Irep::just_named_sub(linear_map![ diff --git a/cprover_bindings/src/lib.rs b/cprover_bindings/src/lib.rs index e77b29a24ca2..a5901afbac2d 100644 --- a/cprover_bindings/src/lib.rs +++ b/cprover_bindings/src/lib.rs @@ -33,6 +33,7 @@ #![feature(f16)] mod env; +pub use env::global_dead_object; pub mod goto_program; pub mod irep; mod machine_model; diff --git a/kani-compiler/src/args.rs b/kani-compiler/src/args.rs index e4b7a4435b0f..3efc5c0f4f61 100644 --- a/kani-compiler/src/args.rs +++ b/kani-compiler/src/args.rs @@ -74,9 +74,6 @@ pub struct Arguments { /// Enable specific checks. #[clap(long)] pub ub_check: Vec, - /// Ignore storage markers. - #[clap(long)] - pub ignore_storage_markers: bool, } #[derive(Debug, Clone, Copy, AsRefStr, EnumString, VariantNames, PartialEq, Eq)] diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 697cacd3b191..b8db1a3d52b6 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -75,18 +75,68 @@ impl<'tcx> GotocCtx<'tcx> { .goto_expr; self.codegen_set_discriminant(dest_ty, dest_expr, *variant_index, location) } + // StorageLive and StorageDead are modelled via CBMC's internal means of detecting + // accesses to dangling pointers, which uses demonic non-determinism. That is, CBMC + // non-deterministically chooses a single object's address to be tracked in a + // pointer-typed global instrumentation variable __CPROVER_dead_object. Any dereference + // entails a check that the pointer being dereferenced is not equal to the pointer held + // in __CPROVER_dead_object. We use this to bridge the difference between Rust and MIR + // semantics as follows: + // + // 1. (At the time of writing) MIR declares all function-local variables at function + // scope, irrespective of the scope/block that Rust code originally used. + // 2. In MIR, StorageLive and StorageDead markers are inserted at the beginning and end + // of the Rust block to record the Rust-level lifetime of the object. + // 3. We translate MIR declarations into GOTO declarations, implying that we will have + // a single object per function for a local variable, even when Rust had a variable + // declared in a sub-scope of the function where said scope was entered multiple + // times (e.g., a loop body). + // 4. To enable detection of use of dangling pointers, we now use + // __CPROVER_dead_object, unless the address of the local object is never taken + // (implying that there cannot be a use of a dangling pointer with respect to said + // object). We update __CPROVER_dead_object as follows: + // * StorageLive is set to NULL when __CPROVER_dead_object pointed to the object + // (re-)entering scope, or else is left unchanged. + // * StorageDead non-deterministically updates (or leaves unchanged) + // __CPROVER_dead_object to point to the object going out of scope. (This is the + // same update approach as used within CBMC.) + // + // This approach will also work when there are multiple occurrences of StorageLive (or + // StorageDead) on a path, or across control-flow branches, and even when StorageDead + // occurs without a preceding StorageLive. StatementKind::StorageLive(var_id) => { - if self.queries.args().ignore_storage_markers { + if !self.current_fn().is_address_taken_local(*var_id) { Stmt::skip(location) } else { - Stmt::decl(self.codegen_local(*var_id, location), None, location) + let global_dead_object = cbmc::global_dead_object(&self.symbol_table); + Stmt::assign( + global_dead_object.clone(), + global_dead_object + .clone() + .eq(self + .codegen_local(*var_id, location) + .address_of() + .cast_to(global_dead_object.typ().clone())) + .ternary(global_dead_object.typ().null(), global_dead_object), + location, + ) } } StatementKind::StorageDead(var_id) => { - if self.queries.args().ignore_storage_markers { + if !self.current_fn().is_address_taken_local(*var_id) { Stmt::skip(location) } else { - Stmt::dead(self.codegen_local(*var_id, location), location) + let global_dead_object = cbmc::global_dead_object(&self.symbol_table); + Stmt::assign( + global_dead_object.clone(), + Type::bool().nondet().ternary( + self.codegen_local(*var_id, location) + .address_of() + .cast_to(global_dead_object.typ().clone()), + global_dead_object, + ), + location, + ) } } StatementKind::Intrinsic(NonDivergingIntrinsic::CopyNonOverlapping( @@ -424,6 +474,7 @@ impl<'tcx> GotocCtx<'tcx> { .branches() .map(|(c, bb)| { Expr::int_constant(c, switch_ty.clone()) + .with_location(loc) .switch_case(Stmt::goto(bb_label(bb), loc)) }) .collect(); diff --git a/kani-compiler/src/codegen_cprover_gotoc/context/current_fn.rs b/kani-compiler/src/codegen_cprover_gotoc/context/current_fn.rs index 32c5d72f7007..ea3c9e909eb6 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/context/current_fn.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/context/current_fn.rs @@ -7,9 +7,9 @@ use cbmc::InternedString; use rustc_middle::ty::Instance as InstanceInternal; use rustc_smir::rustc_internal; use stable_mir::mir::mono::Instance; -use stable_mir::mir::{Body, Local, LocalDecl}; +use stable_mir::mir::{visit::Location, visit::MirVisitor, Body, Local, LocalDecl, Rvalue}; use stable_mir::CrateDef; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; /// This structure represents useful data about the function we are currently compiling. #[derive(Debug)] @@ -26,6 +26,8 @@ pub struct CurrentFnCtx<'tcx> { locals: Vec, /// A list of pretty names for locals that corrspond to user variables. local_names: HashMap, + /// Collection of variables that are used in a reference or address-of expression. + address_taken_locals: HashSet, /// The symbol name of the current function name: String, /// A human readable pretty name for the current function @@ -34,6 +36,24 @@ pub struct CurrentFnCtx<'tcx> { temp_var_counter: u64, } +struct AddressTakenLocalsCollector { + /// Locals that appear in `Rvalue::Ref` or `Rvalue::AddressOf` expressions. + address_taken_locals: HashSet, +} + +impl MirVisitor for AddressTakenLocalsCollector { + fn visit_rvalue(&mut self, rvalue: &Rvalue, _location: Location) { + match rvalue { + Rvalue::Ref(_, _, p) | Rvalue::AddressOf(_, p) => { + if p.projection.is_empty() { + self.address_taken_locals.insert(p.local); + } + } + _ => (), + } + } +} + /// Constructor impl<'tcx> CurrentFnCtx<'tcx> { pub fn new(instance: Instance, gcx: &GotocCtx<'tcx>, body: &Body) -> Self { @@ -46,6 +66,8 @@ impl<'tcx> CurrentFnCtx<'tcx> { .iter() .filter_map(|info| info.local().map(|local| (local, (&info.name).into()))) .collect::>(); + let mut visitor = AddressTakenLocalsCollector { address_taken_locals: HashSet::new() }; + visitor.visit_body(body); Self { block: vec![], instance, @@ -53,6 +75,7 @@ impl<'tcx> CurrentFnCtx<'tcx> { krate: instance.def.krate().name, locals, local_names, + address_taken_locals: visitor.address_taken_locals, name, readable_name, temp_var_counter: 0, @@ -106,6 +129,10 @@ impl<'tcx> CurrentFnCtx<'tcx> { pub fn local_name(&self, local: Local) -> Option { self.local_names.get(&local).copied() } + + pub fn is_address_taken_local(&self, local: Local) -> bool { + self.address_taken_locals.contains(&local) + } } /// Utility functions diff --git a/kani-dependencies b/kani-dependencies index 844d83c23dc7..421188a08762 100644 --- a/kani-dependencies +++ b/kani-dependencies @@ -1,6 +1,6 @@ -CBMC_MAJOR="5" -CBMC_MINOR="95" -CBMC_VERSION="5.95.1" +CBMC_MAJOR="6" +CBMC_MINOR="1" +CBMC_VERSION="6.1.1" # If you update this version number, remember to bump it in `src/setup.rs` too CBMC_VIEWER_MAJOR="3" diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index d043035bcab2..158f1c001b5b 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -245,14 +245,6 @@ pub struct VerificationArgs { #[arg(long, hide_short_help = true, requires("enable_unstable"))] pub ignore_global_asm: bool, - /// Ignore lifetimes of local variables. This effectively extends their - /// lifetimes to the function scope, and hence may cause Kani to miss - /// undefined behavior resulting from using the variable after it dies. - /// This option may impact the soundness of the analysis and may cause false - /// proofs and/or counterexamples - #[arg(long, hide_short_help = true, requires("enable_unstable"))] - pub ignore_locals_lifetime: bool, - /// Write the GotoC symbol table to a file in JSON format instead of goto binary format. #[arg(long, hide_short_help = true)] pub write_json_symtab: bool, diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index 81d50865cc13..387a9723fcdb 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -153,6 +153,11 @@ impl KaniSession { args.push(file.to_owned().into_os_string()); + // Make CBMC verbose by default to tell users about unwinding progress. This should be + // reviewed as CBMC's verbosity defaults evolve. + args.push("--verbosity".into()); + args.push("9".into()); + Ok(args) } @@ -160,18 +165,25 @@ impl KaniSession { pub fn cbmc_check_flags(&self) -> Vec { let mut args = Vec::new(); - if self.args.checks.memory_safety_on() { - args.push("--bounds-check".into()); - args.push("--pointer-check".into()); + // We assume that malloc cannot fail, see https://github.com/model-checking/kani/issues/891 + args.push("--no-malloc-may-fail".into()); + + // With PR #2630 we generate the appropriate checks directly rather than relying on CBMC's + // checks (which are for C semantics). + args.push("--no-undefined-shift-check".into()); + // With PR #647 we use Rust's `-C overflow-checks=on` instead of: + // --unsigned-overflow-check + // --signed-overflow-check + // So these options are deliberately skipped to avoid erroneously re-checking operations. + args.push("--no-signed-overflow-check".into()); + + if !self.args.checks.memory_safety_on() { + args.push("--no-bounds-check".into()); + args.push("--no-pointer-check".into()); } if self.args.checks.overflow_on() { - args.push("--div-by-zero-check".into()); args.push("--float-overflow-check".into()); args.push("--nan-check".into()); - // With PR #647 we use Rust's `-C overflow-checks=on` instead of: - // --unsigned-overflow-check - // --signed-overflow-check - // So these options are deliberately skipped to avoid erroneously re-checking operations. // TODO: Implement conversion checks as an optional check. // They are a well defined operation in rust, but they may yield unexpected results to @@ -179,11 +191,13 @@ impl KaniSession { // We might want to create a transformation pass instead of enabling CBMC since Kani // compiler sometimes rely on the bitwise conversion of signed <-> unsigned. // args.push("--conversion-check".into()); + } else { + args.push("--no-div-by-zero-check".into()); } - if self.args.checks.unwinding_on() { - // TODO: With CBMC v6 the below can be removed as those are defaults. - args.push("--unwinding-assertions".into()); + if !self.args.checks.unwinding_on() { + args.push("--no-unwinding-assertions".into()); + } else { args.push("--no-self-loops-to-assumptions".into()); } @@ -192,7 +206,8 @@ impl KaniSession { // still catch any invalid dereference with --pointer-check. Thus, only enable them // if the user explicitly request them. args.push("--pointer-overflow-check".into()); - args.push("--pointer-primitive-check".into()); + } else { + args.push("--no-pointer-primitive-check".into()); } args diff --git a/kani-driver/src/call_goto_instrument.rs b/kani-driver/src/call_goto_instrument.rs index 83744eddabfd..ae76be150871 100644 --- a/kani-driver/src/call_goto_instrument.rs +++ b/kani-driver/src/call_goto_instrument.rs @@ -93,6 +93,7 @@ impl KaniSession { fn add_library(&self, file: &Path) -> Result<()> { let args: Vec = vec![ "--add-library".into(), + "--no-malloc-may-fail".into(), file.to_owned().into_os_string(), // input file.to_owned().into_os_string(), // output ]; @@ -173,6 +174,7 @@ impl KaniSession { assigns.contracted_function_name.as_str().into(), "--nondet-static-exclude".into(), assigns.recursion_tracker.as_str().into(), + "--no-malloc-may-fail".into(), file.into(), file.into(), ]; diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index 411dabb5270e..bbeb5bfa417d 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -146,10 +146,6 @@ impl KaniSession { flags.push("--ub-check=uninit".into()); } - if self.args.ignore_locals_lifetime { - flags.push("--ignore-storage-markers".into()) - } - flags.extend(self.args.common_args.unstable_features.as_arguments().map(str::to_string)); // This argument will select the Kani flavour of the compiler. It will be removed before diff --git a/kani-driver/src/cbmc_output_parser.rs b/kani-driver/src/cbmc_output_parser.rs index 127f98beab56..b3a78e8d03e2 100644 --- a/kani-driver/src/cbmc_output_parser.rs +++ b/kani-driver/src/cbmc_output_parser.rs @@ -329,6 +329,7 @@ pub enum CheckStatus { Satisfied, // for `cover` properties only Success, Undetermined, + Unknown, Unreachable, Uncovered, // for `code_coverage` properties only Unsatisfiable, // for `cover` properties only @@ -344,6 +345,9 @@ impl std::fmt::Display for CheckStatus { CheckStatus::Failure => style("FAILURE").red(), CheckStatus::Unreachable => style("UNREACHABLE").yellow(), CheckStatus::Undetermined => style("UNDETERMINED").yellow(), + // CBMC 6+ uses UNKNOWN when another property of undefined behavior failed, making it + // impossible to definitively conclude whether other properties hold or not. + CheckStatus::Unknown => style("UNDETERMINED").yellow(), CheckStatus::Unsatisfiable => style("UNSATISFIABLE").yellow(), }; write!(f, "{check_str}") diff --git a/tests/expected/dead-invalid-access-via-raw/main.expected b/tests/expected/dead-invalid-access-via-raw/main.expected index 1d464eb5f031..1cdbd0547226 100644 --- a/tests/expected/dead-invalid-access-via-raw/main.expected +++ b/tests/expected/dead-invalid-access-via-raw/main.expected @@ -1,7 +1,5 @@ SUCCESS\ address must be a multiple of its type's alignment -FAILURE\ -unsafe { *raw_ptr } == 10 SUCCESS\ pointer NULL SUCCESS\ @@ -10,7 +8,3 @@ SUCCESS\ deallocated dynamic object FAILURE\ dead object -SUCCESS\ -pointer outside object bounds -SUCCESS\ -invalid integer address diff --git a/tests/expected/dead-invalid-access-via-raw/value.expected b/tests/expected/dead-invalid-access-via-raw/value.expected index 858d44d54ea4..525e5e40a3b2 100644 --- a/tests/expected/dead-invalid-access-via-raw/value.expected +++ b/tests/expected/dead-invalid-access-via-raw/value.expected @@ -1,2 +1 @@ -Failed Checks: assertion failed: *p_subscoped == 7 Failed Checks: dereference failure: dead object diff --git a/tests/expected/shadow/unsupported_num_objects/test.rs b/tests/expected/shadow/unsupported_num_objects/test.rs index f60d0020e989..88b1171ef09d 100644 --- a/tests/expected/shadow/unsupported_num_objects/test.rs +++ b/tests/expected/shadow/unsupported_num_objects/test.rs @@ -15,14 +15,14 @@ fn check_max_objects() { // - the NULL pointer whose object ID is 0, and // - the object ID for `i` while i < N { - let x = i; - assert_eq!(kani::mem::pointer_object(&x as *const usize), i + 2); + let x: Box = Box::new(i); + assert_eq!(kani::mem::pointer_object(&*x as *const usize), 2 * i + 2); i += 1; } // create a new object whose ID is `N` + 2 let x = 42; - assert_eq!(kani::mem::pointer_object(&x as *const i32), N + 2); + assert_eq!(kani::mem::pointer_object(&x as *const i32), 2 * N + 2); // the following call to `set` would fail if the object ID for `x` exceeds // the maximum allowed by Kani's shadow memory model unsafe { @@ -32,10 +32,10 @@ fn check_max_objects() { #[kani::proof] fn check_max_objects_pass() { - check_max_objects::<1021>(); + check_max_objects::<510>(); } #[kani::proof] fn check_max_objects_fail() { - check_max_objects::<1022>(); + check_max_objects::<511>(); } diff --git a/tests/kani/FloatingPoint/main.rs b/tests/kani/FloatingPoint/main.rs index 93a29f169f27..f8ebccdac02a 100644 --- a/tests/kani/FloatingPoint/main.rs +++ b/tests/kani/FloatingPoint/main.rs @@ -26,7 +26,6 @@ macro_rules! test_floats { } #[kani::proof] -#[kani::solver(minisat)] fn main() { assert!(1.1 == 1.1 * 1.0); assert!(1.1 != 1.11 / 1.0); diff --git a/tests/kani/Intrinsics/Math/Rounding/Ceil/ceilf64.rs b/tests/kani/Intrinsics/Math/Rounding/Ceil/ceilf64.rs index 642d984a7e2b..09c630aa94a7 100644 --- a/tests/kani/Intrinsics/Math/Rounding/Ceil/ceilf64.rs +++ b/tests/kani/Intrinsics/Math/Rounding/Ceil/ceilf64.rs @@ -45,7 +45,6 @@ fn test_conc_sci() { } #[kani::proof] -#[kani::solver(minisat)] fn test_towards_inf() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); @@ -54,7 +53,6 @@ fn test_towards_inf() { } #[kani::proof] -#[kani::solver(minisat)] fn test_diff_one() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/Floor/floorf64.rs b/tests/kani/Intrinsics/Math/Rounding/Floor/floorf64.rs index 54ad74c33430..0560a2c55064 100644 --- a/tests/kani/Intrinsics/Math/Rounding/Floor/floorf64.rs +++ b/tests/kani/Intrinsics/Math/Rounding/Floor/floorf64.rs @@ -45,7 +45,6 @@ fn test_conc_sci() { } #[kani::proof] -#[kani::solver(minisat)] fn test_towards_neg_inf() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); @@ -54,7 +53,6 @@ fn test_towards_neg_inf() { } #[kani::proof] -#[kani::solver(minisat)] fn test_diff_one() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf32.rs b/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf32.rs index 7ffdb5f28747..25e02f45a943 100644 --- a/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf32.rs +++ b/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf32.rs @@ -50,7 +50,6 @@ fn test_conc_sci() { } #[kani::proof] -#[kani::solver(minisat)] fn test_towards_nearest() { let x: f32 = kani::any(); kani::assume(!x.is_nan()); @@ -89,7 +88,6 @@ fn test_towards_nearest() { } #[kani::proof] -#[kani::solver(minisat)] fn test_diff_half_one() { let x: f32 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf64.rs b/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf64.rs index bf656512562e..589a44a4d1ac 100644 --- a/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf64.rs +++ b/tests/kani/Intrinsics/Math/Rounding/NearbyInt/nearbyintf64.rs @@ -50,7 +50,6 @@ fn test_conc_sci() { } #[kani::proof] -#[kani::solver(minisat)] fn test_towards_nearest() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); @@ -89,7 +88,6 @@ fn test_towards_nearest() { } #[kani::proof] -#[kani::solver(minisat)] fn test_diff_half_one() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/RInt/rintf32.rs b/tests/kani/Intrinsics/Math/Rounding/RInt/rintf32.rs index 53d9d7d5fc73..79a0a4f9be2c 100644 --- a/tests/kani/Intrinsics/Math/Rounding/RInt/rintf32.rs +++ b/tests/kani/Intrinsics/Math/Rounding/RInt/rintf32.rs @@ -55,7 +55,6 @@ fn test_conc_sci() { } #[kani::proof] -#[kani::solver(minisat)] fn test_towards_nearest() { let x: f32 = kani::any(); kani::assume(!x.is_nan()); @@ -94,7 +93,6 @@ fn test_towards_nearest() { } #[kani::proof] -#[kani::solver(minisat)] fn test_diff_half_one() { let x: f32 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/RInt/rintf64.rs b/tests/kani/Intrinsics/Math/Rounding/RInt/rintf64.rs index de608e7cdb9f..8c8ea583a2d5 100644 --- a/tests/kani/Intrinsics/Math/Rounding/RInt/rintf64.rs +++ b/tests/kani/Intrinsics/Math/Rounding/RInt/rintf64.rs @@ -55,7 +55,6 @@ fn test_conc_sci() { } #[kani::proof] -#[kani::solver(minisat)] fn test_towards_nearest() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); @@ -94,7 +93,6 @@ fn test_towards_nearest() { } #[kani::proof] -#[kani::solver(minisat)] fn test_diff_half_one() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/Round/roundf32.rs b/tests/kani/Intrinsics/Math/Rounding/Round/roundf32.rs index 04893727dcfa..8a8780878925 100644 --- a/tests/kani/Intrinsics/Math/Rounding/Round/roundf32.rs +++ b/tests/kani/Intrinsics/Math/Rounding/Round/roundf32.rs @@ -39,7 +39,6 @@ fn test_conc_sci() { } #[kani::proof] -#[kani::solver(minisat)] fn test_towards_closer() { let x: f32 = kani::any(); kani::assume(!x.is_nan()); @@ -62,7 +61,6 @@ fn test_towards_closer() { } #[kani::proof] -#[kani::solver(minisat)] fn test_diff_half_one() { let x: f32 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/Round/roundf64.rs b/tests/kani/Intrinsics/Math/Rounding/Round/roundf64.rs index cd61e9607646..ddafc45a2e9e 100644 --- a/tests/kani/Intrinsics/Math/Rounding/Round/roundf64.rs +++ b/tests/kani/Intrinsics/Math/Rounding/Round/roundf64.rs @@ -39,7 +39,6 @@ fn test_conc_sci() { } #[kani::proof] -#[kani::solver(minisat)] fn test_towards_closer() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); @@ -62,7 +61,6 @@ fn test_towards_closer() { } #[kani::proof] -#[kani::solver(minisat)] fn test_diff_half_one() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Intrinsics/Math/Rounding/Trunc/truncf64.rs b/tests/kani/Intrinsics/Math/Rounding/Trunc/truncf64.rs index a8a80672e36b..5fcc8c80606d 100644 --- a/tests/kani/Intrinsics/Math/Rounding/Trunc/truncf64.rs +++ b/tests/kani/Intrinsics/Math/Rounding/Trunc/truncf64.rs @@ -38,7 +38,6 @@ fn test_conc_sci() { } #[kani::proof] -#[kani::solver(minisat)] fn test_towards_zero() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); @@ -51,7 +50,6 @@ fn test_towards_zero() { } #[kani::proof] -#[kani::solver(minisat)] fn test_diff_one() { let x: f64 = kani::any(); kani::assume(!x.is_nan()); diff --git a/tests/kani/Spurious/storage_fixme.rs b/tests/kani/StorageMarkers/main.rs similarity index 99% rename from tests/kani/Spurious/storage_fixme.rs rename to tests/kani/StorageMarkers/main.rs index 51d13f31bcef..770995605ded 100644 --- a/tests/kani/Spurious/storage_fixme.rs +++ b/tests/kani/StorageMarkers/main.rs @@ -3,7 +3,7 @@ // Modifications Copyright Kani Contributors // See GitHub history for details. -// Our handling of storage markers causes spurious failures in this test. +// Our handling of storage markers used to cause spurious failures in this test. // https://github.com/model-checking/kani/issues/3099 // The code is extracted from the implementation of `BTreeMap` which is where we // originally saw the spurious failures while trying to enable storage markers diff --git a/tests/perf/btreeset/insert_any/Cargo.toml b/tests/perf/btreeset/insert_any/Cargo.toml index 66d8ecdddeb1..41fa0a2db3ba 100644 --- a/tests/perf/btreeset/insert_any/Cargo.toml +++ b/tests/perf/btreeset/insert_any/Cargo.toml @@ -9,8 +9,3 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] - -# Temporarily ignore the handling of storage markers till -# https://github.com/model-checking/kani/issues/3099 is fixed -[package.metadata.kani] -flags = { ignore-locals-lifetime = true, enable-unstable = true } diff --git a/tests/perf/btreeset/insert_multi/Cargo.toml b/tests/perf/btreeset/insert_multi/Cargo.toml index 44028f8c842d..bdd2f4e3528a 100644 --- a/tests/perf/btreeset/insert_multi/Cargo.toml +++ b/tests/perf/btreeset/insert_multi/Cargo.toml @@ -9,8 +9,3 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] - -# Temporarily ignore the handling of storage markers till -# https://github.com/model-checking/kani/issues/3099 is fixed -[package.metadata.kani] -flags = { ignore-locals-lifetime = true, enable-unstable = true } diff --git a/tests/perf/btreeset/insert_same/Cargo.toml b/tests/perf/btreeset/insert_same/Cargo.toml index 465119c74fbe..0a4e0f7ee037 100644 --- a/tests/perf/btreeset/insert_same/Cargo.toml +++ b/tests/perf/btreeset/insert_same/Cargo.toml @@ -9,8 +9,3 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] - -# Temporarily ignore the handling of storage markers till -# https://github.com/model-checking/kani/issues/3099 is fixed -[package.metadata.kani] -flags = { ignore-locals-lifetime = true, enable-unstable = true } diff --git a/tests/perf/hashset/Cargo.toml b/tests/perf/hashset/Cargo.toml index 464fba412e6d..d0757e11154b 100644 --- a/tests/perf/hashset/Cargo.toml +++ b/tests/perf/hashset/Cargo.toml @@ -12,8 +12,3 @@ description = "Verify HashSet basic behavior" [package.metadata.kani.unstable] stubbing = true - -# Temporarily ignore the handling of storage markers till -# https://github.com/model-checking/kani/issues/3099 is fixed -[package.metadata.kani] -flags = { ignore-locals-lifetime = true, enable-unstable = true } diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index cc4e6d023f8e..71f8d9f5aafb 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit cc4e6d023f8edf92fea294dcaea2fd5a1132cb47 +Subproject commit 71f8d9f5aafbf59f31ad85eeb7b4b67a7564a685 diff --git a/tests/kani/Strings/copy_empty_string_by_intrinsic.rs b/tests/slow/kani/Strings/copy_empty_string_by_intrinsic.rs similarity index 100% rename from tests/kani/Strings/copy_empty_string_by_intrinsic.rs rename to tests/slow/kani/Strings/copy_empty_string_by_intrinsic.rs diff --git a/tests/kani/Vectors/any/push_slow.rs b/tests/slow/kani/Vectors/any/push_slow.rs similarity index 100% rename from tests/kani/Vectors/any/push_slow.rs rename to tests/slow/kani/Vectors/any/push_slow.rs diff --git a/tests/ui/cbmc_checks/signed-overflow/check_message.rs b/tests/ui/cbmc_checks/signed-overflow/check_message.rs index 0a1527e9a8fc..af496192ee60 100644 --- a/tests/ui/cbmc_checks/signed-overflow/check_message.rs +++ b/tests/ui/cbmc_checks/signed-overflow/check_message.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // // Check we don't print temporary variables as part of CBMC messages. -// cbmc-flags: --signed-overflow-check extern crate kani; use kani::any; diff --git a/tests/ui/cbmc_checks/signed-overflow/expected b/tests/ui/cbmc_checks/signed-overflow/expected index 70669b325e9e..80d3eef3cd25 100644 --- a/tests/ui/cbmc_checks/signed-overflow/expected +++ b/tests/ui/cbmc_checks/signed-overflow/expected @@ -7,4 +7,3 @@ Failed Checks: attempt to calculate the remainder with a divisor of zero Failed Checks: attempt to calculate the remainder with overflow Failed Checks: attempt to shift left with overflow Failed Checks: attempt to shift right with overflow -Failed Checks: arithmetic overflow on signed shl diff --git a/tools/benchcomp/configs/perf-regression.yaml b/tools/benchcomp/configs/perf-regression.yaml index c938b3dd861f..d1e65b24ca2c 100644 --- a/tools/benchcomp/configs/perf-regression.yaml +++ b/tools/benchcomp/configs/perf-regression.yaml @@ -10,13 +10,13 @@ variants: kani_new: config: directory: new - command_line: scripts/kani-perf.sh + command_line: "scripts/setup/ubuntu/install_deps.sh && scripts/kani-perf.sh" env: RUST_TEST_THREADS: "1" kani_old: config: directory: old - command_line: scripts/kani-perf.sh + command_line: "scripts/setup/ubuntu/install_deps.sh && scripts/kani-perf.sh" env: RUST_TEST_THREADS: "1" From 7e02353ff36bc6d08ede14c4e52c5f46c8f3b7f1 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 31 Jul 2024 17:11:01 +0200 Subject: [PATCH 219/225] Reduce memory consumption of cell_stub.rs test (#3399) With MiniSat the test takes 45 seconds instead of 30 seconds, but only consumes 5.5 GB of memory instead of 9 GB. This will hopefully fix CI failures. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --- .../function-contract/interior-mutability/api/cell_stub.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/expected/function-contract/interior-mutability/api/cell_stub.rs b/tests/expected/function-contract/interior-mutability/api/cell_stub.rs index d01ca379655f..9752bec5d272 100644 --- a/tests/expected/function-contract/interior-mutability/api/cell_stub.rs +++ b/tests/expected/function-contract/interior-mutability/api/cell_stub.rs @@ -1,6 +1,6 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: -Zfunction-contracts +// kani-flags: -Zfunction-contracts --solver minisat /// The objective of this test is to show that the contracts for double can be replaced as a stub within the contracts for quadruple. /// This shows that we can generate `kani::any()` for Cell. From b5d306ac9ababf8045cf1f6abefb1616fe0ee651 Mon Sep 17 00:00:00 2001 From: Adrian Palacios <73246657+adpaco-aws@users.noreply.github.com> Date: Wed, 31 Jul 2024 11:58:29 -0400 Subject: [PATCH 220/225] Define a struct-level `#[safety_constraint(...)]` attribute (#3270) This PR defines a struct-level `#[safety_constraint()]` macro attribute that allows users to define the type invariant as the predicate passed as an argument to the attribute. This safety constraint is picked up when deriving `Arbitrary` and `Invariant` implementations so that the values generated with `any()` or checked with `is_safe()` satisfy the constraint. This attribute is similar to the helper attribute introduced in #3283, and they cannot be used together. It's preferable to use this attribute when the constraint implies some relation between different fields. But, at the moment, there's no practical difference between them because the helper attribute allows users to refer to any fields. If we imposed that restriction, this attribute would be the only way to specify struct-wide invariants. An example: ```rs #[derive(kani::Arbitrary)] #[derive(kani::Invariant)] #[safety_constraint(*x == *y)] struct SameCoordsPoint { x: i32, y: i32, } #[kani::proof] fn check_invariant() { let point: SameCoordsPoint = kani::any(); assert!(point.is_safe()); } ``` Resolves #3095 --- library/kani_macros/src/derive.rs | 321 ++++++++++++------ .../abstract-value/abstract-value.rs | 30 ++ .../abstract-value/expected | 7 + .../check-arbitrary/check-arbitrary.rs | 24 ++ .../check-arbitrary/expected | 11 + .../check-invariant/check-invariant.rs | 27 ++ .../check-invariant/expected | 7 + .../grade-example/expected | 11 + .../grade-example/grade-example.rs | 43 +++ .../double-attribute/double-attribute.rs | 16 + .../double-attribute/expected | 6 + .../invalid-pred-error/expected | 19 ++ .../invalid-pred-error/invalid-pred-error.rs | 19 ++ .../mixed-attributes/expected | 6 + .../mixed-attributes/mixed-attributes.rs | 17 + .../no-struct-error/expected | 6 + .../no-struct-error/no-struct-error.rs | 18 + 17 files changed, 490 insertions(+), 98 deletions(-) create mode 100644 tests/expected/safety-constraint-attribute/abstract-value/abstract-value.rs create mode 100644 tests/expected/safety-constraint-attribute/abstract-value/expected create mode 100644 tests/expected/safety-constraint-attribute/check-arbitrary/check-arbitrary.rs create mode 100644 tests/expected/safety-constraint-attribute/check-arbitrary/expected create mode 100644 tests/expected/safety-constraint-attribute/check-invariant/check-invariant.rs create mode 100644 tests/expected/safety-constraint-attribute/check-invariant/expected create mode 100644 tests/expected/safety-constraint-attribute/grade-example/expected create mode 100644 tests/expected/safety-constraint-attribute/grade-example/grade-example.rs create mode 100644 tests/ui/safety-constraint-attribute/double-attribute/double-attribute.rs create mode 100644 tests/ui/safety-constraint-attribute/double-attribute/expected create mode 100644 tests/ui/safety-constraint-attribute/invalid-pred-error/expected create mode 100644 tests/ui/safety-constraint-attribute/invalid-pred-error/invalid-pred-error.rs create mode 100644 tests/ui/safety-constraint-attribute/mixed-attributes/expected create mode 100644 tests/ui/safety-constraint-attribute/mixed-attributes/mixed-attributes.rs create mode 100644 tests/ui/safety-constraint-attribute/no-struct-error/expected create mode 100644 tests/ui/safety-constraint-attribute/no-struct-error/no-struct-error.rs diff --git a/library/kani_macros/src/derive.rs b/library/kani_macros/src/derive.rs index a7aaa8ae334e..cc936560e510 100644 --- a/library/kani_macros/src/derive.rs +++ b/library/kani_macros/src/derive.rs @@ -19,20 +19,20 @@ use syn::{ }; pub fn expand_derive_arbitrary(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let trait_name = "Arbitrary"; let derive_item = parse_macro_input!(item as DeriveInput); let item_name = &derive_item.ident; + 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); + // Add a bound `T: Arbitrary` to every type parameter T. let generics = add_trait_bound_arbitrary(derive_item.generics); // Generate an expression to sum up the heap size of each field. let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - 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(&item_name, &derive_item.data); - - let expanded = if let Some(safety_cond) = safety_conds_opt { + let expanded = if let Some(safety_conds) = safety_conds_opt { let field_refs = field_refs(&item_name, &derive_item.data); quote! { // The generated implementation. @@ -40,7 +40,7 @@ pub fn expand_derive_arbitrary(item: proc_macro::TokenStream) -> proc_macro::Tok fn any() -> Self { let obj = #body; #field_refs - kani::assume(#safety_cond); + kani::assume(#safety_conds); obj } } @@ -94,48 +94,6 @@ fn fn_any_body(ident: &Ident, data: &Data) -> TokenStream { } } -/// Parse the condition expressions in `#[safety_constraint()]` attached to struct -/// fields and, it at least one was found, generate a conjunction to be assumed. -/// -/// For example, if we're deriving implementations for the struct -/// ``` -/// #[derive(Arbitrary)] -/// #[derive(Invariant)] -/// struct PositivePoint { -/// #[safety_constraint(*x >= 0)] -/// x: i32, -/// #[safety_constraint(*y >= 0)] -/// y: i32, -/// } -/// ``` -/// this function will generate the `TokenStream` -/// ``` -/// *x >= 0 && *y >= 0 -/// ``` -/// which can be passed to `kani::assume` to constrain the values generated -/// through the `Arbitrary` impl so that they are type-safe by construction. -fn safety_conds(ident: &Ident, data: &Data) -> Option { - match data { - Data::Struct(struct_data) => safety_conds_inner(ident, &struct_data.fields), - Data::Enum(_) => None, - Data::Union(_) => None, - } -} - -/// Generates an expression resulting from the conjunction of conditions -/// specified as safety constraints for each field. See `safety_conds` for more details. -fn safety_conds_inner(ident: &Ident, fields: &Fields) -> Option { - match fields { - Fields::Named(ref fields) => { - let conds: Vec = - fields.named.iter().filter_map(|field| parse_safety_expr(ident, field)).collect(); - if !conds.is_empty() { Some(quote! { #(#conds)&&* }) } else { None } - } - Fields::Unnamed(_) => None, - Fields::Unit => None, - } -} - /// Generates the sequence of expressions to initialize the variables used as /// references to the struct fields. /// @@ -191,6 +149,55 @@ fn field_refs_inner(_ident: &Ident, fields: &Fields) -> TokenStream { } } +fn safe_body_default(ident: &Ident, data: &Data) -> TokenStream { + match data { + Data::Struct(struct_data) => safe_body_default_inner(ident, &struct_data.fields), + Data::Enum(_) => unreachable!(), + Data::Union(_) => unreachable!(), + } +} + +fn safe_body_default_inner(_ident: &Ident, fields: &Fields) -> TokenStream { + match fields { + Fields::Named(ref fields) => { + let field_safe_calls: Vec = fields + .named + .iter() + .map(|field| { + let name = &field.ident; + quote_spanned! {field.span()=> + #name.is_safe() + } + }) + .collect(); + if !field_safe_calls.is_empty() { + quote! { #( #field_safe_calls )&&* } + } else { + quote! { true } + } + } + Fields::Unnamed(ref fields) => { + let field_safe_calls: Vec = fields + .unnamed + .iter() + .enumerate() + .map(|(idx, field)| { + let field_idx = Index::from(idx); + quote_spanned! {field.span()=> + #field_idx.is_safe() + } + }) + .collect(); + if !field_safe_calls.is_empty() { + quote! { #( #field_safe_calls )&&* } + } else { + quote! { true } + } + } + Fields::Unit => quote! { true }, + } +} + /// Generate an item initialization where an item can be a struct or a variant. /// For named fields, this will generate: `Item { field1: kani::any(), field2: kani::any(), .. }` /// For unnamed fields, this will generate: `Item (kani::any(), kani::any(), ..)` @@ -267,6 +274,38 @@ fn parse_safety_expr(ident: &Ident, field: &syn::Field) -> Option { } } +fn parse_safety_expr_input(ident: &Ident, derive_input: &DeriveInput) -> Option { + let name = ident; + let mut safety_attr = None; + + // Keep the attribute if we find it + for attr in &derive_input.attrs { + if attr.path().is_ident("safety_constraint") { + safety_attr = Some(attr); + } + } + + // Parse the arguments in the `#[safety_constraint(...)]` attribute + if let Some(attr) = safety_attr { + let expr_args: Result = attr.parse_args(); + + // Check if there was an error parsing the arguments + if let Err(err) = expr_args { + abort!(Span::call_site(), "Cannot derive impl for `{}`", ident; + note = attr.span() => + "safety constraint in `{}` could not be parsed: {}", name.to_string(), err + ) + } + + // Return the expression associated to the safety constraint + let safety_expr = expr_args.unwrap(); + Some(quote_spanned! {derive_input.span()=> + #safety_expr + }) + } else { + None + } +} /// Generate the body of the function `any()` for enums. The cases are: /// 1. For zero-variants enumerations, this will encode a `panic!()` statement. /// 2. For one or more variants, the code will be something like: @@ -318,33 +357,112 @@ fn fn_any_enum(ident: &Ident, data: &DataEnum) -> TokenStream { } } +fn safe_body_with_calls( + item_name: &Ident, + 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); + + if let Some(safety_conds) = safety_conds_opt { + quote! { #safe_body_default && #safety_conds } + } else { + safe_body_default + } +} + pub fn expand_derive_invariant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let trait_name = "Invariant"; let derive_item = parse_macro_input!(item as DeriveInput); let item_name = &derive_item.ident; + 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); // Generate an expression to sum up the heap size of each field. let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - let body = is_safe_body(&item_name, &derive_item.data); - let field_refs = field_refs(&item_name, &derive_item.data); - let expanded = quote! { // The generated implementation. impl #impl_generics kani::Invariant for #item_name #ty_generics #where_clause { fn is_safe(&self) -> bool { let obj = self; #field_refs - #body + #safe_body } } }; proc_macro::TokenStream::from(expanded) } +/// Looks for `#[safety_constraint(...)]` attributes used in the struct or its +/// fields, and returns the constraints if there were any, otherwise returns +/// `None`. +/// Note: Errors out if the attribute is used in both the struct and its fields. +fn safety_conds_opt( + item_name: &Ident, + derive_input: &DeriveInput, + trait_name: &str, +) -> Option { + let has_item_safety_constraint = + has_item_safety_constraint(&item_name, &derive_input, trait_name); + + 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; + note = item_name.span() => + "`#[safety_constraint(...)]` cannot be used in struct and its fields simultaneously" + ) + } + + if has_item_safety_constraint { + 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)) + } else { + None + } +} + +fn has_item_safety_constraint(ident: &Ident, derive_input: &DeriveInput, trait_name: &str) -> bool { + let safety_constraints_in_item = + derive_input.attrs.iter().filter(|attr| attr.path().is_ident("safety_constraint")).count(); + if safety_constraints_in_item > 1 { + abort!(Span::call_site(), "Cannot derive `{}` for `{}`", trait_name, ident; + note = ident.span() => + "`#[safety_constraint(...)]` cannot be used more than once." + ) + } + safety_constraints_in_item == 1 +} + +fn has_field_safety_constraints(ident: &Ident, data: &Data) -> bool { + match data { + Data::Struct(struct_data) => has_field_safety_constraints_inner(ident, &struct_data.fields), + Data::Enum(_) => false, + Data::Union(_) => false, + } +} + +/// Checks if the `#[safety_constraint(...)]` attribute is attached to any +/// field. +fn has_field_safety_constraints_inner(_ident: &Ident, fields: &Fields) -> bool { + match fields { + Fields::Named(ref fields) => fields + .named + .iter() + .any(|field| field.attrs.iter().any(|attr| attr.path().is_ident("safety_constraint"))), + Fields::Unnamed(_) => false, + Fields::Unit => false, + } +} + /// Add a bound `T: Invariant` to every type parameter T. -fn add_trait_bound_invariant(mut generics: Generics) -> Generics { +pub fn add_trait_bound_invariant(mut generics: Generics) -> Generics { generics.params.iter_mut().for_each(|param| { if let GenericParam::Type(type_param) = param { type_param.bounds.push(parse_quote!(kani::Invariant)); @@ -353,17 +471,51 @@ fn add_trait_bound_invariant(mut generics: Generics) -> Generics { generics } -fn is_safe_body(ident: &Ident, data: &Data) -> TokenStream { +fn safe_body_from_struct_attr( + ident: &Ident, + derive_input: &DeriveInput, + trait_name: &str, +) -> TokenStream { + if !matches!(derive_input.data, Data::Struct(_)) { + abort!(Span::call_site(), "Cannot derive `{}` for `{}`", trait_name, ident; + note = ident.span() => + "`#[safety_constraint(...)]` can only be used in structs" + ) + }; + parse_safety_expr_input(ident, derive_input).unwrap() +} + +/// Parse the condition expressions in `#[safety_constraint()]` attached to struct +/// fields and, it at least one was found, generate a conjunction to be assumed. +/// +/// For example, if we're deriving implementations for the struct +/// ``` +/// #[derive(Arbitrary)] +/// #[derive(Invariant)] +/// struct PositivePoint { +/// #[safety_constraint(*x >= 0)] +/// x: i32, +/// #[safety_constraint(*y >= 0)] +/// y: i32, +/// } +/// ``` +/// this function will generate the `TokenStream` +/// ``` +/// *x >= 0 && *y >= 0 +/// ``` +/// which can be used by the `Arbitrary` and `Invariant` to generate and check +/// type-safe values for the struct, respectively. +fn safe_body_from_fields_attr(ident: &Ident, data: &Data, trait_name: &str) -> TokenStream { match data { - Data::Struct(struct_data) => struct_invariant_conjunction(ident, &struct_data.fields), + Data::Struct(struct_data) => safe_body_from_fields_attr_inner(ident, &struct_data.fields), Data::Enum(_) => { - abort!(Span::call_site(), "Cannot derive `Invariant` for `{}` enum", ident; + abort!(Span::call_site(), "Cannot derive `{}` for `{}` enum", trait_name, ident; note = ident.span() => "`#[derive(Invariant)]` cannot be used for enums such as `{}`", ident ) } Data::Union(_) => { - abort!(Span::call_site(), "Cannot derive `Invariant` for `{}` union", ident; + abort!(Span::call_site(), "Cannot derive `{}` for `{}` union", trait_name, ident; note = ident.span() => "`#[derive(Invariant)]` cannot be used for unions such as `{}`", ident ) @@ -371,50 +523,23 @@ fn is_safe_body(ident: &Ident, data: &Data) -> TokenStream { } } -/// Generates an expression that is the conjunction of safety constraints for each field in the struct. -fn struct_invariant_conjunction(ident: &Ident, fields: &Fields) -> TokenStream { +/// Generates an expression resulting from the conjunction of conditions +/// specified as safety constraints for each field. +/// See `safe_body_from_fields_attr` for more details. +fn safe_body_from_fields_attr_inner(ident: &Ident, fields: &Fields) -> TokenStream { match fields { // Expands to the expression - // `true && && && ..` - // where `safety_condN` is - // - `self.fieldN.is_safe() && ` if a condition `` was - // specified through the `#[safety_constraint()]` helper attribute, or - // - `self.fieldN.is_safe()` otherwise - // - // Therefore, if `#[safety_constraint()]` isn't specified for any field, this expands to - // `true && self.field1.is_safe() && self.field2.is_safe() && ..` + // ` && && ..` + // where `` is the safety condition specified for the N-th field. Fields::Named(ref fields) => { - let safety_conds: Vec = fields - .named - .iter() - .map(|field| { - let name = &field.ident; - let default_expr = quote_spanned! {field.span()=> - #name.is_safe() - }; - parse_safety_expr(ident, field) - .map(|expr| quote! { #expr && #default_expr}) - .unwrap_or(default_expr) - }) - .collect(); - // An initial value is required for empty structs - safety_conds.iter().fold(quote! { true }, |acc, cond| { - quote! { #acc && #cond } - }) + let safety_conds: Vec = + fields.named.iter().filter_map(|field| parse_safety_expr(ident, field)).collect(); + quote! { #(#safety_conds)&&* } } - Fields::Unnamed(ref fields) => { - // Expands to the expression - // `true && self.0.is_safe() && self.1.is_safe() && ..` - let safe_calls = fields.unnamed.iter().enumerate().map(|(i, field)| { - let idx = syn::Index::from(i); - quote_spanned! {field.span()=> - self.#idx.is_safe() - } - }); - // An initial value is required for empty structs - safe_calls.fold(quote! { true }, |acc, call| { - quote! { #acc && #call } - }) + Fields::Unnamed(_) => { + quote! { + true + } } // Expands to the expression // `true` diff --git a/tests/expected/safety-constraint-attribute/abstract-value/abstract-value.rs b/tests/expected/safety-constraint-attribute/abstract-value/abstract-value.rs new file mode 100644 index 000000000000..6e29938d4d5a --- /dev/null +++ b/tests/expected/safety-constraint-attribute/abstract-value/abstract-value.rs @@ -0,0 +1,30 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that the `#[safety_constraint(...)]` attribute works as expected when +//! deriving `Arbitrary` and `Invariant` implementations. + +//! In this case, we test the attribute on a struct with a generic type `T` +//! which requires the bound `From` because of the comparisons in the +//! `#[safety_constraint(...)]` predicate. The struct represents an abstract +//! value for which we only track its sign. The actual value is kept private. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +#[safety_constraint((*positive && *conc_value >= 0.into()) || (!*positive && *conc_value < 0.into()))] +struct AbstractValue +where + T: PartialOrd + From, +{ + pub positive: bool, + conc_value: T, +} + +#[kani::proof] +fn check_abstract_value() { + let value: AbstractValue = kani::any(); + assert!(value.is_safe()); +} diff --git a/tests/expected/safety-constraint-attribute/abstract-value/expected b/tests/expected/safety-constraint-attribute/abstract-value/expected new file mode 100644 index 000000000000..2fc76378041d --- /dev/null +++ b/tests/expected/safety-constraint-attribute/abstract-value/expected @@ -0,0 +1,7 @@ +Check 1: check_abstract_value.assertion.1\ + - Status: SUCCESS\ + - Description: "assertion failed: value.is_safe()" + +VERIFICATION:- SUCCESSFUL + +Complete - 1 successfully verified harnesses, 0 failures, 1 total. diff --git a/tests/expected/safety-constraint-attribute/check-arbitrary/check-arbitrary.rs b/tests/expected/safety-constraint-attribute/check-arbitrary/check-arbitrary.rs new file mode 100644 index 000000000000..6e2b3ab97812 --- /dev/null +++ b/tests/expected/safety-constraint-attribute/check-arbitrary/check-arbitrary.rs @@ -0,0 +1,24 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that the `#[safety_constraint(...)]` attribute works as expected when +//! deriving `Arbitrary` and `Invariant` implementations. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +#[safety_constraint(*x >= 0 && *y >= 0)] +struct Point { + x: i32, + y: i32, +} + +#[kani::proof] +fn check_arbitrary() { + let point: Point = kani::any(); + assert!(point.x >= 0); + assert!(point.y >= 0); + assert!(point.is_safe()); +} diff --git a/tests/expected/safety-constraint-attribute/check-arbitrary/expected b/tests/expected/safety-constraint-attribute/check-arbitrary/expected new file mode 100644 index 000000000000..ee1e13bb726d --- /dev/null +++ b/tests/expected/safety-constraint-attribute/check-arbitrary/expected @@ -0,0 +1,11 @@ +Check 1: check_arbitrary.assertion.1\ + - Status: SUCCESS\ + - Description: "assertion failed: point.x >= 0" + +Check 2: check_arbitrary.assertion.2\ + - Status: SUCCESS\ + - Description: "assertion failed: point.y >= 0" + +Check 3: check_arbitrary.assertion.3\ + - Status: SUCCESS\ + - Description: "assertion failed: point.is_safe()" diff --git a/tests/expected/safety-constraint-attribute/check-invariant/check-invariant.rs b/tests/expected/safety-constraint-attribute/check-invariant/check-invariant.rs new file mode 100644 index 000000000000..fce7319779f0 --- /dev/null +++ b/tests/expected/safety-constraint-attribute/check-invariant/check-invariant.rs @@ -0,0 +1,27 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that the `#[safety_constraint(...)]` attribute works as expected when +//! deriving `Arbitrary` and `Invariant` implementations. + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +#[safety_constraint(*x == *y)] +struct SameCoordsPoint { + x: i32, + y: i32, +} + +#[kani::proof] +fn check_invariant() { + let point: SameCoordsPoint = kani::any(); + assert!(point.is_safe()); + + // Assuming `point.x != point.y` here should be like assuming `false`. + // The assertion should be unreachable because we're blocking the path. + kani::assume(point.x != point.y); + assert!(false, "this assertion should be unreachable"); +} diff --git a/tests/expected/safety-constraint-attribute/check-invariant/expected b/tests/expected/safety-constraint-attribute/check-invariant/expected new file mode 100644 index 000000000000..a4605f03b7b4 --- /dev/null +++ b/tests/expected/safety-constraint-attribute/check-invariant/expected @@ -0,0 +1,7 @@ +Check 1: check_invariant.assertion.1\ + - Status: SUCCESS\ + - Description: "assertion failed: point.is_safe()" + +Check 2: check_invariant.assertion.2\ + - Status: UNREACHABLE\ + - Description: ""this assertion should be unreachable"" diff --git a/tests/expected/safety-constraint-attribute/grade-example/expected b/tests/expected/safety-constraint-attribute/grade-example/expected new file mode 100644 index 000000000000..fd95a713d65a --- /dev/null +++ b/tests/expected/safety-constraint-attribute/grade-example/expected @@ -0,0 +1,11 @@ +Grade::check_percentage_safety.assertion.1\ + - Status: SUCCESS\ + - Description: "assertion failed: self.percentage <= 100" + +check_grade_safe.assertion.1\ + - Status: SUCCESS\ + - Description: "assertion failed: grade.is_safe()" + +VERIFICATION:- SUCCESSFUL + +Complete - 1 successfully verified harnesses, 0 failures, 1 total. diff --git a/tests/expected/safety-constraint-attribute/grade-example/grade-example.rs b/tests/expected/safety-constraint-attribute/grade-example/grade-example.rs new file mode 100644 index 000000000000..7998ab27df49 --- /dev/null +++ b/tests/expected/safety-constraint-attribute/grade-example/grade-example.rs @@ -0,0 +1,43 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that the `#[safety_constraint(...)]` attribute works as expected when +//! deriving `Arbitrary` and `Invariant` implementations. + +//! In this case, we test the attribute on a struct that represents a hybrid +//! grade (letter-numerical) which should keep the following equivalences: +//! - A for 90-100% +//! - B for 80-89% +//! - C for 70-79% +//! - D for 60-69% +//! - F for 0-59% +//! +//! In addition, we explicitly test that `percentage` is 0-100% + +extern crate kani; +use kani::Invariant; + +#[derive(kani::Arbitrary)] +#[derive(kani::Invariant)] +#[safety_constraint((*letter == 'A' && *percentage >= 90 && *percentage <= 100) || + (*letter == 'B' && *percentage >= 80 && *percentage < 90) || + (*letter == 'C' && *percentage >= 70 && *percentage < 80) || + (*letter == 'D' && *percentage >= 60 && *percentage < 70) || + (*letter == 'F' && *percentage < 60))] +struct Grade { + letter: char, + percentage: u32, +} + +impl Grade { + pub fn check_percentage_safety(&self) { + assert!(self.percentage <= 100); + } +} + +#[kani::proof] +fn check_grade_safe() { + let grade: Grade = kani::any(); + assert!(grade.is_safe()); + grade.check_percentage_safety(); +} diff --git a/tests/ui/safety-constraint-attribute/double-attribute/double-attribute.rs b/tests/ui/safety-constraint-attribute/double-attribute/double-attribute.rs new file mode 100644 index 000000000000..ef7be7fe0e03 --- /dev/null +++ b/tests/ui/safety-constraint-attribute/double-attribute/double-attribute.rs @@ -0,0 +1,16 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that there is a compilation error when the `#[safety_constraint(...)]` +//! attribute is used more than once on the same struct. + +extern crate kani; +use kani::Invariant; + +#[derive(Invariant)] +#[safety_constraint(*x >= 0)] +#[safety_constraint(*y >= 0)] +struct Point { + x: i32, + y: i32, +} diff --git a/tests/ui/safety-constraint-attribute/double-attribute/expected b/tests/ui/safety-constraint-attribute/double-attribute/expected new file mode 100644 index 000000000000..899b85e20e9a --- /dev/null +++ b/tests/ui/safety-constraint-attribute/double-attribute/expected @@ -0,0 +1,6 @@ +error: Cannot derive `Invariant` for `Point` + | +| #[derive(Invariant)] + | ^^^^^^^^^ + | +note: `#[safety_constraint(...)]` cannot be used more than once. diff --git a/tests/ui/safety-constraint-attribute/invalid-pred-error/expected b/tests/ui/safety-constraint-attribute/invalid-pred-error/expected new file mode 100644 index 000000000000..e15e3ff7cf7d --- /dev/null +++ b/tests/ui/safety-constraint-attribute/invalid-pred-error/expected @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + | +| #[safety_constraint(x >= 0 && y >= 0)] + | ^ expected `&i32`, found integer + | +help: consider dereferencing the borrow + | +| #[safety_constraint(*x >= 0 && y >= 0)] + | + + +error[E0308]: mismatched types + | +| #[safety_constraint(x >= 0 && y >= 0)] + | ^ expected `&i32`, found integer + | +help: consider dereferencing the borrow + | +| #[safety_constraint(x >= 0 && *y >= 0)] + | diff --git a/tests/ui/safety-constraint-attribute/invalid-pred-error/invalid-pred-error.rs b/tests/ui/safety-constraint-attribute/invalid-pred-error/invalid-pred-error.rs new file mode 100644 index 000000000000..89c862bb50c3 --- /dev/null +++ b/tests/ui/safety-constraint-attribute/invalid-pred-error/invalid-pred-error.rs @@ -0,0 +1,19 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that there is a compilation error when the predicate passed to the +//! `#[safety_constraint(...)]` attribute would result in a compiler error. +//! +//! Note: the `#[derive(kani::Invariant)]` macro is required for the compiler error, +//! otherwise the `#[safety_constraint(...)]` attribute is ignored. + +extern crate kani; + +// Note: The struct fields `x` and `y` are references in this context, we should +// refer to `*x` and `*y` instead. +#[derive(kani::Invariant)] +#[safety_constraint(x >= 0 && y >= 0)] +struct Point { + x: i32, + y: i32, +} diff --git a/tests/ui/safety-constraint-attribute/mixed-attributes/expected b/tests/ui/safety-constraint-attribute/mixed-attributes/expected new file mode 100644 index 000000000000..bca53d41bf13 --- /dev/null +++ b/tests/ui/safety-constraint-attribute/mixed-attributes/expected @@ -0,0 +1,6 @@ +error: Cannot derive `Invariant` for `Point` + | +| #[derive(Invariant)] + | ^^^^^^^^^ + | +note: `#[safety_constraint(...)]` cannot be used in struct and its fields simultaneously diff --git a/tests/ui/safety-constraint-attribute/mixed-attributes/mixed-attributes.rs b/tests/ui/safety-constraint-attribute/mixed-attributes/mixed-attributes.rs new file mode 100644 index 000000000000..931cebd4839d --- /dev/null +++ b/tests/ui/safety-constraint-attribute/mixed-attributes/mixed-attributes.rs @@ -0,0 +1,17 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that there is a compilation error when the `#[safety_constraint(...)]` +//! attribute for struct and the `#[safety_constraint(...)]` attribute for +//! fields is used at the same time. + +extern crate kani; +use kani::Invariant; + +#[derive(Invariant)] +#[safety_constraint(*x >= 0)] +struct Point { + x: i32, + #[safety_constraint(*y >= 0)] + y: i32, +} diff --git a/tests/ui/safety-constraint-attribute/no-struct-error/expected b/tests/ui/safety-constraint-attribute/no-struct-error/expected new file mode 100644 index 000000000000..df6bd82f00d6 --- /dev/null +++ b/tests/ui/safety-constraint-attribute/no-struct-error/expected @@ -0,0 +1,6 @@ +error: Cannot derive `Invariant` for `MyEnum` + | +| #[derive(kani::Invariant)] + | ^^^^^^^^^^^^^^^ + | +note: `#[safety_constraint(...)]` can only be used in structs diff --git a/tests/ui/safety-constraint-attribute/no-struct-error/no-struct-error.rs b/tests/ui/safety-constraint-attribute/no-struct-error/no-struct-error.rs new file mode 100644 index 000000000000..092bbe1319c7 --- /dev/null +++ b/tests/ui/safety-constraint-attribute/no-struct-error/no-struct-error.rs @@ -0,0 +1,18 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Check that Kani raises an error when a derive macro with the +//! `#[safety_constraint(...)]` attribute is is used in items which are not a +//! struct. +//! +//! Note: the `#[derive(kani::Invariant)]` macro is required for the compiler error, +//! otherwise the `#[safety_constraint(...)]` attribute is ignored. + +extern crate kani; + +#[derive(kani::Invariant)] +#[safety_constraint(true)] +enum MyEnum { + A, + B(i32), +} From 43d87134746f6f1f0671eecf9cb3a1052c2a9dc4 Mon Sep 17 00:00:00 2001 From: Michael Tautschnig Date: Wed, 31 Jul 2024 18:32:10 +0200 Subject: [PATCH 221/225] Fix cbmc-nightly for macOS (#3400) With macOS, cmake needs a full path to the C++ compiler. (Documented in CBMC's COMPILING.md.) --- .github/workflows/cbmc-latest.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cbmc-latest.yml b/.github/workflows/cbmc-latest.yml index f707c5d558a5..696f08fd44b8 100644 --- a/.github/workflows/cbmc-latest.yml +++ b/.github/workflows/cbmc-latest.yml @@ -38,7 +38,8 @@ jobs: repository: diffblue/cbmc path: cbmc - - name: Build CBMC + - name: Build CBMC (Linux) + if: ${{ startsWith(matrix.os, 'ubuntu') }} working-directory: ./cbmc run: | cmake -S . -Bbuild -DWITH_JBMC=OFF -Dsat_impl="minisat2;cadical" @@ -46,6 +47,15 @@ jobs: # Prepend the bin directory to $PATH echo "${GITHUB_WORKSPACE}/cbmc/build/bin" >> $GITHUB_PATH + - name: Build CBMC (macOS) + if: ${{ startsWith(matrix.os, 'macos') }} + working-directory: ./cbmc + run: | + cmake -S . -Bbuild -DWITH_JBMC=OFF -Dsat_impl="minisat2;cadical" -DCMAKE_CXX_COMPILER=$(which clang++) + cmake --build build -- -j 4 + # Prepend the bin directory to $PATH + echo "${GITHUB_WORKSPACE}/cbmc/build/bin" >> $GITHUB_PATH + - name: Execute Kani regressions working-directory: ./kani run: ./scripts/kani-regression.sh From 3bc4f0c76f8a64e62241daf61e81de18b66f0cd2 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 31 Jul 2024 11:50:08 -0700 Subject: [PATCH 222/225] Enable concrete playback for contract and stubs (#3389) This PR enables concrete playback for contract and stubs. Since these two cases may not actually behave as users expect (it can even have an internal failure), I am adding documentation to the generated test calling that out. As I was testing this issue, I realized that concrete playback didn't quite work for arrays. So I introduced a new private function `any_raw_array`, which doesn't change the behavior during verification, but allow us to special case it in the concrete playback flow. Resolves #3383 --------- Co-authored-by: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com> --- kani-compiler/src/kani_middle/attributes.rs | 45 +++++----- .../src/kani_middle/codegen_units.rs | 2 +- kani-compiler/src/kani_middle/metadata.rs | 4 +- kani-driver/src/args/mod.rs | 13 +-- .../src/concrete_playback/test_generator.rs | 85 +++++++++++++++---- kani-driver/src/metadata.rs | 16 ++-- kani_metadata/src/harness.rs | 32 ++++++- library/kani/src/arbitrary.rs | 2 +- library/kani/src/concrete_playback.rs | 5 ++ library/kani/src/lib.rs | 16 ++-- library/kani_core/src/arbitrary.rs | 2 +- library/kani_core/src/lib.rs | 16 ++-- .../kani_macros/src/sysroot/contracts/mod.rs | 11 +-- .../playback_expected/config.yml | 4 + .../playback_expected/expected | 8 ++ .../playback_expected/playback.sh | 42 +++++++++ .../src/playback_contract.rs | 19 +++++ .../playback_expected/src/playback_stubs.rs | 34 ++++++++ .../verify_std_cmd/verify_std.sh | 2 +- 19 files changed, 279 insertions(+), 79 deletions(-) create mode 100644 tests/script-based-pre/playback_expected/config.yml create mode 100644 tests/script-based-pre/playback_expected/expected create mode 100755 tests/script-based-pre/playback_expected/playback.sh create mode 100644 tests/script-based-pre/playback_expected/src/playback_contract.rs create mode 100644 tests/script-based-pre/playback_expected/src/playback_stubs.rs diff --git a/kani-compiler/src/kani_middle/attributes.rs b/kani-compiler/src/kani_middle/attributes.rs index 9eb6d3d6ee4e..8c729bbdec9f 100644 --- a/kani-compiler/src/kani_middle/attributes.rs +++ b/kani-compiler/src/kani_middle/attributes.rs @@ -4,7 +4,7 @@ use std::collections::BTreeMap; -use kani_metadata::{CbmcSolver, HarnessAttributes, Stub}; +use kani_metadata::{CbmcSolver, HarnessAttributes, HarnessKind, Stub}; use rustc_ast::{ attr, token::Token, @@ -310,7 +310,7 @@ impl<'tcx> KaniAttributes<'tcx> { /// the session and emit all errors found. pub(super) fn check_attributes(&self) { // Check that all attributes are correctly used and well formed. - let is_harness = self.is_harness(); + 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); @@ -454,7 +454,7 @@ impl<'tcx> KaniAttributes<'tcx> { /// Is this item a harness? (either `proof` or `proof_for_contract` /// attribute are present) - fn is_harness(&self) -> bool { + fn is_proof_harness(&self) -> bool { self.map.contains_key(&KaniAttributeKind::Proof) || self.map.contains_key(&KaniAttributeKind::ProofForContract) } @@ -469,13 +469,18 @@ impl<'tcx> KaniAttributes<'tcx> { panic!("Expected a local item, but got: {:?}", self.item); }; trace!(?self, "extract_harness_attributes"); - assert!(self.is_harness()); - self.map.iter().fold(HarnessAttributes::default(), |mut harness, (kind, attributes)| { + assert!(self.is_proof_harness()); + let harness_attrs = if let Some(Ok(harness)) = self.proof_for_contract() { + HarnessAttributes::new(HarnessKind::ProofForContract { target_fn: harness.to_string() }) + } else { + HarnessAttributes::new(HarnessKind::Proof) + }; + self.map.iter().fold(harness_attrs, |mut harness, (kind, attributes)| { match kind { KaniAttributeKind::ShouldPanic => harness.should_panic = true, KaniAttributeKind::Recursion => { self.tcx.dcx().span_err(self.tcx.def_span(self.item), "The attribute `kani::recursion` should only be used in combination with function contracts."); - }, + } KaniAttributeKind::Solver => { harness.solver = parse_solver(self.tcx, attributes[0]); } @@ -485,7 +490,7 @@ impl<'tcx> KaniAttributes<'tcx> { KaniAttributeKind::Unwind => { harness.unwind_value = parse_unwind(self.tcx, attributes[0]) } - KaniAttributeKind::Proof => harness.proof = true, + KaniAttributeKind::Proof => { /* no-op */ } KaniAttributeKind::ProofForContract => self.handle_proof_for_contract(&mut harness), KaniAttributeKind::StubVerified => self.handle_stub_verified(&mut harness), KaniAttributeKind::Unstable => { @@ -498,7 +503,7 @@ impl<'tcx> KaniAttributes<'tcx> { | KaniAttributeKind::InnerCheck | KaniAttributeKind::ReplacedWith => { self.tcx.dcx().span_err(self.tcx.def_span(self.item), format!("Contracts are not supported on harnesses. (Found the kani-internal contract attribute `{}`)", kind.as_ref())); - }, + } KaniAttributeKind::DisableChecks => { // Internal attribute which shouldn't exist here. unreachable!() @@ -552,14 +557,14 @@ impl<'tcx> KaniAttributes<'tcx> { self.item_name(), ), ) - .with_span_note( - self.tcx.def_span(def_id), - format!( - "Try adding a contract to this function or use the unsound `{}` attribute instead.", - KaniAttributeKind::Stub.as_ref(), + .with_span_note( + self.tcx.def_span(def_id), + format!( + "Try adding a contract to this function or use the unsound `{}` attribute instead.", + KaniAttributeKind::Stub.as_ref(), + ), ) - ) - .emit(); + .emit(); continue; } Some(Ok(replacement_name)) => replacement_name, @@ -689,7 +694,7 @@ fn has_kani_attribute bool>( tcx.get_attrs_unchecked(def_id).iter().filter_map(|a| attr_kind(tcx, a)).any(predicate) } -/// Same as [`KaniAttributes::is_harness`] but more efficient because less +/// Same as [`KaniAttributes::is_proof_harness`] but more efficient because less /// attribute parsing is performed. pub fn is_proof_harness(tcx: TyCtxt, instance: InstanceStable) -> bool { let def_id = rustc_internal::internal(tcx, instance.def.def_id()); @@ -896,7 +901,7 @@ fn parse_stubs(tcx: TyCtxt, harness: DefId, attributes: &[&Attribute]) -> Vec { tcx.dcx().span_err( error_span, - "attribute `kani::stub` takes two path arguments; found argument that is not a path", + "attribute `kani::stub` takes two path arguments; found argument that is not a path", ); None } @@ -910,9 +915,9 @@ 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, - format!("invalid argument for `{ATTRIBUTE}` attribute, expected one of the supported solvers (e.g. `kissat`) or a SAT solver binary (e.g. `bin=\"\"`)") - ) + 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=\"\"`)"), + ) }; let attr_args = attr.meta_item_list().unwrap(); diff --git a/kani-compiler/src/kani_middle/codegen_units.rs b/kani-compiler/src/kani_middle/codegen_units.rs index 260b363a868a..b4ea06c8d5db 100644 --- a/kani-compiler/src/kani_middle/codegen_units.rs +++ b/kani-compiler/src/kani_middle/codegen_units.rs @@ -104,7 +104,7 @@ impl CodegenUnits { /// Generate [KaniMetadata] for the target crate. fn generate_metadata(&self) -> KaniMetadata { let (proof_harnesses, test_harnesses) = - self.harness_info.values().cloned().partition(|md| md.attributes.proof); + self.harness_info.values().cloned().partition(|md| md.attributes.is_proof_harness()); KaniMetadata { crate_name: self.crate_info.name.clone(), proof_harnesses, diff --git a/kani-compiler/src/kani_middle/metadata.rs b/kani-compiler/src/kani_middle/metadata.rs index c00f38be4cb0..2f0f22d49e1c 100644 --- a/kani-compiler/src/kani_middle/metadata.rs +++ b/kani-compiler/src/kani_middle/metadata.rs @@ -6,7 +6,7 @@ use std::path::Path; use crate::kani_middle::attributes::test_harness_name; -use kani_metadata::{ArtifactType, HarnessAttributes, HarnessMetadata}; +use kani_metadata::{ArtifactType, HarnessAttributes, HarnessKind, HarnessMetadata}; use rustc_middle::ty::TyCtxt; use stable_mir::mir::mono::Instance; use stable_mir::CrateDef; @@ -61,7 +61,7 @@ pub fn gen_test_metadata( original_file: loc.filename, original_start_line: loc.start_line, original_end_line: loc.end_line, - attributes: HarnessAttributes::default(), + attributes: HarnessAttributes::new(HarnessKind::Test), // TODO: This no longer needs to be an Option. goto_file: Some(model_file), contract: Default::default(), diff --git a/kani-driver/src/args/mod.rs b/kani-driver/src/args/mod.rs index 158f1c001b5b..2d7593e8050a 100644 --- a/kani-driver/src/args/mod.rs +++ b/kani-driver/src/args/mod.rs @@ -559,13 +559,6 @@ impl ValidateArgs for VerificationArgs { --output-format=old.", )); } - if self.concrete_playback.is_some() && self.is_stubbing_enabled() { - // Concrete playback currently does not work with contracts or stubbing. - return Err(Error::raw( - ErrorKind::ArgumentConflict, - "Conflicting options: --concrete-playback isn't compatible with stubbing.", - )); - } if self.concrete_playback.is_some() && self.jobs() != Some(1) { // Concrete playback currently embeds a lot of assumptions about the order in which harnesses get called. return Err(Error::raw( @@ -869,13 +862,13 @@ mod tests { let res = parse_unstable_disabled("--harness foo -Z stubbing").unwrap(); assert!(res.verify_opts.is_stubbing_enabled()); - // `-Z stubbing` cannot be called with `--concrete-playback` + // `-Z stubbing` can now be called with concrete playback. let res = parse_unstable_disabled( "--harness foo --concrete-playback=print -Z concrete-playback -Z stubbing", ) .unwrap(); - let err = res.validate().unwrap_err(); - assert_eq!(err.kind(), ErrorKind::ArgumentConflict); + // Note that `res.validate()` fails because input file does not exist. + assert!(matches!(res.verify_opts.validate(), Ok(()))); } #[test] diff --git a/kani-driver/src/concrete_playback/test_generator.rs b/kani-driver/src/concrete_playback/test_generator.rs index dbd2fb9e03d2..5faa6299a5d3 100644 --- a/kani-driver/src/concrete_playback/test_generator.rs +++ b/kani-driver/src/concrete_playback/test_generator.rs @@ -6,10 +6,11 @@ use crate::args::ConcretePlaybackMode; use crate::call_cbmc::VerificationResult; +use crate::cbmc_output_parser::Property; use crate::session::KaniSession; use anyhow::{Context, Result}; use concrete_vals_extractor::{extract_harness_values, ConcreteVal}; -use kani_metadata::HarnessMetadata; +use kani_metadata::{HarnessKind, HarnessMetadata}; use std::collections::hash_map::DefaultHasher; use std::ffi::OsString; use std::fs::{read_to_string, File}; @@ -32,7 +33,7 @@ impl KaniSession { }; if let Ok(result_items) = &verification_result.results { - let harness_values: Vec> = extract_harness_values(result_items); + let harness_values = extract_harness_values(result_items); if harness_values.is_empty() { println!( @@ -43,9 +44,9 @@ impl KaniSession { } else { let mut unit_tests: Vec = harness_values .iter() - .map(|concrete_vals| { + .map(|(prop, concrete_vals)| { let pretty_name = harness.get_harness_name_unqualified(); - format_unit_test(&pretty_name, &concrete_vals) + format_unit_test(&pretty_name, &concrete_vals, gen_test_doc(harness, prop)) }) .collect(); unit_tests.dedup_by(|a, b| a.name == b.name); @@ -168,6 +169,9 @@ impl KaniSession { writeln!(temp_file, "{line}")?; if curr_line_num == proof_harness_end_line { for unit_test in unit_tests.iter() { + // Write an empty line before the unit test. + writeln!(temp_file)?; + for unit_test_line in unit_test.code.iter() { curr_line_num += 1; writeln!(temp_file, "{unit_test_line}")?; @@ -176,7 +180,7 @@ impl KaniSession { } } - // Renames are usually automic, so we won't corrupt the user's source file during a + // Renames are usually atomic, so we won't corrupt the user's source file during a // crash; but first flush all updates to disk, which persist wouldn't take care of. temp_file.as_file().sync_all()?; temp_file.persist(source_path).expect("Could not rename file"); @@ -231,8 +235,52 @@ impl KaniSession { } } +fn gen_test_doc(harness: &HarnessMetadata, property: &Property) -> String { + let mut doc_str = match &harness.attributes.kind { + HarnessKind::Proof => { + format!("/// Test generated for harness `{}` \n", harness.pretty_name) + } + HarnessKind::ProofForContract { target_fn } => { + format!( + "/// Test generated for harness `{}` that checks contract for `{target_fn}`\n", + harness.pretty_name + ) + } + HarnessKind::Test => { + unreachable!("Concrete playback for tests is not supported") + } + }; + doc_str.push_str("///\n"); + doc_str.push_str(&format!( + "/// Check for `{}`: \"{}\"\n", + property.property_class(), + property.description + )); + if !harness.attributes.stubs.is_empty() { + doc_str.push_str( + r#"/// +/// # Warning +/// +/// Concrete playback tests combined with stubs or contracts is highly +/// experimental, and subject to change. +/// +/// The original harness has stubs which are not applied to this test. +/// This may cause a mismatch of non-deterministic values if the stub +/// creates any non-deterministic value. +/// The execution path may also differ, which can be used to refine the stub +/// logic. +"#, + ); + } + doc_str +} + /// Generate a formatted unit test from a list of concrete values. -fn format_unit_test(harness_name: &str, concrete_vals: &[ConcreteVal]) -> UnitTest { +fn format_unit_test( + harness_name: &str, + concrete_vals: &[ConcreteVal], + doc_str: String, +) -> UnitTest { // Hash the concrete values along with the proof harness name. let mut hasher = DefaultHasher::new(); harness_name.hash(&mut hasher); @@ -241,6 +289,7 @@ fn format_unit_test(harness_name: &str, concrete_vals: &[ConcreteVal]) -> UnitTe let func_name = format!("kani_concrete_playback_{harness_name}_{hash}"); let func_before_concrete_vals = [ + doc_str, "#[test]".to_string(), format!("fn {func_name}() {{"), format!("{:<4}let concrete_vals: Vec> = vec![", " "), @@ -324,7 +373,7 @@ 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> { + pub fn extract_harness_values(result_items: &[Property]) -> Vec<(&Property, Vec)> { result_items .iter() .filter(|prop| { @@ -340,7 +389,7 @@ mod concrete_vals_extractor { let concrete_vals: Vec = trace.iter().filter_map(&extract_from_trace_item).collect(); - concrete_vals + (property, concrete_vals) }) .collect() } @@ -359,7 +408,7 @@ mod concrete_vals_extractor { { if trace_item.step_type == "assignment" && lhs.starts_with("goto_symex$$return_value") - && func.starts_with("kani::any_raw_internal") + && func.starts_with("kani::any_raw_") { let declared_width = width_u64 as usize; let actual_width = bit_concrete_val.len(); @@ -484,9 +533,10 @@ mod tests { /// Since hashes can not be relied on in tests, this compares all parts of a unit test except the hash. #[test] 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 unit_test = format_unit_test(harness_name, &concrete_vals); + 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); let expected_after_func_name = vec![ @@ -498,18 +548,23 @@ mod tests { "}".to_string(), ]; - assert_eq!(full_func[0], "#[test]"); + assert_eq!(full_func[0], doc_str); + assert_eq!(full_func[1], "#[test]"); assert_eq!( split_unit_test_name.before_hash, format!("kani_concrete_playback_{harness_name}") ); - assert_eq!(full_func[1], format!("fn {}() {{", unit_test.name)); - assert_eq!(full_func[2..], expected_after_func_name); + assert_eq!(full_func[2], format!("fn {}() {{", unit_test.name)); + assert_eq!(full_func[3..], expected_after_func_name); } /// Generates a unit test and returns its hash. fn extract_hash_from_unit_test(harness_name: &str, concrete_vals: &[ConcreteVal]) -> String { - let unit_test = format_unit_test(harness_name, concrete_vals); + let unit_test = format_unit_test( + harness_name, + concrete_vals, + "/// Harness created for unit test".to_string(), + ); split_unit_test_name(&unit_test.name).hash } @@ -603,7 +658,7 @@ mod tests { }), }]), }]; - let concrete_vals = extract_harness_values(&processed_items).pop().unwrap(); + let (_, concrete_vals) = extract_harness_values(&processed_items).pop().unwrap(); let concrete_val = &concrete_vals[0]; assert_eq!(concrete_val.byte_arr, vec![1, 3]); diff --git a/kani-driver/src/metadata.rs b/kani-driver/src/metadata.rs index cd916358d92e..3f9cd8f2bf84 100644 --- a/kani-driver/src/metadata.rs +++ b/kani-driver/src/metadata.rs @@ -200,7 +200,7 @@ fn find_proof_harnesses<'a>( #[cfg(test)] pub mod tests { use super::*; - use kani_metadata::HarnessAttributes; + use kani_metadata::{HarnessAttributes, HarnessKind}; use std::path::PathBuf; pub fn mock_proof_harness( @@ -209,6 +209,8 @@ pub mod tests { krate: Option<&str>, model_file: Option, ) -> HarnessMetadata { + let mut attributes = HarnessAttributes::new(HarnessKind::Proof); + attributes.unwind_value = unwind_value; HarnessMetadata { pretty_name: name.into(), mangled_name: name.into(), @@ -216,7 +218,7 @@ pub mod tests { original_file: "".into(), original_start_line: 0, original_end_line: 0, - attributes: HarnessAttributes { unwind_value, proof: true, ..Default::default() }, + attributes, goto_file: model_file, contract: Default::default(), } @@ -236,7 +238,7 @@ pub mod tests { find_proof_harnesses( &BTreeSet::from([&"check_three".to_string()]), &ref_harnesses, - false + false, ) .len(), 1 @@ -245,7 +247,7 @@ pub mod tests { find_proof_harnesses( &BTreeSet::from([&"check_two".to_string()]), &ref_harnesses, - false + false, ) .first() .unwrap() @@ -256,7 +258,7 @@ pub mod tests { find_proof_harnesses( &BTreeSet::from([&"check_one".to_string()]), &ref_harnesses, - false + false, ) .first() .unwrap() @@ -280,7 +282,7 @@ pub mod tests { find_proof_harnesses( &BTreeSet::from([&"check_three".to_string()]), &ref_harnesses, - true + true, ) .is_empty() ); @@ -299,7 +301,7 @@ pub mod tests { find_proof_harnesses( &BTreeSet::from([&"module::not_check_three".to_string()]), &ref_harnesses, - true + true, ) .first() .unwrap() diff --git a/kani_metadata/src/harness.rs b/kani_metadata/src/harness.rs index 3dd6c82ebd39..41eb4eb20919 100644 --- a/kani_metadata/src/harness.rs +++ b/kani_metadata/src/harness.rs @@ -38,10 +38,10 @@ pub struct HarnessMetadata { } /// The attributes added by the user to control how a harness is executed. -#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct HarnessAttributes { /// Whether the harness has been annotated with proof. - pub proof: bool, + pub kind: HarnessKind, /// Whether the harness is expected to panic or not. pub should_panic: bool, /// Optional data to store solver. @@ -52,6 +52,34 @@ pub struct HarnessAttributes { pub stubs: Vec, } +#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] +pub enum HarnessKind { + /// Function was annotated with `#[kani::proof]`. + Proof, + /// Function was annotated with `#[kani::proof_for_contract(target_fn)]`. + ProofForContract { target_fn: String }, + /// This is a test harness annotated with `#[test]`. + Test, +} + +impl HarnessAttributes { + /// Create a new harness of the provided kind. + pub fn new(kind: HarnessKind) -> HarnessAttributes { + HarnessAttributes { + kind, + should_panic: false, + solver: None, + unwind_value: None, + stubs: vec![], + } + } + + /// Return whether this is a proof harness. + pub fn is_proof_harness(&self) -> bool { + matches!(self.kind, HarnessKind::Proof | HarnessKind::ProofForContract { .. }) + } +} + /// The stubbing type. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct Stub { diff --git a/library/kani/src/arbitrary.rs b/library/kani/src/arbitrary.rs index ef6e2ef23dd4..83b113d64927 100644 --- a/library/kani/src/arbitrary.rs +++ b/library/kani/src/arbitrary.rs @@ -31,7 +31,7 @@ macro_rules! trivial_arbitrary { unsafe { crate::any_raw_internal::() } } fn any_array() -> [Self; MAX_ARRAY_LENGTH] { - unsafe { crate::any_raw_internal::<[Self; MAX_ARRAY_LENGTH]>() } + unsafe { crate::any_raw_array::() } } } }; diff --git a/library/kani/src/concrete_playback.rs b/library/kani/src/concrete_playback.rs index aa6cd7e4018d..0de51862b7d8 100644 --- a/library/kani/src/concrete_playback.rs +++ b/library/kani/src/concrete_playback.rs @@ -40,6 +40,11 @@ pub fn concrete_playback_run(mut local_concrete_vals: Vec>, pro }); } +/// Iterate over `any_raw_internal` since CBMC produces assignment per element. +pub(crate) unsafe fn any_raw_array() -> [T; N] { + [(); N].map(|_| crate::any_raw_internal::()) +} + /// Concrete playback implementation of /// kani::any_raw_internal. Because CBMC does not bother putting in /// Zero-Sized Types, those are defaulted to an empty vector. diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index 3cf46bd7af07..046c6e7a0667 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -249,21 +249,25 @@ pub fn any_where bool>(f: F) -> T { /// Note that SIZE_T must be equal the size of type T in bytes. #[inline(never)] #[cfg(not(feature = "concrete_playback"))] -pub(crate) unsafe fn any_raw_internal() -> T { - any_raw_inner::() +unsafe fn any_raw_internal() -> T { + any_raw::() } +/// This is the same as [any_raw_internal] for verification flow, but not for concrete playback. #[inline(never)] -#[cfg(feature = "concrete_playback")] -pub(crate) unsafe fn any_raw_internal() -> T { - concrete_playback::any_raw_internal::() +#[cfg(not(feature = "concrete_playback"))] +unsafe fn any_raw_array() -> [T; N] { + any_raw::<[T; N]>() } +#[cfg(feature = "concrete_playback")] +use concrete_playback::{any_raw_array, any_raw_internal}; + /// This low-level function returns nondet bytes of size T. #[rustc_diagnostic_item = "KaniAnyRaw"] #[inline(never)] #[allow(dead_code)] -fn any_raw_inner() -> T { +fn any_raw() -> T { kani_intrinsic() } diff --git a/library/kani_core/src/arbitrary.rs b/library/kani_core/src/arbitrary.rs index 7cfb649b11a0..8c6cfd335104 100644 --- a/library/kani_core/src/arbitrary.rs +++ b/library/kani_core/src/arbitrary.rs @@ -34,7 +34,7 @@ macro_rules! generate_arbitrary { unsafe { crate::kani::any_raw_internal::() } } fn any_array() -> [Self; MAX_ARRAY_LENGTH] { - unsafe { crate::kani::any_raw_internal::<[Self; MAX_ARRAY_LENGTH]>() } + unsafe { crate::kani::any_raw_array::() } } } }; diff --git a/library/kani_core/src/lib.rs b/library/kani_core/src/lib.rs index 016c805e8f8e..9baba1abe886 100644 --- a/library/kani_core/src/lib.rs +++ b/library/kani_core/src/lib.rs @@ -248,21 +248,25 @@ macro_rules! kani_intrinsics { /// Note that SIZE_T must be equal the size of type T in bytes. #[inline(never)] #[cfg(not(feature = "concrete_playback"))] - pub(crate) unsafe fn any_raw_internal() -> T { - any_raw_inner::() + unsafe fn any_raw_internal() -> T { + any_raw::() } + /// This is the same as [any_raw_internal] for verification flow, but not for concrete playback. #[inline(never)] - #[cfg(feature = "concrete_playback")] - pub(crate) unsafe fn any_raw_internal() -> T { - concrete_playback::any_raw_internal::() + #[cfg(not(feature = "concrete_playback"))] + unsafe fn any_raw_array() -> [T; N] { + any_raw::<[T; N]>() } + #[cfg(feature = "concrete_playback")] + use concrete_playback::{any_raw_array, any_raw_internal}; + /// This low-level function returns nondet bytes of size T. #[rustc_diagnostic_item = "KaniAnyRaw"] #[inline(never)] #[allow(dead_code)] - pub fn any_raw_inner() -> T { + fn any_raw() -> T { kani_intrinsic() } diff --git a/library/kani_macros/src/sysroot/contracts/mod.rs b/library/kani_macros/src/sysroot/contracts/mod.rs index defcd9dae1b4..12a1215de2c7 100644 --- a/library/kani_macros/src/sysroot/contracts/mod.rs +++ b/library/kani_macros/src/sysroot/contracts/mod.rs @@ -336,7 +336,7 @@ use proc_macro::TokenStream; use proc_macro2::{Ident, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; -use syn::{parse_macro_input, Expr, ExprClosure, ItemFn}; +use syn::{parse_macro_input, parse_quote, Expr, ExprClosure, ItemFn}; mod bootstrap; mod check; @@ -387,15 +387,12 @@ passthrough!(stub_verified, false); pub fn proof_for_contract(attr: TokenStream, item: TokenStream) -> TokenStream { let args = proc_macro2::TokenStream::from(attr); - let ItemFn { attrs, vis, sig, block } = parse_macro_input!(item as ItemFn); + let mut fn_item = parse_macro_input!(item as ItemFn); + fn_item.block.stmts.insert(0, parse_quote!(kani::internal::init_contracts();)); quote!( #[allow(dead_code)] #[kanitool::proof_for_contract = stringify!(#args)] - #(#attrs)* - #vis #sig { - kani::internal::init_contracts(); - #block - } + #fn_item ) .into() } diff --git a/tests/script-based-pre/playback_expected/config.yml b/tests/script-based-pre/playback_expected/config.yml new file mode 100644 index 000000000000..d15b5d277ed6 --- /dev/null +++ b/tests/script-based-pre/playback_expected/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: playback.sh +expected: expected diff --git a/tests/script-based-pre/playback_expected/expected b/tests/script-based-pre/playback_expected/expected new file mode 100644 index 000000000000..bedb39581b7f --- /dev/null +++ b/tests/script-based-pre/playback_expected/expected @@ -0,0 +1,8 @@ +[TEST] Generate test for playback_contract.rs... +Verification failed for - check_modify_slice +Result for playback_contract.rs: test result: FAILED. 1 passed; 1 failed + +[TEST] Generate test for playback_stubs.rs... +Verification failed for - check_lt_0 +Verification failed for - check_bad_stub +Result for playback_stubs.rs: test result: FAILED. 1 passed; 1 failed diff --git a/tests/script-based-pre/playback_expected/playback.sh b/tests/script-based-pre/playback_expected/playback.sh new file mode 100755 index 000000000000..3b358db257a2 --- /dev/null +++ b/tests/script-based-pre/playback_expected/playback.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +# This will run Kani verification in every `src/*.rs` file followed by playback command. +# Expected output is generated from individual expected files. +set -o nounset + +run() { + local input_rs=${1:?"Missing input file"} + + echo "[TEST] Generate test for ${input_rs}..." + kani "${input_rs}" \ + -Z concrete-playback --concrete-playback=inplace \ + -Z function-contracts -Z stubbing --output-format terse + + # Note that today one of the tests will succeed since the contract pre-conditions are not inserted by Kani. + # Hopefully this will change with https://github.com/model-checking/kani/issues/3326 + echo "[TEST] Run test for ${input_rs}..." + summary=$(kani playback -Z concrete-playback "${input_rs}" -- kani_concrete_playback | grep "test result") + echo "Result for ${input_rs}: ${summary}" +} + +ROOT_DIR=$(git rev-parse --show-toplevel) +MODIFIED_DIR=modified +rm -rf "${MODIFIED_DIR}" +mkdir "${MODIFIED_DIR}" + +for rs in src/*.rs; do + if [[ -e "${rs}" ]]; then + echo "Running ${rs}" + cp "${rs}" "${MODIFIED_DIR}" + pushd "${MODIFIED_DIR}" > /dev/null + run "$(basename "${rs}")" + popd > /dev/null + else + echo "No .rs files found in src directory" + exit 1 + fi +done + + # Cleanup +rm -rf "${MODIFIED_DIR}" \ No newline at end of file diff --git a/tests/script-based-pre/playback_expected/src/playback_contract.rs b/tests/script-based-pre/playback_expected/src/playback_contract.rs new file mode 100644 index 000000000000..8271e9a3d03f --- /dev/null +++ b/tests/script-based-pre/playback_expected/src/playback_contract.rs @@ -0,0 +1,19 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +//! Check that Kani correctly adds tests to when the harness is a proof for contract. +extern crate kani; + +#[kani::requires(idx < slice.len())] +#[kani::modifies(slice)] +#[kani::ensures(| _ | slice[idx] == new_val)] +fn modify_slice(slice: &mut [u32], idx: usize, new_val: u32) { + // Inject bug by incrementing index first. + let new_idx = idx + 1; + *slice.get_mut(new_idx).expect("Expected valid index, but contract is wrong") = new_val; +} + +#[kani::proof_for_contract(modify_slice)] +fn check_modify_slice() { + let mut data: [u32; 4] = kani::any(); + modify_slice(&mut data, kani::any(), kani::any()) +} diff --git a/tests/script-based-pre/playback_expected/src/playback_stubs.rs b/tests/script-based-pre/playback_expected/src/playback_stubs.rs new file mode 100644 index 000000000000..ce5107949f04 --- /dev/null +++ b/tests/script-based-pre/playback_expected/src/playback_stubs.rs @@ -0,0 +1,34 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +//! Check that Kani playback works with stubs. +#![allow(dead_code)] + +fn is_zero(val: u8) -> bool { + val == 0 +} + +fn not_zero(val: u8) -> bool { + val != 0 +} + +/// Add a harness that will fail due to incorrect stub but the test will succeed. +#[kani::proof] +#[kani::stub(is_zero, not_zero)] +fn check_bad_stub() { + assert!(is_zero(kani::any())) +} + +fn lt_zero(val: i8) -> bool { + val < 0 +} + +fn lt_ten(val: i8) -> bool { + val < 10 +} + +/// Add a harness that will fail in an equivalent way. +#[kani::proof] +#[kani::stub(lt_zero, lt_ten)] +fn check_lt_0() { + assert!(lt_zero(kani::any())) +} diff --git a/tests/script-based-pre/verify_std_cmd/verify_std.sh b/tests/script-based-pre/verify_std_cmd/verify_std.sh index 91454c4b65e7..3a24bf15241e 100755 --- a/tests/script-based-pre/verify_std_cmd/verify_std.sh +++ b/tests/script-based-pre/verify_std_cmd/verify_std.sh @@ -29,7 +29,7 @@ mod verify { use core::kani; #[kani::proof] fn check_non_zero() { - let orig: u32 = kani::any_raw_inner(); + let orig: u32 = kani::any(); if let Some(val) = core::num::NonZeroU32::new(orig) { assert!(orig == val.into()); } else { From 695e6f76832bafd379a9fea2e6434032cebb6166 Mon Sep 17 00:00:00 2001 From: Carolyn Zech Date: Wed, 31 Jul 2024 16:15:45 -0400 Subject: [PATCH 223/225] Tutorial Sections 2.1 and 2.2 Updates (#3387) Update the tutorial, namely: - Update the [bounds checking and pointers example](https://model-checking.github.io/kani/tutorial-kinds-of-failure.html#bounds-checking-and-pointers). `cargo test` catches the UB in the current example, so this PR modifies the code slightly so that `cargo test` still misses the UB, as desired. - Rather than including larger sections on experimental features throughout the tutorial, create a separate experimental features section and include (briefer) references to them in the tutorial. - The old text recommended debugging by generating a trace with `--visualize`, with a briefer mention of `--concrete-playback`. Since `--visualize` is deprecated, revise the debugging exercises to recommend `--concrete-playback` instead. 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: Jaisurya Nanduri <91620234+jaisnan@users.noreply.github.com> --- docs/src/SUMMARY.md | 7 +- docs/src/reference/attributes.md | 2 +- .../experimental/concrete-playback.md} | 19 ++--- docs/src/reference/experimental/coverage.md | 32 ++++++++ .../experimental/experimental-features.md | 5 ++ .../reference/{ => experimental}/stubbing.md | 2 +- docs/src/tutorial-first-steps.md | 60 ++------------- docs/src/tutorial-kinds-of-failure.md | 74 ++++++------------- docs/src/tutorial-real-code.md | 2 +- .../kinds-of-failure/src/bounds_check.rs | 2 +- docs/src/usage.md | 2 +- docs/src/verification-results.md | 2 +- 12 files changed, 81 insertions(+), 128 deletions(-) rename docs/src/{debugging-verification-failures.md => reference/experimental/concrete-playback.md} (87%) create mode 100644 docs/src/reference/experimental/coverage.md create mode 100644 docs/src/reference/experimental/experimental-features.md rename docs/src/reference/{ => experimental}/stubbing.md (99%) diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index ff7914c1a07a..784ec075d183 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -12,12 +12,13 @@ - [Failures that Kani can spot](./tutorial-kinds-of-failure.md) - [Loop unwinding](./tutorial-loop-unwinding.md) - [Nondeterministic variables](./tutorial-nondeterministic-variables.md) - - [Debugging verification failures](./debugging-verification-failures.md) - [Reference](./reference.md) - [Attributes](./reference/attributes.md) - - [Stubbing](./reference/stubbing.md) - + - [Experimental features](./reference/experimental/experimental-features.md) + - [Coverage](./reference/experimental/coverage.md) + - [Stubbing](./reference/experimental/stubbing.md) + - [Concrete Playback](./reference/experimental/concrete-playback.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/attributes.md b/docs/src/reference/attributes.md index bd8b80cbbdaa..4556077911fc 100644 --- a/docs/src/reference/attributes.md +++ b/docs/src/reference/attributes.md @@ -60,7 +60,7 @@ For example, the class in `Check 1: my_harness.assertion.1` is `assertion`, so t > **NOTE**: The `#[kani::should_panic]` is only recommended for writing > harnesses which complement existing harnesses that don't use the same -> attribute. In order words, it's only recommended to write *negative harnesses* +> attribute. In other words, it's only recommended to write *negative harnesses* > after having written *positive* harnesses that successfully verify interesting > properties about the function under verification. diff --git a/docs/src/debugging-verification-failures.md b/docs/src/reference/experimental/concrete-playback.md similarity index 87% rename from docs/src/debugging-verification-failures.md rename to docs/src/reference/experimental/concrete-playback.md index c5dae6e74308..237f5673caf0 100644 --- a/docs/src/debugging-verification-failures.md +++ b/docs/src/reference/experimental/concrete-playback.md @@ -1,20 +1,13 @@ -# Debugging verification failures +# Concrete Playback -When the result of a certain check comes back as a `FAILURE`, -Kani offers different options to help debug: -* `--concrete-playback`. This _experimental_ feature generates a Rust unit test case that plays back a failing -proof harness using a concrete counterexample. -* `--visualize`. This feature generates an HTML text-based trace that -enumerates the execution steps leading to the check failure. - -## Concrete playback +When the result of a certain check comes back as a `FAILURE`, Kani offers the `concrete-playback` option to help debug. This feature generates a Rust unit test case that plays back a failing proof harness using a concrete counterexample. When concrete playback is enabled, Kani will generate unit tests for assertions that failed during verification, as well as cover statements that are reachable. These tests can then be executed using Kani's playback subcommand. -### Usage +## Usage In order to enable this feature, run Kani with the `-Z concrete-playback --concrete-playback=[print|inplace]` flag. After getting a verification failure, Kani will generate a Rust unit test case that plays back a failing @@ -46,7 +39,7 @@ The output will have a line in the beginning like You can further debug the binary with tools like `rust-gdb` or `lldb`. -### Example +## Example Running `kani -Z concrete-playback --concrete-playback=print` on the following source file: ```rust @@ -75,7 +68,7 @@ Here, `133` and `35207` are the concrete values that, when substituted for `a` a cause an assertion failure. `vec![135, 137]` is the byte array representation of `35207`. -### Request for comments +## 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, @@ -83,7 +76,7 @@ please add them to [this GitHub issue](https://github.com/model-checking/kani/is We are tracking the existing feature requests in [this GitHub milestone](https://github.com/model-checking/kani/milestone/10). -### Limitations +## Limitations * This feature does not generate unit tests for failing non-panic checks (e.g., UB checks). This is because checks would not trigger runtime errors during concrete playback. diff --git a/docs/src/reference/experimental/coverage.md b/docs/src/reference/experimental/coverage.md new file mode 100644 index 000000000000..fb73d5f7c05b --- /dev/null +++ b/docs/src/reference/experimental/coverage.md @@ -0,0 +1,32 @@ +## Coverage + +Recall our `estimate_size` example from [First steps](../../tutorial-first-steps.md), +where we wrote a proof harness constraining the range of inputs to integers less than 4096: + +```rust +{{#include ../../tutorial/first-steps-v2/src/lib.rs:kani}} +``` + +We must wonder if we've really fully tested our function. +What if we revise the function, but forget to update the assumption in our proof harness to cover the new range of inputs? + +Fortunately, Kani is able to report a coverage metric for each proof harness. +In the `first-steps-v2` directory, try running: + +``` +cargo kani --coverage -Z line-coverage --harness verify_success +``` + +which verifies the harness, then prints coverage information for each line. +In this case, we see that each line of `estimate_size` is followed by `FULL`, indicating that our proof harness provides full coverage. + +Try changing the assumption in the proof harness to `x < 2048`. +Now the harness won't be testing all possible cases. +Rerun the command. +You'll see this line: + +``` +src/lib.rs, 24, NONE +``` + +which indicates that the proof no longer covers line 24, which addresses the case where `x >= 2048`. diff --git a/docs/src/reference/experimental/experimental-features.md b/docs/src/reference/experimental/experimental-features.md new file mode 100644 index 000000000000..bd9fb6cd8572 --- /dev/null +++ b/docs/src/reference/experimental/experimental-features.md @@ -0,0 +1,5 @@ +# Experimental Features + +We elaborate on some of the more commonly used experimental features in Kani. +This is not an exhaustive list; to see all of Kani's experimental features, run `cargo kani --help`. +To use an experimental feature, invoke Kani with the `--unstable` or `-Z` flag followed by the name of the feature. \ No newline at end of file diff --git a/docs/src/reference/stubbing.md b/docs/src/reference/experimental/stubbing.md similarity index 99% rename from docs/src/reference/stubbing.md rename to docs/src/reference/experimental/stubbing.md index b4eeb4fe6b98..0ffbba5f0b0b 100644 --- a/docs/src/reference/stubbing.md +++ b/docs/src/reference/experimental/stubbing.md @@ -113,7 +113,7 @@ In the following, we describe all the limitations of the stubbing feature. The usage of stubbing is limited to the verification of a single harness. Therefore, users are **required to pass the `--harness` option** when using the stubbing feature. -In addition, this feature **isn't compatible with [concrete playback](../debugging-verification-failures.md#concrete-playback)**. +In addition, this feature **isn't compatible with [concrete playback](./concrete-playback.md)**. ### Support diff --git a/docs/src/tutorial-first-steps.md b/docs/src/tutorial-first-steps.md index 57e70edb96dd..bbfd236c6b58 100644 --- a/docs/src/tutorial-first-steps.md +++ b/docs/src/tutorial-first-steps.md @@ -53,40 +53,11 @@ Kani has immediately found a failure. Notably, we haven't had to write explicit assertions in our proof harness: by default, Kani will find a host of erroneous conditions which include a reachable call to `panic` or a failing `assert`. If Kani had run successfully on this harness, this amounts to a mathematical proof that there is no input that could cause a panic in `estimate_size`. -### Getting a trace +> By default, Kani only reports failures, not how the failure happened. +> In this example, it would be nice to get a concrete example of a value of `x` that triggers the failure. +> Kani offers an (experimental) [concrete playback](reference/experimental/concrete-playback.md) feature that serves this purpose. +> As an exercise, try applying concrete playback to this example and see what Kani outputs. -By default, Kani only reports failures, not how the failure happened. -In this running example, it seems obvious what we're interested in (the value of `x` that caused the failure) because we just have one unknown input at the start (similar to the property test), but that's kind of a special case. -In general, understanding how a failure happened requires exploring a full (potentially large) _execution trace_. - -An execution trace is a record of exactly how a failure can occur. -Nondeterminism (like a call to `kani::any()`, which could return any value) can appear in the middle of its execution. -A trace is a record of exactly how execution proceeded, including concrete choices (like `1023`) for all of these nondeterministic values. - -To get a trace for a failing check in Kani, run: - -``` -cargo kani --visualize --enable-unstable -``` - -This command runs Kani and generates an HTML report that includes a trace. -Open the report with your preferred browser. -Under the "Errors" heading, click on the "trace" link to find the trace for this failure. - -From this trace report, we can filter through it to find relevant lines. -A good rule of thumb is to search for either `kani::any()` or assignments to variables you're interested in. -At present time, an unfortunate amount of generated code is present in the trace. -This code isn't a part of the Rust code you wrote, but is an internal implementation detail of how Kani runs proof harnesses. -Still, searching for `kani::any()` quickly finds us these lines: - -``` -let x: u32 = kani::any(); -x = 1023u -``` - -Here we're seeing the line of code and the value assigned in this particular trace. -Like property testing, this is just one **example** of a failure. -To proceed, we recommend fixing the code to avoid this particular issue and then re-running Kani to see if you find more issues. ### Exercise: Try other failures @@ -193,25 +164,6 @@ Here's a revised example of the proof harness, one that now succeeds: {{#include tutorial/first-steps-v2/src/lib.rs:kani}} ``` -But now we must wonder if we've really fully tested our function. -What if we revise the function, but forget to update the assumption in our proof harness to cover the new range of inputs? - -Fortunately, Kani is able to report a coverage metric for each proof harness. -Try running: - -``` -cargo kani --visualize --harness verify_success -``` - -The beginning of the report includes coverage information. -Clicking through to the file will show fully-covered lines in green. -Lines not covered by our proof harness will show in red. - -Try changing the assumption in the proof harness to `x < 2048`. -Now the harness won't be testing all possible cases. -Rerun `cargo kani --visualize`. -Look at the report: you'll see we no longer have 100% coverage of the function. - ## Summary In this section: @@ -219,6 +171,4 @@ In this section: 1. We saw Kani find panics, assertion failures, and even some other failures like unsafe dereferencing of null pointers. 2. We saw Kani find failures that testing could not easily find. 3. We saw how to write a proof harness and use `kani::any()`. -4. We saw how to get a failing **trace** using `kani --visualize` -5. We saw how proof harnesses are used to set up preconditions with `kani::assume()`. -6. We saw how to obtain **coverage** metrics and use them to ensure our proofs are covering as much as they should be. +4. We saw how proof harnesses are used to set up preconditions with `kani::assume()`. diff --git a/docs/src/tutorial-kinds-of-failure.md b/docs/src/tutorial-kinds-of-failure.md index 29236b95db0c..00f9408ca709 100644 --- a/docs/src/tutorial-kinds-of-failure.md +++ b/docs/src/tutorial-kinds-of-failure.md @@ -25,7 +25,7 @@ This property test will immediately find a failing case, thanks to Rust's built- But what if we change this function to use unsafe Rust? ```rust -return unsafe { *a.get_unchecked(i % a.len() + 1) }; +return unsafe { *a.as_ptr().add(i % a.len() + 1) }; ``` Now the error becomes invisible to this test: @@ -55,7 +55,7 @@ cargo kani --harness bound_check We still see a failure from Kani, even without Rust's runtime bounds checking. > Also, notice there were many checks in the verification output. -> (At time of writing, 351.) +> (At time of writing, 345.) > This is a result of using the standard library `Vec` implementation, which means our harness actually used quite a bit of code, short as it looks. > Kani is inserting a lot more checks than appear as asserts in our code, so the output can be large. @@ -63,7 +63,7 @@ We get the following summary at the end: ``` SUMMARY: - ** 1 of 351 failed + ** 1 of 345 failed (8 unreachable) Failed Checks: dereference failure: pointer outside object bounds File: "./src/bounds_check.rs", line 11, in bounds_check::get_wrapped @@ -79,71 +79,44 @@ Consider trying a few more small exercises with this example: 1. Exercise: Switch back to the normal/safe indexing operation and re-try Kani. How does Kani's output change, compared to the unsafe operation? (Try predicting the answer, then seeing if you got it right.) -2. Exercise: [Remember how to get a trace from Kani?](./tutorial-first-steps.md#getting-a-trace) Find out what inputs it failed on. +2. Exercise: Try Kani's experimental [concrete playback](reference/experimental/concrete-playback.md) feature on this example. 3. Exercise: Fix the error, run Kani, and see a successful verification. 4. Exercise: Try switching back to the unsafe code (now with the error fixed) and re-run Kani. Does it still verify successfully?
Click to see explanation for exercise 1 -Having switched back to the safe indexing operation, Kani reports two failures: +Having switched back to the safe indexing operation, Kani reports a bounds check failure: ``` -SUMMARY: - ** 2 of 350 failed +SUMMARY: + ** 1 of 343 failed (8 unreachable) Failed Checks: index out of bounds: the length is less than or equal to the given index - File: "./src/bounds_check.rs", line 11, in bounds_check::get_wrapped -Failed Checks: dereference failure: pointer outside object bounds - File: "./src/bounds_check.rs", line 11, in bounds_check::get_wrapped + File: "src/bounds_check.rs", line 11, in bounds_check::get_wrapped VERIFICATION:- FAILED ``` -The first is Rust's runtime bounds checking for the safe indexing operation. -The second is Kani's check to ensure the pointer operation is actually safe. -This pattern (two checks for similar issues in safe Rust code) is common to see, and we'll see it again in the next section. - -> **NOTE**: While Kani will always be checking for both properties, [in the future the output here may change](https://github.com/model-checking/kani/issues/1349). -> You might have noticed that the bad pointer dereference can't happen, since the bounds check would panic first. -> In the future, Kani's output may report only the bounds checking failure in this example. -
Click to see explanation for exercise 2 -Having run `cargo kani --harness bound_check --visualize` and clicked on one of the failures to see a trace, there are three things to immediately notice: - -1. This trace is huge. Because the standard library `Vec` is involved, there's a lot going on. -2. The top of the trace file contains some "trace navigation tips" that might be helpful in navigating the trace. -3. There's a lot of generated code and it's really hard to just read the trace itself. - -To navigate this trace to find the information you need, we again recommend searching for things you expect to be somewhere in the trace: - -1. Search the page for `kani::any` or ` =` such as `size =` or `let size`. -We can use this to find out what example values lead to a problem. -In this case, where we just have a couple of `kani::any` values in our proof harness, we can learn a lot just by seeing what these are. -In this trace we find (and the values you get may be different): - -``` -Step 36: Function bound_check, File src/bounds_check.rs, Line 37 -let size: usize = kani::any(); -size = 2464ul - -Step 39: Function bound_check, File src/bounds_check.rs, Line 39 -let index: usize = kani::any(); -index = 2463ul +`cargo kani -Z concrete-playback --concrete-playback=inplace --harness bound_check` produces the following test: +``` +rust +#[test] +fn kani_concrete_playback_bound_check_4752536404478138800() { + let concrete_vals: Vec> = vec![ + // 1ul + vec![1, 0, 0, 0, 0, 0, 0, 0], + // 18446744073709551615ul + vec![255, 255, 255, 255, 255, 255, 255, 255], + ]; + kani::concrete_playback_run(concrete_vals, bound_check); +} ``` - -You may see different values here, as it depends on the solver's behavior. - -2. Try searching for `failure:`. This will be near the end of the page. -You can now search upwards from a failure to see what values certain variables had. -Sometimes it can be helpful to change the source code to add intermediate variables, so their value is visible in the trace. -For instance, you might want to compute the index before indexing into the array. -That way you'd see in the trace exactly what value is being used. - -These two techniques should help you find both the nondeterministic inputs, and the values that were involved in the failing assertion. +which indicates that substituting the concrete values `size = 1` and `index = 2^64` in our proof harness will produce the out of bounds access.
@@ -247,6 +220,5 @@ In this section: 1. We saw Kani spot out-of-bounds accesses. 2. We saw Kani spot actually-unsafe dereferencing of a raw pointer to invalid memory. -3. We got more experience reading the traces that Kani generates, to debug a failing proof harness. 3. We saw Kani spot a division by zero error and an overflowing addition. -5. As an exercise, we tried proving an assertion (finding the midpoint) that was not completely trivial. +4. As an exercise, we tried proving an assertion (finding the midpoint) that was not completely trivial. diff --git a/docs/src/tutorial-real-code.md b/docs/src/tutorial-real-code.md index c4bad5a2d82e..3dd216cd6258 100644 --- a/docs/src/tutorial-real-code.md +++ b/docs/src/tutorial-real-code.md @@ -74,7 +74,7 @@ A first proof will likely start in the following form: Running Kani on this simple starting point will help figure out: 1. What unexpected constraints might be needed on your inputs (using `kani::assume`) to avoid "expected" failures. -2. Whether you're over-constrained. Check the coverage report using `--visualize`. Ideally you'd see 100% coverage, and if not, it's usually because you've assumed too much (thus over-constraining the inputs). +2. Whether you're over-constrained. Check the coverage report using `--coverage -Z line-coverage`. Ideally you'd see 100% coverage, and if not, it's usually because you've assumed too much (thus over-constraining the inputs). 3. Whether Kani will support all the Rust features involved. 4. Whether you've started with a tractable problem. (Remember to try setting `#[kani::unwind(1)]` to force early termination and work up from there.) diff --git a/docs/src/tutorial/kinds-of-failure/src/bounds_check.rs b/docs/src/tutorial/kinds-of-failure/src/bounds_check.rs index 9204d8f0f17e..c4a30982c95d 100644 --- a/docs/src/tutorial/kinds-of-failure/src/bounds_check.rs +++ b/docs/src/tutorial/kinds-of-failure/src/bounds_check.rs @@ -13,7 +13,7 @@ fn get_wrapped(i: usize, a: &[u32]) -> u32 { // ANCHOR_END: code // Alternative unsafe return for the above function: -// return unsafe { *a.get_unchecked(i % a.len() + 1) }; +// return unsafe { *a.as_ptr().add(i % a.len() + 1) }; #[cfg(test)] mod tests { diff --git a/docs/src/usage.md b/docs/src/usage.md index 459916c87222..aaa5d3fa234c 100644 --- a/docs/src/usage.md +++ b/docs/src/usage.md @@ -26,7 +26,7 @@ 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 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 [debugging verification failures](./debugging-verification-failures.md) section. + 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. * `--visualize`: _Experimental_, `--enable-unstable` feature that generates an HTML report providing traces (i.e., counterexamples) for each failure found by Kani. diff --git a/docs/src/verification-results.md b/docs/src/verification-results.md index a8187163d41b..100c9ed554be 100644 --- a/docs/src/verification-results.md +++ b/docs/src/verification-results.md @@ -38,7 +38,7 @@ Check 4: success_example.assertion.4 ``` 2. `FAILURE`: This indicates that the check failed (i.e., the property doesn't -hold). In this case, please see the [debugging verification failures](./debugging-verification-failures.md) +hold). In this case, please see the [concrete playback](./experimental/concrete-playback.md) section for more help. Example: From 9d1cc0ee89c8510c7208867cd4becd9d3dc6b9a5 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 31 Jul 2024 17:01:16 -0700 Subject: [PATCH 224/225] Add code scanner tool (#3120) This is just a utility tool that allow us to scan a crate and extract some metrics out of it. I also added a script to scan the standard library. Currently, the tool will produce a few CSV files with raw data. --------- Co-authored-by: Qinheping Hu --- Cargo.lock | 31 + Cargo.toml | 1 + scripts/std-analysis.sh | 115 ++++ tests/perf/s2n-quic | 2 +- .../script-based-pre/tool-scanner/config.yml | 4 + .../tool-scanner/scanner-test.expected | 6 + .../tool-scanner/scanner-test.sh | 20 + tests/script-based-pre/tool-scanner/test.rs | 77 +++ tools/scanner/Cargo.toml | 23 + tools/scanner/build.rs | 26 + tools/scanner/src/analysis.rs | 629 ++++++++++++++++++ tools/scanner/src/bin/scan.rs | 31 + tools/scanner/src/lib.rs | 103 +++ 13 files changed, 1067 insertions(+), 1 deletion(-) create mode 100755 scripts/std-analysis.sh create mode 100644 tests/script-based-pre/tool-scanner/config.yml create mode 100644 tests/script-based-pre/tool-scanner/scanner-test.expected create mode 100755 tests/script-based-pre/tool-scanner/scanner-test.sh create mode 100644 tests/script-based-pre/tool-scanner/test.rs create mode 100644 tools/scanner/Cargo.toml create mode 100644 tools/scanner/build.rs create mode 100644 tools/scanner/src/analysis.rs create mode 100644 tools/scanner/src/bin/scan.rs create mode 100644 tools/scanner/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 01dbccdd546a..d68b8db21918 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -288,6 +288,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + [[package]] name = "either" version = "1.13.0" @@ -892,6 +913,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scanner" +version = "0.0.0" +dependencies = [ + "csv", + "serde", + "strum", + "strum_macros", +] + [[package]] name = "scopeguard" version = "1.2.0" diff --git a/Cargo.toml b/Cargo.toml index 1b4933c5bdcf..68b5bcc20ff3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ members = [ "library/std", "tools/compiletest", "tools/build-kani", + "tools/scanner", "kani-driver", "kani-compiler", "kani_metadata", diff --git a/scripts/std-analysis.sh b/scripts/std-analysis.sh new file mode 100755 index 000000000000..87ac991cb00d --- /dev/null +++ b/scripts/std-analysis.sh @@ -0,0 +1,115 @@ +#!/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 "-------------------------------------------------------" diff --git a/tests/perf/s2n-quic b/tests/perf/s2n-quic index 71f8d9f5aafb..2d5e891f3fdc 160000 --- a/tests/perf/s2n-quic +++ b/tests/perf/s2n-quic @@ -1 +1 @@ -Subproject commit 71f8d9f5aafbf59f31ad85eeb7b4b67a7564a685 +Subproject commit 2d5e891f3fdc8a88b2d457baceedea5751efaa0d diff --git a/tests/script-based-pre/tool-scanner/config.yml b/tests/script-based-pre/tool-scanner/config.yml new file mode 100644 index 000000000000..6fd2895971a4 --- /dev/null +++ b/tests/script-based-pre/tool-scanner/config.yml @@ -0,0 +1,4 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT +script: scanner-test.sh +expected: scanner-test.expected diff --git a/tests/script-based-pre/tool-scanner/scanner-test.expected b/tests/script-based-pre/tool-scanner/scanner-test.expected new file mode 100644 index 000000000000..c8f9af0ef1b7 --- /dev/null +++ b/tests/script-based-pre/tool-scanner/scanner-test.expected @@ -0,0 +1,6 @@ +2 test_scan_fn_loops.csv +16 test_scan_functions.csv +5 test_scan_input_tys.csv +14 test_scan_overall.csv +3 test_scan_recursion.csv +5 test_scan_unsafe_ops.csv diff --git a/tests/script-based-pre/tool-scanner/scanner-test.sh b/tests/script-based-pre/tool-scanner/scanner-test.sh new file mode 100755 index 000000000000..2cd5a33a3f8e --- /dev/null +++ b/tests/script-based-pre/tool-scanner/scanner-test.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + +set -e + +# Run this inside a tmp folder in the current directory +OUT_DIR=output_dir +# Ensure output folder is clean +rm -rf ${OUT_DIR} +mkdir output_dir +# Move the original source to the output folder since it will be modified +cp test.rs ${OUT_DIR} +pushd $OUT_DIR + +cargo run -p scanner test.rs --crate-type lib +wc -l *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 new file mode 100644 index 000000000000..24b346e535b5 --- /dev/null +++ b/tests/script-based-pre/tool-scanner/test.rs @@ -0,0 +1,77 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +//! Sanity check for the utility tool `scanner`. + +pub fn check_outer_coercion() { + assert!(false); +} + +unsafe fn do_nothing() {} + +pub fn generic() -> T { + unsafe { do_nothing() }; + T::default() +} + +pub struct RecursiveType { + pub inner: Option<*const RecursiveType>, +} + +pub enum RecursiveEnum { + Base, + Recursion(Box), + RefCell(std::cell::RefCell), +} + +pub fn recursive_type(input1: RecursiveType, input2: RecursiveEnum) { + let _ = (input1, input2); +} + +pub fn with_iterator(input: &[usize]) -> usize { + input + .iter() + .copied() + .find(|e| *e == 0) + .unwrap_or_else(|| input.iter().fold(0, |acc, i| acc + 1)) +} + +static mut COUNTER: Option = Some(0); +static OK: bool = true; + +pub unsafe fn next_id() -> usize { + let sum = COUNTER.unwrap() + 1; + COUNTER = Some(sum); + sum +} + +pub unsafe fn current_id() -> usize { + COUNTER.unwrap() +} + +pub fn ok() -> bool { + OK +} + +pub unsafe fn raw_to_ref<'a, T>(raw: *const T) -> &'a T { + &*raw +} + +pub fn recursion_begin(stop: bool) { + if !stop { + recursion_tail() + } +} + +pub fn recursion_tail() { + recursion_begin(false); + not_recursive(); +} + +pub fn start_recursion() { + recursion_begin(true); +} + +pub fn not_recursive() { + let _ = ok(); +} diff --git a/tools/scanner/Cargo.toml b/tools/scanner/Cargo.toml new file mode 100644 index 000000000000..edbd330bea47 --- /dev/null +++ b/tools/scanner/Cargo.toml @@ -0,0 +1,23 @@ +# Copyright Kani Contributors +# SPDX-License-Identifier: Apache-2.0 OR MIT + + +[package] +name = "scanner" +description = "A rustc extension used to scan rust features in a crate" +version = "0.0.0" +edition = "2021" +license = "MIT OR Apache-2.0" +publish = false + +[dependencies] +csv = "1.3" +serde = {version = "1", features = ["derive"]} +strum = "0.26" +strum_macros = "0.26" + +[package.metadata.rust-analyzer] +# This crate uses rustc crates. +# More info: https://github.com/rust-analyzer/rust-analyzer/pull/7891 +rustc_private = true + diff --git a/tools/scanner/build.rs b/tools/scanner/build.rs new file mode 100644 index 000000000000..775a0f507a45 --- /dev/null +++ b/tools/scanner/build.rs @@ -0,0 +1,26 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use std::env; +use std::path::PathBuf; + +macro_rules! path_str { + ($input:expr) => { + String::from( + $input + .iter() + .collect::() + .to_str() + .unwrap_or_else(|| panic!("Invalid path {}", stringify!($input))), + ) + }; +} + +/// Configure the compiler to properly link the scanner binary with rustc's library. +pub fn main() { + // Add rustup to the rpath in order to properly link with the correct rustc version. + let rustup_home = env::var("RUSTUP_HOME").unwrap(); + let rustup_tc = env::var("RUSTUP_TOOLCHAIN").unwrap(); + let rustup_lib = path_str!([&rustup_home, "toolchains", &rustup_tc, "lib"]); + println!("cargo:rustc-link-arg-bin=scan=-Wl,-rpath,{rustup_lib}"); +} diff --git a/tools/scanner/src/analysis.rs b/tools/scanner/src/analysis.rs new file mode 100644 index 000000000000..c376af9662f8 --- /dev/null +++ b/tools/scanner/src/analysis.rs @@ -0,0 +1,629 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +//! Provide different static analysis to be performed in the crate under compilation + +use crate::info; +use csv::WriterBuilder; +use serde::{ser::SerializeStruct, Serialize, Serializer}; +use stable_mir::mir::mono::Instance; +use stable_mir::mir::visit::{Location, PlaceContext, PlaceRef}; +use stable_mir::mir::{ + Body, MirVisitor, Mutability, ProjectionElem, Safety, Terminator, TerminatorKind, +}; +use stable_mir::ty::{AdtDef, AdtKind, FnDef, GenericArgs, MirConst, RigidTy, Ty, TyKind}; +use stable_mir::visitor::{Visitable, Visitor}; +use stable_mir::{CrateDef, CrateItem}; +use std::collections::{HashMap, HashSet}; +use std::ops::ControlFlow; +use std::path::{Path, PathBuf}; + +#[derive(Clone, Debug)] +pub struct OverallStats { + /// The key and value of each counter. + counters: Vec<(&'static str, usize)>, + /// TODO: Group stats per function. + fn_stats: HashMap, +} + +#[derive(Clone, Debug, Serialize)] +struct FnStats { + name: String, + is_unsafe: Option, + has_unsafe_ops: Option, + has_unsupported_input: Option, + has_loop: Option, +} + +impl FnStats { + fn new(fn_item: CrateItem) -> FnStats { + FnStats { + name: fn_item.name(), + is_unsafe: None, + has_unsafe_ops: None, + has_unsupported_input: None, + // TODO: Implement this. + has_loop: None, + } + } +} + +impl OverallStats { + pub fn new() -> OverallStats { + let all_items = stable_mir::all_local_items(); + let fn_stats: HashMap<_, _> = all_items + .into_iter() + .filter_map(|item| item.ty().kind().is_fn().then_some((item, FnStats::new(item)))) + .collect(); + let counters = vec![("total_fns", fn_stats.len())]; + OverallStats { counters, fn_stats } + } + + pub fn store_csv(&self, base_path: PathBuf, file_stem: &str) { + let filename = format!("{}_overall", file_stem); + 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 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::>()); + } + + /// Iterate over all functions defined in this crate and log generic vs monomorphic. + pub fn generic_fns(&mut self) { + let all_items = stable_mir::all_local_items(); + let fn_items = + all_items.into_iter().filter(|item| item.ty().kind().is_fn()).collect::>(); + let (mono_fns, generics) = fn_items + .iter() + .partition::, _>(|fn_item| Instance::try_from(**fn_item).is_ok()); + self.counters + .extend_from_slice(&[("generic_fns", generics.len()), ("mono_fns", mono_fns.len())]); + } + + /// Iterate over all functions defined in this crate and log safe vs unsafe. + pub fn safe_fns(&mut self, _base_filename: PathBuf) { + let all_items = stable_mir::all_local_items(); + let (unsafe_fns, safe_fns) = all_items + .into_iter() + .filter_map(|item| { + let kind = item.ty().kind(); + if !kind.is_fn() { + return None; + }; + let fn_sig = kind.fn_sig().unwrap(); + let is_unsafe = fn_sig.skip_binder().safety == Safety::Unsafe; + self.fn_stats.get_mut(&item).unwrap().is_unsafe = Some(is_unsafe); + Some((item, is_unsafe)) + }) + .partition::, _>(|(_, is_unsafe)| *is_unsafe); + self.counters + .extend_from_slice(&[("safe_fns", safe_fns.len()), ("unsafe_fns", unsafe_fns.len())]); + } + + /// Iterate over all functions defined in this crate and log the inputs. + pub fn supported_inputs(&mut self, filename: PathBuf) { + let all_items = stable_mir::all_local_items(); + let (supported, unsupported) = all_items + .into_iter() + .filter_map(|item| { + let kind = item.ty().kind(); + if !kind.is_fn() { + return None; + }; + let fn_sig = kind.fn_sig().unwrap(); + let props = FnInputProps::new(item.name()).collect(fn_sig.skip_binder().inputs()); + self.fn_stats.get_mut(&item).unwrap().has_unsupported_input = + Some(!props.is_supported()); + Some(props) + }) + .partition::, _>(|props| props.is_supported()); + self.counters.extend_from_slice(&[ + ("supported_inputs", supported.len()), + ("unsupported_inputs", unsupported.len()), + ]); + dump_csv(filename, &unsupported); + } + + /// Iterate over all functions defined in this crate and log any unsafe operation. + pub fn unsafe_operations(&mut self, filename: PathBuf) { + let all_items = stable_mir::all_local_items(); + let (has_unsafe, no_unsafe) = all_items + .into_iter() + .filter_map(|item| { + let kind = item.ty().kind(); + if !kind.is_fn() { + return None; + }; + let unsafe_ops = FnUnsafeOperations::new(item.name()).collect(&item.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 = + Some(unsafe_ops.has_unsafe()); + Some((is_unsafe, unsafe_ops)) + }) + .partition::, _>(|(_, props)| props.has_unsafe()); + self.counters.extend_from_slice(&[ + ("has_unsafe_ops", has_unsafe.len()), + ("no_unsafe_ops", no_unsafe.len()), + ("safe_abstractions", has_unsafe.iter().filter(|(is_unsafe, _)| !is_unsafe).count()), + ]); + dump_csv(filename, &has_unsafe.into_iter().map(|(_, props)| props).collect::>()); + } + + /// Iterate over all functions defined in this crate and log any loop / "hidden" loop. + /// + /// A hidden loop is a call to a iterator function that has a loop inside. + pub fn loops(&mut self, filename: PathBuf) { + let all_items = stable_mir::all_local_items(); + let (has_loops, no_loops) = all_items + .into_iter() + .filter_map(|item| { + let kind = item.ty().kind(); + if !kind.is_fn() { + return None; + }; + Some(FnLoops::new(item.name()).collect(&item.body())) + }) + .partition::, _>(|props| props.has_loops()); + self.counters + .extend_from_slice(&[("has_loops", has_loops.len()), ("no_loops", no_loops.len())]); + dump_csv(filename, &has_loops); + } + + /// Create a callgraph for this crate and try to find recursive calls. + pub fn recursion(&mut self, filename: PathBuf) { + let all_items = stable_mir::all_local_items(); + let recursions = Recursion::collect(&all_items); + self.counters.extend_from_slice(&[ + ("with_recursion", recursions.with_recursion.len()), + ("recursive_fns", recursions.recursive_fns.len()), + ]); + dump_csv( + filename, + &recursions + .with_recursion + .iter() + .map(|def| { + ( + def.name(), + if recursions.recursive_fns.contains(&def) { "recursive" } else { "" }, + ) + }) + .collect::>(), + ); + } +} + +macro_rules! fn_props { + ($(#[$attr:meta])* + struct $name:ident { + $( + $(#[$prop_attr:meta])* + $prop:ident, + )+ + }) => { + #[derive(Debug)] + struct $name { + fn_name: String, + $($(#[$prop_attr])* $prop: usize,)+ + } + + impl $name { + const fn num_props() -> usize { + [$(stringify!($prop),)+].len() + } + + fn new(fn_name: String) -> Self { + Self { fn_name, $($prop: 0,)+} + } + } + + /// Need to manually implement this, since CSV serializer does not support map (i.e.: flatten). + impl Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("FnInputProps", Self::num_props())?; + state.serialize_field("fn_name", &self.fn_name)?; + $(state.serialize_field(stringify!($prop), &self.$prop)?;)+ + state.end() + } + } + }; +} + +fn_props! { + struct FnInputProps { + boxes, + closures, + coroutines, + floats, + fn_defs, + fn_ptrs, + generics, + interior_muts, + raw_ptrs, + recursive_types, + mut_refs, + simd, + unions, + } +} + +impl FnInputProps { + pub fn collect(mut self, inputs: &[Ty]) -> FnInputProps { + for input in inputs { + let mut visitor = TypeVisitor { metrics: &mut self, visited: HashSet::new() }; + let _ = visitor.visit_ty(input); + } + self + } + + pub fn is_supported(&self) -> bool { + (self.closures + + self.coroutines + + self.floats + + self.fn_defs + + self.fn_ptrs + + self.interior_muts + + self.raw_ptrs + + self.recursive_types + + self.mut_refs) + == 0 + } +} + +struct TypeVisitor<'a> { + metrics: &'a mut FnInputProps, + visited: HashSet, +} + +impl<'a> TypeVisitor<'a> { + pub fn visit_variants(&mut self, def: AdtDef, _args: &GenericArgs) -> ControlFlow<()> { + for variant in def.variants_iter() { + for field in variant.fields() { + self.visit_ty(&field.ty())? + } + } + ControlFlow::Continue(()) + } +} + +impl<'a> Visitor for TypeVisitor<'a> { + type Break = (); + + fn visit_ty(&mut self, ty: &Ty) -> ControlFlow { + if self.visited.contains(ty) { + self.metrics.recursive_types += 1; + ControlFlow::Continue(()) + } else { + self.visited.insert(*ty); + let kind = ty.kind(); + match kind { + TyKind::Alias(..) => {} + TyKind::Param(_) => self.metrics.generics += 1, + TyKind::RigidTy(rigid) => match rigid { + RigidTy::Coroutine(..) => self.metrics.coroutines += 1, + RigidTy::Closure(..) => self.metrics.closures += 1, + RigidTy::FnDef(..) => self.metrics.fn_defs += 1, + RigidTy::FnPtr(..) => self.metrics.fn_ptrs += 1, + RigidTy::Float(..) => self.metrics.floats += 1, + RigidTy::RawPtr(..) => self.metrics.raw_ptrs += 1, + RigidTy::Ref(_, _, Mutability::Mut) => self.metrics.mut_refs += 1, + RigidTy::Adt(def, args) => match def.kind() { + AdtKind::Union => self.metrics.unions += 1, + _ => { + let name = def.name(); + if def.is_box() { + self.metrics.boxes += 1; + } else if name.ends_with("UnsafeCell") { + self.metrics.interior_muts += 1; + } else { + self.visit_variants(def, &args)?; + } + } + }, + _ => {} + }, + kind => unreachable!("Expected rigid type, but found: {kind:?}"), + } + ty.super_visit(self) + } + } +} + +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(); + for d in data { + writer.serialize(d).unwrap(); + } +} + +fn_props! { + 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. + unsafe_call, + /// Access or modify a mutable static variable. + unsafe_static_access, + /// Access fields of unions. + unsafe_union_access, + } +} + +impl FnUnsafeOperations { + pub fn collect(self, body: &Body) -> FnUnsafeOperations { + let mut visitor = BodyVisitor { props: self, body }; + visitor.visit_body(body); + visitor.props + } + + pub fn has_unsafe(&self) -> bool { + (self.inline_assembly + + self.unsafe_static_access + + self.unsafe_dereference + + self.unsafe_union_access + + self.unsafe_call) + > 0 + } +} + +struct BodyVisitor<'a> { + props: FnUnsafeOperations, + body: &'a Body, +} + +impl<'a> MirVisitor for BodyVisitor<'a> { + 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 { + self.props.unsafe_call += 1; + } + } + TerminatorKind::InlineAsm { .. } => self.props.inline_assembly += 1, + _ => { /* safe */ } + } + self.super_terminator(term, location) + } + + fn visit_projection_elem( + &mut self, + place: PlaceRef, + elem: &ProjectionElem, + ptx: PlaceContext, + location: Location, + ) { + match elem { + ProjectionElem::Deref => { + if place.ty(self.body.locals()).unwrap().kind().is_raw_ptr() { + self.props.unsafe_dereference += 1; + } + } + ProjectionElem::Field(_, ty) => { + if ty.kind().is_union() { + self.props.unsafe_union_access += 1; + } + } + ProjectionElem::Downcast(_) => {} + ProjectionElem::OpaqueCast(_) => {} + ProjectionElem::Subtype(_) => {} + ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } => { /* safe */ } + } + self.super_projection_elem(elem, ptx, location) + } + + fn visit_mir_const(&mut self, constant: &MirConst, location: Location) { + if constant.ty().kind().is_raw_ptr() { + self.props.unsafe_static_access += 1; + } + self.super_mir_const(constant, location) + } +} + +fn_props! { + struct FnLoops { + iterators, + nested_loops, + /// TODO: Collect loops. + loops, + } +} + +impl FnLoops { + pub fn collect(self, body: &Body) -> FnLoops { + let mut visitor = IteratorVisitor { props: self, body }; + visitor.visit_body(body); + visitor.props + } + + pub fn has_loops(&self) -> bool { + (self.iterators + self.loops + self.nested_loops) > 0 + } +} + +/// Try to find hidden loops by looking for calls to Iterator functions that has a loop in them. +/// +/// Note that this will not find a loop, if the iterator is called inside a closure. +/// Run with -C opt-level 2 to help with this issue (i.e.: inline). +struct IteratorVisitor<'a> { + props: FnLoops, + body: &'a Body, +} + +impl<'a> MirVisitor for IteratorVisitor<'a> { + fn visit_terminator(&mut self, term: &Terminator, location: Location) { + if let TerminatorKind::Call { func, .. } = &term.kind { + let kind = func.ty(self.body.locals()).unwrap().kind(); + if let TyKind::RigidTy(RigidTy::FnDef(def, _)) = kind { + let fullname = def.name(); + let names = fullname.split("::").collect::>(); + if let [.., s_last, last] = names.as_slice() { + if *s_last == "Iterator" + && [ + "for_each", + "collect", + "advance_by", + "all", + "any", + "partition", + "partition_in_place", + "fold", + "try_fold", + "spec_fold", + "spec_try_fold", + "try_for_each", + "for_each", + "try_reduce", + "reduce", + "find", + "find_map", + "try_find", + "position", + "rposition", + "nth", + "count", + "last", + "find", + ] + .contains(last) + { + self.props.iterators += 1; + } + } + } + } + self.super_terminator(term, location) + } +} + +#[derive(Debug, Default)] +struct Recursion { + /// Collect the functions that may lead to a recursion loop. + /// I.e., for the following control flow graph: + /// ```dot + /// A -> B + /// B -> C + /// C -> [B, D] + /// ``` + /// this field value would contain A, B, and C since they can all lead to a recursion. + with_recursion: HashSet, + /// Collect the functions that are part of a recursion loop. + /// For the following control flow graph: + /// ```dot + /// A -> [B, C] + /// B -> B + /// C -> D + /// D -> [C, E] + /// ``` + /// The recursive functions would be B, C, and D. + recursive_fns: HashSet, +} + +impl Recursion { + pub fn collect<'a>(items: impl IntoIterator) -> Recursion { + let call_graph = items + .into_iter() + .filter_map(|item| { + if let TyKind::RigidTy(RigidTy::FnDef(def, _)) = item.ty().kind() { + let body = item.body(); + let mut visitor = FnCallVisitor { body: &body, fns: vec![] }; + visitor.visit_body(&body); + Some((def, visitor.fns)) + } else { + None + } + }) + .collect::>(); + let mut recursions = Recursion::default(); + recursions.analyze(call_graph); + recursions + } + + /// DFS post-order traversal to collect all loops in our control flow graph. + /// We only include direct call recursions which can only happen within a crate. + /// + /// # How it works + /// + /// Given a call graph, [(fn_def, [fn_def]*)]*, enqueue all existing nodes together with the + /// graph distance. + /// Keep track of the current path and the visiting status of each node. + /// For those that we have visited once, store whether a loop is reachable from them. + fn analyze(&mut self, call_graph: HashMap>) { + #[derive(Copy, Clone, PartialEq, Eq)] + enum Status { + ToVisit, + Visiting, + Visited, + } + let mut visit_status = HashMap::::new(); + let mut queue: Vec<_> = call_graph.keys().map(|node| (*node, 0)).collect(); + let mut path: Vec = vec![]; + while let Some((next, level)) = queue.last().copied() { + match visit_status.get(&next).unwrap_or(&Status::ToVisit) { + Status::ToVisit => { + assert_eq!(path.len(), level); + path.push(next); + visit_status.insert(next, Status::Visiting); + let next_level = level + 1; + if let Some(callees) = call_graph.get(&next) { + queue.extend(callees.iter().map(|callee| (*callee, next_level))); + } + } + Status::Visiting => { + if level < path.len() { + // We have visited all callees in this node. + visit_status.insert(next, Status::Visited); + path.pop(); + } else { + // Found a loop. + let mut in_loop = false; + for def in &path { + in_loop |= *def == next; + if in_loop { + self.recursive_fns.insert(*def); + } + self.with_recursion.insert(*def); + } + } + queue.pop(); + } + Status::Visited => { + queue.pop(); + if self.with_recursion.contains(&next) { + self.with_recursion.extend(&path); + } + } + } + } + } +} + +struct FnCallVisitor<'a> { + body: &'a Body, + fns: Vec, +} + +impl<'a> MirVisitor for FnCallVisitor<'a> { + fn visit_terminator(&mut self, term: &Terminator, location: Location) { + if let TerminatorKind::Call { func, .. } = &term.kind { + let kind = func.ty(self.body.locals()).unwrap().kind(); + if let TyKind::RigidTy(RigidTy::FnDef(def, _)) = kind { + self.fns.push(def); + } + } + self.super_terminator(term, location) + } +} diff --git a/tools/scanner/src/bin/scan.rs b/tools/scanner/src/bin/scan.rs new file mode 100644 index 000000000000..92b5319ec780 --- /dev/null +++ b/tools/scanner/src/bin/scan.rs @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Modifications Copyright Kani Contributors +// See GitHub history for details. + +// This is a modified version of project-stable-mir `test-drive` +// + +//! Provide a binary that can be used as a replacement to rustc. +//! +//! Besides executing the regular compilation, this binary will run a few static analyses. +//! +//! The result for each analysis will be stored in a file with the same prefix as an object file, +//! together with the name of the analysis. +//! +//! Look at each analysis documentation to see which files an analysis produces. + +use scanner::run_all; +use std::process::ExitCode; + +// ---- Arguments that should be parsed by the test-driver (w/ "scan" prefix) +/// Enable verbose mode. +const VERBOSE_ARG: &str = "--scan-verbose"; + +/// This is a wrapper that can be used to replace rustc. +fn main() -> ExitCode { + let args = std::env::args(); + let (scan_args, rustc_args): (Vec, _) = args.partition(|arg| arg.starts_with("--scan")); + let verbose = scan_args.contains(&VERBOSE_ARG.to_string()); + run_all(rustc_args, verbose) +} diff --git a/tools/scanner/src/lib.rs b/tools/scanner/src/lib.rs new file mode 100644 index 000000000000..7f9555781ccf --- /dev/null +++ b/tools/scanner/src/lib.rs @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Modifications Copyright Kani Contributors +// See GitHub history for details. + +// This is a modified version of project-stable-mir `test-drive` +// + +//! This library provide different ways of scanning a crate. + +#![feature(rustc_private)] + +mod analysis; + +extern crate rustc_driver; +extern crate rustc_interface; +extern crate rustc_middle; +extern crate rustc_session; +#[macro_use] +extern crate rustc_smir; +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 stable_mir::CompilerError; +use std::ops::ControlFlow; +use std::path::{Path, PathBuf}; +use std::process::ExitCode; +use std::sync::atomic::{AtomicBool, Ordering}; +use strum::IntoEnumIterator; +use strum_macros::{AsRefStr, EnumIter}; + +// Use a static variable for simplicity. +static VERBOSE: AtomicBool = AtomicBool::new(false); + +pub fn run_all(rustc_args: Vec, verbose: bool) -> ExitCode { + run_analyses(rustc_args, &Analysis::iter().collect::>(), verbose) +} + +/// 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)); + if result.is_ok() || matches!(result, Err(CompilerError::Skipped)) { + ExitCode::SUCCESS + } else { + ExitCode::FAILURE + } +} + +#[derive(AsRefStr, EnumIter, Debug, PartialEq)] +#[strum(serialize_all = "snake_case")] +pub enum Analysis { + /// Collect information about generic functions. + MonoFns, + /// Collect information about function safety. + SafeFns, + /// Collect information about function inputs. + InputTys, + /// Collect information about unsafe operations. + UnsafeOps, + /// Collect information about loops inside a function. + FnLoops, + /// Collect information about recursion via direct calls. + Recursion, +} + +fn info(msg: String) { + if VERBOSE.load(Ordering::Relaxed) { + eprintln!("[INFO] {}", msg); + } +} + +/// This function invoke the required analyses in the given order. +fn analyze_crate(tcx: TyCtxt, analyses: &[Analysis]) -> ControlFlow<()> { + 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. + // let file_stem = base_path.file_stem().unwrap(); + let file_stem = format!("{}_scan", stable_mir::local_crate().name); + let mut crate_stats = OverallStats::new(); + for analysis in analyses { + let filename = format!("{}_{}", file_stem, analysis.as_ref()); + let mut out_path = base_path.parent().map_or(PathBuf::default(), Path::to_path_buf); + out_path.set_file_name(filename); + match analysis { + Analysis::MonoFns => { + crate_stats.generic_fns(); + } + Analysis::SafeFns => { + crate_stats.safe_fns(out_path); + } + Analysis::InputTys => crate_stats.supported_inputs(out_path), + Analysis::UnsafeOps => crate_stats.unsafe_operations(out_path), + Analysis::FnLoops => crate_stats.loops(out_path), + Analysis::Recursion => crate_stats.recursion(out_path), + } + } + crate_stats.store_csv(base_path, &file_stem); + ControlFlow::<()>::Continue(()) +} From 370b2159ce179b82c4781fe7db1e93de68c900aa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 1 Aug 2024 09:38:47 +0200 Subject: [PATCH 225/225] Automatic toolchain upgrade to nightly-2024-08-01 (#3402) Update Rust toolchain from nightly-2024-07-31 to nightly-2024-08-01 without any other source changes. This is an automatically generated pull request. If any of the CI checks fail, manual intervention is required. In such a case, review the changes at https://github.com/rust-lang/rust from https://github.com/rust-lang/rust/commit/f8060d282d42770fadd73905e3eefb85660d3278 up to https://github.com/rust-lang/rust/commit/28a58f2fa7f0c46b8fab8237c02471a915924fe5. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f7324e45d06d..8753157827b5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2024-07-31" +channel = "nightly-2024-08-01" components = ["llvm-tools", "rustc-dev", "rust-src", "rustfmt"]
Commits