From 829c3c0093f633b5c20224a205d6f75e523a9673 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 1 Aug 2024 14:58:55 -0400 Subject: [PATCH 01/72] Add empty pass --- kani-compiler/src/args.rs | 2 + .../transform/check_aliasing/mod.rs | 42 +++++++++++++++++++ .../src/kani_middle/transform/mod.rs | 7 ++++ kani-driver/src/call_single_file.rs | 6 +++ kani_metadata/src/unstable.rs | 2 + .../aliasing/write_read_write.expected | 2 + tests/expected/aliasing/write_read_write.rs | 14 +++++++ 7 files changed, 75 insertions(+) create mode 100644 kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs create mode 100644 tests/expected/aliasing/write_read_write.expected create mode 100644 tests/expected/aliasing/write_read_write.rs diff --git a/kani-compiler/src/args.rs b/kani-compiler/src/args.rs index 3efc5c0f4f61..48028ed898b8 100644 --- a/kani-compiler/src/args.rs +++ b/kani-compiler/src/args.rs @@ -85,6 +85,8 @@ pub enum ExtraChecks { /// Check pointer validity when casting pointers to references. /// See https://github.com/model-checking/kani/issues/2975. PtrToRefCast, + /// Check for violations of pointer aliasing model + Aliasing, /// Check for using uninitialized memory. Uninit, } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs new file mode 100644 index 000000000000..9be812346f39 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -0,0 +1,42 @@ +// 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::transform::{TransformPass, TransformationType}; +use crate::kani_queries::QueryDb; +use rustc_middle::ty::TyCtxt; +use stable_mir::mir::mono::Instance; +use stable_mir::mir::Body; +use std::fmt::Debug; +use tracing::trace; + + +/// Instrument the code with checks for uninitialized memory. +#[derive(Debug)] +pub struct AliasingPass { +} + +impl TransformPass for AliasingPass { + 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::Aliasing) + } + + fn transform(&mut self, _tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + trace!(function=?instance.name(), "transform"); + (false, body) + } +} diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index 5b497b09619d..81d412215d4f 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -19,6 +19,7 @@ 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_aliasing::AliasingPass; use crate::kani_middle::transform::check_uninit::UninitPass; use crate::kani_middle::transform::check_values::ValidValuePass; use crate::kani_middle::transform::contracts::AnyModifiesPass; @@ -33,6 +34,7 @@ use std::collections::HashMap; use std::fmt::Debug; pub(crate) mod body; +mod check_aliasing; mod check_uninit; mod check_values; mod contracts; @@ -82,6 +84,11 @@ impl BodyTransformation { mem_init_fn_cache: HashMap::new(), }, ); + // Check aliasing + transformer.add_pass( + queries, + AliasingPass { }, + ); transformer.add_pass( queries, IntrinsicGeneratorPass { diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index bbeb5bfa417d..78a1993c9b87 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -139,6 +139,12 @@ impl KaniSession { flags.push("--ub-check=ptr_to_ref_cast".into()) } + if self.args.common_args.unstable_features.contains(UnstableFeature::Aliasing) { + // Automatically enable shadow memory, since the version of uninitialized memory checks + // without non-determinism depends on it. + flags.push("--ub-check=aliasing".into()); + } + if self.args.common_args.unstable_features.contains(UnstableFeature::UninitChecks) { // Automatically enable shadow memory, since the version of uninitialized memory checks // without non-determinism depends on it. diff --git a/kani_metadata/src/unstable.rs b/kani_metadata/src/unstable.rs index 68e4fba28819..a98339268736 100644 --- a/kani_metadata/src/unstable.rs +++ b/kani_metadata/src/unstable.rs @@ -93,6 +93,8 @@ pub enum UnstableFeature { PtrToRefCastChecks, /// Automatically check that uninitialized memory is not used. UninitChecks, + /// Check the pointer aliasing model + Aliasing, /// Enable an unstable option or subcommand. UnstableOptions, } diff --git a/tests/expected/aliasing/write_read_write.expected b/tests/expected/aliasing/write_read_write.expected new file mode 100644 index 000000000000..40b69fbc424a --- /dev/null +++ b/tests/expected/aliasing/write_read_write.expected @@ -0,0 +1,2 @@ +FAILURE\ +assertion failed: Stack violated diff --git a/tests/expected/aliasing/write_read_write.rs b/tests/expected/aliasing/write_read_write.rs new file mode 100644 index 000000000000..dea1ab64c755 --- /dev/null +++ b/tests/expected/aliasing/write_read_write.rs @@ -0,0 +1,14 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zaliasing + +#[kani::proof] +fn main() { + let mut x = 10; + let ref_x = &mut x; + let raw_1 = ref_x as *mut i32; + let raw_2 = ref_x as *const i32; + let _write = unsafe { *raw_1 = 100 }; + let _read = unsafe { *raw_2 }; + let _write = unsafe { *raw_1 = 110 }; +} From 052545df1c7cf8589362c22e35ca161b03efbf97 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 2 Aug 2024 11:59:48 -0400 Subject: [PATCH 02/72] Add "boxed" test for pointers into the heap. --- tests/expected/aliasing/boxed.expected | 2 ++ tests/expected/aliasing/boxed.rs | 14 ++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/expected/aliasing/boxed.expected create mode 100644 tests/expected/aliasing/boxed.rs diff --git a/tests/expected/aliasing/boxed.expected b/tests/expected/aliasing/boxed.expected new file mode 100644 index 000000000000..40b69fbc424a --- /dev/null +++ b/tests/expected/aliasing/boxed.expected @@ -0,0 +1,2 @@ +FAILURE\ +assertion failed: Stack violated diff --git a/tests/expected/aliasing/boxed.rs b/tests/expected/aliasing/boxed.rs new file mode 100644 index 000000000000..798f25ee50a9 --- /dev/null +++ b/tests/expected/aliasing/boxed.rs @@ -0,0 +1,14 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// kani-flags: -Zaliasing + +#[kani::proof] +fn main() { + let x = Box::new(10); + let ref_x = Box::into_raw(x); + let raw_1 = ref_x as *mut i32; + let raw_2 = ref_x as *const i32; + let _write = unsafe { *raw_1 = 100 }; + let _read = unsafe { *raw_2 }; + let _write = unsafe { *raw_1 = 110 }; +} From ba92c6b824c0a7843ecb5a0ae70ecadf35077e8f Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 9 Aug 2024 02:01:15 -0400 Subject: [PATCH 03/72] Add Stacked Borrows instrumentation code for stack allocations --- .../transform/check_aliasing/mod.rs | 581 +++++++++++++++++- .../aliasing/{boxed.rs => box.rs.fixme} | 0 .../{boxed.expected => boxed.expected.fixme} | 0 tests/expected/aliasing/duplicate_write.rs | 491 +++++++++++++++ ...pected => write_read_write.expected.fixme} | 0 ...ead_write.rs => write_read_write.rs.fixme} | 0 6 files changed, 1067 insertions(+), 5 deletions(-) rename tests/expected/aliasing/{boxed.rs => box.rs.fixme} (100%) rename tests/expected/aliasing/{boxed.expected => boxed.expected.fixme} (100%) create mode 100644 tests/expected/aliasing/duplicate_write.rs rename tests/expected/aliasing/{write_read_write.expected => write_read_write.expected.fixme} (100%) rename tests/expected/aliasing/{write_read_write.rs => write_read_write.rs.fixme} (100%) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 9be812346f39..6d2dbb91adff 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -9,14 +9,77 @@ use crate::kani_middle::transform::{TransformPass, TransformationType}; use crate::kani_queries::QueryDb; use rustc_middle::ty::TyCtxt; use stable_mir::mir::mono::Instance; -use stable_mir::mir::Body; use std::fmt::Debug; -use tracing::trace; +use stable_mir::ty::{ + FnDef, GenericArgKind, GenericArgs, Region, RegionKind, RigidTy, Ty, TyKind, Span +}; +use stable_mir::mir::{ + BasicBlockIdx, Body, Local, LocalDecl, Mutability, Place, TerminatorKind, UnwindAction +}; +use stable_mir::{CrateDef, Error}; +use stable_mir::mir::{BasicBlock, BorrowKind, MirVisitor, MutBorrowKind, Operand, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, VarDebugInfo +}; +use std::collections::HashMap; +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct FunctionSignature { + name: String, + args: Vec, +} + +impl FunctionSignature { + pub fn new(name: &str, args: &[GenericArgKind]) -> FunctionSignature { + FunctionSignature { + name: name.to_string(), + args: args.to_vec(), + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct FunctionInstance { + signature: FunctionSignature, + instance: Instance, +} + +impl FunctionInstance { + pub fn new(signature: FunctionSignature, instance: Instance) -> FunctionInstance { + FunctionInstance { + signature, + instance, + } + } +} + +#[derive(Default, Debug)] +pub struct FunctionInstanceCache(Vec); + +pub struct StackedBorrowsPass { + cache: FunctionInstanceCache, +} /// Instrument the code with checks for uninitialized memory. #[derive(Debug)] pub struct AliasingPass { + cache: FunctionInstanceCache, +} + +struct InitializedPassState<'tcx, 'cache> { + body: Body, + tcx: TyCtxt<'tcx>, + cache: &'cache mut FunctionInstanceCache, +} + +impl<'tcx, 'cache> InitializedPassState<'tcx, 'cache> { + fn new(body: Body, tcx: TyCtxt<'tcx>, cache: &'cache mut FunctionInstanceCache) -> Self { + Self { body, tcx, cache } + } + + fn collect_locals(self) -> LocalPassState<'tcx, 'cache> { + let mut visitor = CollectLocalVisitor::new(); + visitor.visit_body(&self.body); + LocalPassState { tcx: self.tcx, cache: self.cache, values: visitor.values, body: self.body } + } } impl TransformPass for AliasingPass { @@ -35,8 +98,516 @@ impl TransformPass for AliasingPass { args.ub_check.contains(&ExtraChecks::Aliasing) } - fn transform(&mut self, _tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { - trace!(function=?instance.name(), "transform"); - (false, body) + fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + let pass = InitializedPassState::new(body, tcx, &mut self.cache); + let out = pass.collect_locals().collect_body().finalize(); + (true, out) + } +} + +struct LocalPassState<'tcx, 'cache> { + body: Body, + tcx: TyCtxt<'tcx>, + cache: &'cache mut FunctionInstanceCache, + values: Vec, +} + +struct InstrumentationData<'tcx, 'cache> { + tcx: TyCtxt<'tcx>, + cache: &'cache mut FunctionInstanceCache, + meta_stack: HashMap, + body: CachedBodyMutator, +} + +struct BodyMutationPassState<'tcx, 'cache> { + values: Vec, + instrumentation_data: InstrumentationData<'tcx, 'cache>, +} + +impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { + fn assign_ref( + &mut self, + lvalue: Local, + rvalue: Local, + span: Span) { + let kind = RegionKind::ReErased; + let region = Region { kind }; + let borrow = BorrowKind::Mut { kind: MutBorrowKind::Default }; + let lvalue = Place::from(lvalue); + let rvalue = Rvalue::Ref(region, borrow, Place::from(rvalue)); + let kind = StatementKind::Assign(lvalue, rvalue); + self.body.insert_statement(Statement { kind, span }); + } + + fn instrument_local( + &mut self, + local: usize, + ) -> Result<(), Error> { + // Initialize the constants + let ty = self.body.local(local).ty; + let ref_ty = Ty::new_ref(Region { kind: RegionKind::ReErased }, ty, Mutability::Not ); + let body = &mut self.body; + let local_ref = self.meta_stack.entry(local).or_insert_with(|| body.new_local(ref_ty, Mutability::Not)); + let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniInitializeLocal", &[GenericArgKind::Type(ty)]))?; + body.call(instance, [local, *local_ref].to_vec(), body.unit); + Ok(()) + } + + // get back to this one + // fn instrument_new_stack_reference(&mut self, idx: &MutatorIndex, to: Local, from: Local) -> Result<(), Error> { + // // Initialize the constants + // let ty_from = self.body.local(from).ty; + // let ty_to = self.body.local(from).ty; + // let from_metadata = self.meta_stack.get(&from).unwrap().clone(); + // let to_metadata = self.meta_value.get(&to).unwrap().clone(); + // let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutableRef", &[GenericArgKind::Type(ty_from), GenericArgKind::Type(ty_to)]))?; + // self.body.call(instance, [&from_metadata.0 as &[Local], &to_metadata.0].concat(), self.body.unit); + // self.body.split(idx); + // Ok(()) + // } + + // And this one + // fn instrument_new_raw_from_ref(&mut self, idx: &MutatorIndex, to: Local, from: Local) -> Result<(), Error> { + // // Initialize the constants + // let ty_from = self.body.local(from).ty; + // let ty_to = self.body.local(from).ty; + // let from_metadata = self.meta_value.get(&from).unwrap().clone(); + // let to_metadata = self.meta_value_mut.get(&to).unwrap().clone(); + // let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutableRaw", &[GenericArgKind::Type(ty_from), GenericArgKind::Type(ty_to)]))?; + // self.body.call(instance, [&from_metadata.0 as &[Local], &to_metadata.0].concat(), self.body.unit); + // self.body.split(idx); + // Ok(()) + // } + + fn instrument_index(&mut self, _values: &Vec, idx: &MutatorIndex) -> Result<(), Error> { + match self.body.inspect(idx) { + Instruction::Stmt(Statement { kind, ..} ) => { + match kind { + StatementKind::Assign(to, Rvalue::Ref(_, BorrowKind::Mut { .. }, from)) => { + let Place { local, projection } = from; + match projection[..] { + [] => { + // Direct reference to the stack local + // self.instrument_new_stack_reference(idx, to.local, *local) + Ok(()) + } + [ProjectionElem::Deref] => { + // to = &*from + // (Reborrow) + Ok(()) + } + _ => { + Ok(()) + } + } + } + StatementKind::Assign(to, Rvalue::AddressOf(Mutability::Mut, from)) => { + // to = &raw *from + if self.body.local(from.local).ty.kind().is_ref() { + // let _ = self.instrument_new_raw_from_ref(idx, to.local, from.local); + } + Ok(()) + } + StatementKind::Assign(to, Rvalue::Ref(_, _, from)) => { + // immutable reference + (to, from); + Ok(()) + } + StatementKind::Assign(to, Rvalue::Use(Operand::Copy(from))) => { + // TODO impl use local + (to, from); + Ok(()) + } + StatementKind::Assign(to, Rvalue::Use(Operand::Move(from))) => { + // TODO impl move local + (to, from); + Ok(()) + } + StatementKind::Assign(_, Rvalue::Use(Operand::Constant(_))) => { + // load from static memory, ignore + Ok(()) + } + StatementKind::Assign(place, rvalue) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, + StatementKind::Retag(_, _) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, + StatementKind::FakeRead(_, _) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, + StatementKind::SetDiscriminant { place, variant_index } => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, + StatementKind::Deinit(_) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, + StatementKind::StorageLive(_) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, + StatementKind::StorageDead(_) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, + StatementKind::PlaceMention(_) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, + StatementKind::AscribeUserType { place, projections, variance } => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, + StatementKind::Coverage(_) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, + StatementKind::Intrinsic(_) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, + StatementKind::ConstEvalCounter => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, + StatementKind::Nop => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, + } + } + Instruction::Term(_) => Ok(()), + } + } + + fn instrument_locals(&mut self, + values: &Vec) -> Result<(), Error> { + for local in values { + self.instrument_local(*local)? + } + Ok(()) + } + + fn instrument_instructions(&mut self, values: &Vec) -> Result<(), Error> { + let mut index = self.body.new_index(); + let mut status = MutatorIndexStatus::Remaining; + while status == MutatorIndexStatus::Remaining { + self.instrument_index(values, &index)?; + status = self.body.decrement_index(&mut index); + } + Ok(()) + } +} + + +impl<'tcx, 'cache> BodyMutationPassState<'tcx, 'cache> { + fn instrument_locals(&mut self) -> Result<(), Error> { + self.instrumentation_data.instrument_locals(&self.values) + } + + fn instrument_instructions(&mut self) -> Result<(), Error> { + self.instrumentation_data.instrument_instructions(&self.values)?; + Ok(()) + } + + fn finalize(mut self) -> Body { + self.instrument_locals().unwrap(); + self.instrumentation_data.body.finalize_prologue(); + self.instrument_instructions().unwrap(); + self.instrumentation_data.body.finalize() + } +} + +struct BodyMutator { + blocks: Vec, + locals: Vec, + arg_count: usize, + var_debug_info: Vec, + spread_arg: Option, + span: Span, + + ghost_locals: Vec, + ghost_blocks: Vec, + ghost_statements: Vec, +} + +struct CachedBodyMutator { + body: BodyMutator, + unit: Local, + cache: HashMap, +} + +impl BodyMutator { + fn new(blocks: Vec, locals: Vec, arg_count: usize, var_debug_info: Vec, spread_arg: Option, span: Span, ghost_locals: Vec, ghost_blocks: Vec, statements: Vec) -> Self { + BodyMutator { blocks, locals, arg_count, var_debug_info, spread_arg, span, ghost_locals, ghost_blocks, ghost_statements: statements } + } + + fn gen_bb0(body: &mut Body) -> BasicBlock { + let target = body.blocks.len() + 1; + let kind = TerminatorKind::Goto { target }; + let span = body.span; + let terminator = Terminator { kind, span }; + let statements = Vec::new(); + std::mem::replace(&mut body.blocks[0], BasicBlock { statements, terminator }) + } + + fn gen_unit(body: &Body) -> LocalDecl { + let ty = Ty::new_tuple(&[]); + let span = body.span; + let mutability = Mutability::Not; + LocalDecl { ty, span, mutability } + } + + fn from(mut body: Body) -> Self { + let bb0 = Self::gen_bb0(&mut body); + body.blocks.push(bb0); + let ghost_locals = vec![Self::gen_unit(&body)]; + let ghost_blocks = vec![]; + let locals = body.locals().to_vec(); + let arg_count = body.arg_locals().len(); + let spread_arg = body.spread_arg(); + let debug_info = body.var_debug_info; + let statements = Vec::new(); + BodyMutator::new(body.blocks, locals, arg_count, debug_info, spread_arg, body.span, ghost_locals, ghost_blocks, statements) + } +} + +impl<'tcx, 'cache> LocalPassState<'tcx, 'cache> { + fn collect_body(self) -> BodyMutationPassState<'tcx, 'cache> { + let values = self.values; + let instrumentation_data = InstrumentationData { + tcx: self.tcx, + cache: self.cache, + meta_stack: HashMap::new(), + body: CachedBodyMutator::from(self.body), + }; + BodyMutationPassState { + values, + instrumentation_data + } + } +} + +struct CollectLocalVisitor { + values: Vec, +} + +impl CollectLocalVisitor { + fn new() -> Self { + let values = Vec::new(); + CollectLocalVisitor { values } + } +} + +impl MirVisitor for CollectLocalVisitor { + fn visit_local_decl(&mut self, local: Local, decl: &LocalDecl) { + // // For now collect em all + let _ = decl; + self.values.push(local); + // if let TyKind::RigidTy(ty) = decl.ty.kind() { + // if function_ty(&ty) { + // eprintln!("WARN: Function types not yet supported ") + // } else if value_ty(&ty) { + // self.values.push(local); + // } else if !value_reference_ty(&ty) { + // panic!("Type {:?} not supported by the analysis.", ty); + // } + // } + } +} + +impl FunctionInstanceCache { + fn new() -> Self { + Self (Vec::new()) + } + + fn register(&mut self, ctx: &TyCtxt, sig: FunctionSignature) -> Result<&Instance, Error> { + let FunctionInstanceCache(cache) = self; + for i in 0..cache.len() { + if sig == cache[i].signature { + return Ok(&cache[i].instance); + } + } + let fndef = + super::super::find_fn_def(*ctx, &sig.name) + .ok_or(Error::new(format!("Not found: {}", &sig.name)))?; + let instance = Instance::resolve(fndef, &GenericArgs(sig.args.clone()))?; + cache.push(FunctionInstance::new(sig, instance)); + Ok(&cache[cache.len() - 1].instance) + } + + #[allow(unused)] + fn get(&self, sig: &FunctionSignature) -> Result<&Instance, Error> { + let FunctionInstanceCache(cache) = self; + for FunctionInstance { + signature, + instance, + } in cache { + if *sig == *signature { + return Ok(instance); + } + } + Err(Error::new(format!("Not found: {:?}", sig))) + } +} + +impl CachedBodyMutator { + fn from(body: Body) -> Self { + let mut body = BodyMutator::from(body); + let unit = body.new_local(Ty::new_tuple(&[]), Mutability::Not); + let cache = HashMap::new(); + CachedBodyMutator { body, unit, cache } + } + + fn local(&self, idx: usize) -> &LocalDecl { + &self.body.locals[idx] + } + + fn new_local(&mut self, ty: Ty, mutability: Mutability) -> Local { + self.body.new_local(ty, mutability) + } + + fn call(&mut self, callee: &Instance, args: Vec, local: Local) { + let func_local; + { + let cache = &mut self.cache; + let body = &mut self.body; + { + func_local = cache.entry(*callee).or_insert_with(|| body.new_local(callee.ty(), Mutability::Not)); + } + } + self.body.call(*func_local, args, local); + } + + fn finalize_prologue(&mut self) { + self.body.finalize_prologue(); + } + + fn insert_statement(&mut self, stmt: Statement) { + self.body.ghost_statements.push(stmt); + } + + fn assign_ref(&mut self, lvalue: Local, rvalue: Local) { + self.body.assign_ref(lvalue, rvalue) + } + + fn new_index(&mut self) -> MutatorIndex { + self.body.new_index() + } + + fn decrement_index(&mut self, idx: &mut MutatorIndex) -> MutatorIndexStatus { + self.body.decrement(idx) + } + + fn split(&mut self, idx: &MutatorIndex) { + self.body.split(idx); + } + + fn inspect(&self, idx: &MutatorIndex) -> Instruction { + self.body.inspect(idx) + } + + fn finalize(self) -> Body { + self.body.finalize() + } + + fn span(&self) -> Span { + self.body.span + } +} + +#[derive(Debug)] +struct MutatorIndex { + bb: BasicBlockIdx, + idx: usize, + span: Span +} + +#[derive(PartialEq, Eq)] +enum MutatorIndexStatus { + Remaining, + Done +} + +enum Instruction<'a> { + Stmt(&'a Statement), + Term(&'a Terminator) +} + +impl BodyMutator { + fn new_local(&mut self, ty: Ty, mutability: Mutability) -> Local { + let span = self.span; + let decl = LocalDecl { ty, span, mutability }; + let local = self.locals.len() + self.ghost_locals.len(); + self.ghost_locals.push(decl); + local + } + + fn call(&mut self, callee: Local, args: Vec, local: Local) { + let projection = Vec::new(); + let destination = Place { local, projection }; + let args = args.into_iter().map(|v| Operand::Copy(Place { local: v, projection: vec![] } )).collect(); + let func = Operand::Copy(Place::from(callee)); + let unwind = UnwindAction::Terminate; + let target = Some(self.next_block()); + let kind = TerminatorKind::Call { func, args, destination, target, unwind }; + let span = self.span; + let terminator = Terminator { kind, span }; + let statements = std::mem::replace(&mut self.ghost_statements, Vec::new()); + self.ghost_blocks.push(BasicBlock { statements, terminator }); + } + + fn finalize_prologue(&mut self) { + let kind = TerminatorKind::Goto { target: self.blocks.len() - 1 }; + let span = self.span; + let terminator = Terminator { kind, span }; + self.insert_bb(terminator); + } + + fn new_index(&self) -> MutatorIndex { + let len = self.blocks.len(); + let bb = len - 1; + let idx = if len > 0 { self.blocks[bb].statements.len() - 1 } else { 0 }; + let span = self.span; + MutatorIndex { bb, idx, span } + } + + fn decrement(&self, index: &mut MutatorIndex) -> MutatorIndexStatus { + let mut status = MutatorIndexStatus::Done; + if index.idx > 0 || index.bb > 0 { + status = MutatorIndexStatus::Remaining; + } + if index.idx > 0 { + index.span = self.blocks[index.bb] + .statements[index.idx].span; + index.idx -= 1; + } else if index.bb > 0 { + index.bb -= 1; + index.span = self.blocks[index.bb].terminator.span; + index.idx = self.blocks[index.bb].statements.len() + } + status + } + + fn inspect(&self, index: &MutatorIndex) -> Instruction { + if index.idx >= self.blocks[index.bb].statements.len() { + Instruction::Term(&self.blocks[index.bb].terminator) + } else { + Instruction::Stmt(&self.blocks[index.bb].statements[index.idx]) + } + } + + fn split(&mut self, index: &MutatorIndex) { + let kind = TerminatorKind::Goto { target: self.blocks.len() + self.ghost_blocks.len() - 1 }; + let span = index.span; + let term = Terminator { kind, span }; + let len = self.blocks[index.bb].statements.len(); + if index.idx < len { + self.ghost_statements.extend(self.blocks[index.bb].statements.split_off(index.idx + 1)); + } + let term = std::mem::replace(&mut self.blocks[index.bb].terminator, term); + self.insert_bb(term); + } + + fn insert_statement(&mut self, stmt: Statement) { + self.ghost_statements.push(stmt); + } + + fn assign_ref(&mut self, lvalue: Local, rvalue: Local) { + let kind = RegionKind::ReErased; + let region = Region { kind }; + let borrow = BorrowKind::Mut { kind: MutBorrowKind::Default }; + let lvalue = Place::from(lvalue); + let rvalue = Rvalue::Ref(region, borrow, Place::from(rvalue)); + let kind = StatementKind::Assign(lvalue, rvalue); + let span = self.span; + self.insert_statement(Statement { kind, span }); + } + + fn next_block(&self) -> usize { + self.blocks.len() + self.ghost_blocks.len() + 1 + } + + fn insert_bb(&mut self, terminator: Terminator) { + let statements = std::mem::replace(&mut self.ghost_statements, Vec::new()); + let execute_original_body = BasicBlock { statements, terminator }; + self.ghost_blocks.push(execute_original_body); + } + + fn finalize(self) -> Body { + match self { + BodyMutator { mut blocks, mut locals, arg_count, var_debug_info, spread_arg, span, ghost_locals, ghost_blocks, ghost_statements } => { + assert!(ghost_statements.len() == 0); + blocks.extend(ghost_blocks.into_iter()); + locals.extend(ghost_locals.into_iter()); + Body::new(blocks, locals, arg_count, var_debug_info, spread_arg, span) + } + } } } diff --git a/tests/expected/aliasing/boxed.rs b/tests/expected/aliasing/box.rs.fixme similarity index 100% rename from tests/expected/aliasing/boxed.rs rename to tests/expected/aliasing/box.rs.fixme diff --git a/tests/expected/aliasing/boxed.expected b/tests/expected/aliasing/boxed.expected.fixme similarity index 100% rename from tests/expected/aliasing/boxed.expected rename to tests/expected/aliasing/boxed.expected.fixme diff --git a/tests/expected/aliasing/duplicate_write.rs b/tests/expected/aliasing/duplicate_write.rs new file mode 100644 index 000000000000..f4cc15504522 --- /dev/null +++ b/tests/expected/aliasing/duplicate_write.rs @@ -0,0 +1,491 @@ +#![allow(internal_features)] +#![feature(rustc_attrs)] +#![feature(vec_into_raw_parts)] +// Copyright Jacob Salzberg +// SPDX-License-Identifier: Apache-2.0 + +// Basic test from the stacked borrows paper +#![allow(non_snake_case)] +#![feature(const_trait_impl)] +#![cfg_attr(not(kani), feature(register_tool))] +#![cfg_attr(not(kani), register_tool(kani))] +use std::ptr::null; +use std::ptr::addr_of; +use std::convert::TryInto; + +const MAX_NUM_OBJECTS: usize = 1024; +const MAX_OBJECT_SIZE: usize = 64; + +const STACK_DEPTH: usize = 15; +type PointerTag = u8; + +#[cfg(any(kani))] +fn assume(b: bool) { + kani::assume(b); +} + +#[cfg(not(kani))] +fn assume(b: bool) { + assert!(b); +} + +#[cfg(any(kani))] +fn pointer_object(ptr: *const T) -> usize { + kani::mem::pointer_object(ptr) +} + +#[cfg(not(kani))] +fn pointer_object(ptr: *const T) -> usize { + ptr as usize +} + +#[cfg(any(kani))] +fn pointer_offset(_ptr: *const T) -> usize { + 0 +} + +#[cfg(not(kani))] +fn pointer_offset(ptr: *const T) -> usize { + 0 +} + +/// The stacked borrows state. +pub mod sstate { + use super::*; + /// Associate every pointer object with a tag + static mut TAGS: [[PointerTag; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS] = + [[0; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS]; + /// Next pointer id: the next pointer id in sequence + static mut NEXT_TAG: PointerTag = 0; + + #[non_exhaustive] + struct Access; + impl Access { + pub(self) const READ: bool = false; + pub(self) const WRITE: bool = true; + } + + #[non_exhaustive] + struct Permission; + impl Permission { + pub(self) const UNIQUE: u8 = 0; + pub(self) const SHAREDRW: u8 = 1; + pub(self) const SHAREDRO: u8 = 2; + pub(self) const DISABLED: u8 = 3; + + pub(self) fn grants(access: bool, tag: u8) -> bool { + tag != Self::DISABLED && (access != Access::WRITE || tag != Self::SHAREDRO) + } + } + + /// Associate every pointer object with a permission + static mut PERMS: [[PointerTag; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS] = + [[Permission::UNIQUE; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS]; + + pub(super) mod monitors { + static mut STATE: bool = false; + static mut OBJECT: usize = 0; + static mut OFFSET: usize = 0; + static mut STACK_TAGS: [u8; STACK_DEPTH] = [0; STACK_DEPTH]; + static mut STACK_PERMS: [u8; STACK_DEPTH] = [0; STACK_DEPTH]; + static mut STACK_TOP: usize = 0; + + #[non_exhaustive] + struct MonitorState; + impl MonitorState { + pub(self) const UNINIT: bool = false; + pub(self) const INIT: bool = true; + } + + use super::*; + // pub fn get_objects() -> *mut usize { + // unsafe { OBJECTS as *mut usize } + // } + + // fn get_offsets() -> *mut usize { + // unsafe { OFFSETS as *mut usize } + // } + + // fn get_states() -> *mut bool { + // unsafe { STATES as *mut bool } + // } + + // fn get_stack_tops() -> *mut usize { + // unsafe { STACK_TOPS as *mut usize } + // } + + // fn get_stack_ids() -> *mut [PointerTag; STACK_DEPTH] { + // unsafe { STACK_TAGS as *mut [PointerTag; STACK_DEPTH] } + // } + + // fn get_stack_permissions() -> *mut [u8; STACK_DEPTH] { + // unsafe { STACK_TAGS as *mut [u8; STACK_DEPTH] } + // } + + /// Monitors: + /// If there are K bytes in the address space, + /// every stacked borrows instrumentation has + /// between 0 and K monitors. + /// These monitors track a single byte of the program, + /// associating it with a stack of pointer values + /// (represented by tags). + /// Whenever a pointer borrows an object containing + /// the byte, its tag is pushed to the stack; + /// when a read or write is performed through this pointer, + /// writes from pointers above its location on the stack + /// are disabled. + /// This function prepares N monitors, + /// writes them to global heap memory, then + /// stores them in pointers. + /// An N+1th monitor is allocated as a "garbage" + /// area to be used when no monitor is picked. + pub fn prepare_monitors() { + unsafe { + OBJECT = 0usize; + // vec![0usize; size].into_raw_parts().0 as *const (); + OFFSET = 0usize; + // vec![0usize; size].into_raw_parts().0 as *const (); + STATE = MonitorState::UNINIT; + // vec![MonitorState::UNINIT; size].into_raw_parts().0 as *const (); + STACK_TAGS = [NEXT_TAG; STACK_DEPTH]; + // vec![[NEXT_TAG; STACK_DEPTH]; size].into_raw_parts().0 as *const (); + STACK_PERMS = [Permission::UNIQUE; STACK_DEPTH]; + // vec![[Permission::UNIQUE; STACK_DEPTH]; size].into_raw_parts().0 as *const (); + STACK_TOP = 0usize; + // vec![0usize; size].into_raw_parts().0 as *const (); + } + } + + /// Initialize local when track local is true, picking a monitor, + /// and setting its object and offset to within pointer. + pub(super) unsafe fn track_local(tag: u8, pointer: *const U) { + // Decide whether to initialize the stacks + // for location:location+size_of(U). + // Offset has already been picked earlier. + unsafe { + // Pick a monitor nondeterministically + // use self::*; + // let states = get_states(); + // let objects = get_objects(); + // let offsets = get_offsets(); + // let stack_ids = get_stack_ids(); + // let stack_perms = get_stack_permissions(); + // let tops = get_stack_tops(); + + // let mut i = sstate_config::MONITORS.try_into().unwrap(); + // while i > 0 { + // i -= 1; + // if demonic_nondet() && *states.offset(i) == MonitorState::UNINIT { + // let top = *tops.offset(i); + // *states.offset(i) = MonitorState::INIT; + // *objects.offset(i) = pointer_object(pointer); + // assume(*offsets.offset(i) == 0 || + // *offsets.offset(i) < std::mem::size_of::()); + // (*stack_ids.offset(i))[0] = tag; + // (*stack_perms.offset(i))[0] = Permission::UNIQUE; + // } + // } + if demonic_nondet() && STATE == MonitorState::UNINIT { + STATE = MonitorState::INIT; + OBJECT = pointer_object(pointer); + assume(OFFSET < std::mem::size_of::()); + STACK_TAGS[STACK_TOP] = tag; + STACK_PERMS[STACK_TOP] = Permission::UNIQUE; + STACK_TOP += 1; + } + } + } + + /// Push a tag with a permission perm at pointer + pub(super) fn push(tag: u8, perm: u8, pointer: *const U) { + // Decide whether to initialize the stacks + // for location:location+size_of(U). + // Offset has already been picked earlier. + unsafe { + // Pick a monitor nondeterministically + use self::*; + // let states = get_states(); + // let objects = get_objects(); + // let offsets = get_offsets(); + // let stack_ids = get_stack_ids(); + // let stack_perms = get_stack_permissions(); + // let tops = get_stack_tops(); + + // let mut i = sstate_config::MONITORS.try_into().unwrap(); + if STATE == MonitorState::INIT && + OBJECT == pointer_object(pointer) && + OFFSET == pointer_offset(pointer) + { + STACK_TAGS[STACK_TOP + 1] = tag; + STACK_PERMS[STACK_TOP + 1] = perm; + STACK_TOP += 1; + } + } + } + + pub(super) fn stack_check(tag: u8, access: bool, address: *const U) { + unsafe { + use self::*; + // let states = get_states(); + // let objects = get_objects(); + // let offsets = get_offsets(); + // let stack_ids = get_stack_ids(); + // let stack_perms = get_stack_permissions(); + // let tops = get_stack_tops(); + // let mut i = sstate_config::MONITORS.try_into().unwrap(); + if STATE == MonitorState::INIT && + OFFSET == pointer_offset(address) && + OBJECT == pointer_object(address) { + let mut found = false; + let mut j = 0; + let mut new_top = 0; + assert!(STACK_TOP < STACK_DEPTH); + while j < STACK_DEPTH { + if j < STACK_TOP { + let id = STACK_TAGS[j]; + let kind = STACK_PERMS[j]; + if Permission::grants(access, kind) && id == tag { + new_top = j + 1; + found = true; + } + } + j += 1; + } + } + // while i > 0 { + // { + // let top = *tops.offset(i); + // let mut found = false; + // let mut j = STACK_DEPTH; + // let mut new_top = 0; + // while j > 0 { + // } + // assert!(found, "Stack violated."); + // *tops.offset(i) = new_top; + // } + // } + } + } + } + + #[rustc_diagnostic_item = "KaniInitializeSState"] + pub fn initialize() { + self::monitors::prepare_monitors(); + } + + /// Run a stack check on the pointer value at the given location. + pub fn stack_check(tag: u8, access: bool, address: *const U) { + self::monitors::stack_check(tag, access, address) + } + + /// Push the permissions at the given location + pub fn push(tag: u8, perm: u8, address: *const U) { + self::monitors::push(tag, perm, address) + } + + /// Initialize the local stored at reference if initialized is set to false, + /// and track it using a monitor when using demonic nondeterminism. + /// + /// Every function call in the source program stack-allocates + /// the local variables that it uses; references to these + /// variables are only valid after these variables are initialized (first written). + /// Therefore this function can be used by supplying an initialized flag + /// set to true after the first write, a track flag set to the value + /// of a query to a demonic nondeterminism oracle (when this feature is used) + /// and a reference to the stack location. + pub fn initialize_local(pointer: *const U) { + unsafe { + let tag = NEXT_TAG; + TAGS[pointer_object(pointer)][pointer_offset(pointer)] = NEXT_TAG; + PERMS[pointer_object(pointer)][pointer_offset(pointer)] = Permission::UNIQUE; + NEXT_TAG += 1; + monitors::track_local(tag, pointer); + } + } + + pub fn use_2(ptr: *const T) { + unsafe { + let tag = TAGS[pointer_object(ptr)][pointer_offset(ptr)]; + let perm = PERMS[pointer_object(ptr)][pointer_offset(ptr)]; + for i in 0..std::mem::size_of::() { + stack_check(tag, Access::WRITE, ptr.byte_add(i)); + } + } + } + + /// Make a new mutable reference at the rvalue. + /// Associate the tag with the lvalue. + pub fn new_mut_ref(lvalue: *const &mut T, rvalue: &mut T) { + unsafe { + // use_2 the rvalue + use_2(rvalue as *const T); + // Then associate the lvalue and push it + push(NEXT_TAG, Permission::UNIQUE, lvalue); + // TAGS[pointer_object(lvalue)][pointer_offset(lvalue)] = NEXT_TAG; + // PERMS[pointer_object(lvalue)][pointer_offset(lvalue)] = Permission::UNIQUE; + NEXT_TAG += 1; + } + } + + /// Make a raw mutable reference at the rvalue. + /// Associate the tag with the lvalue. + pub fn new_mut_raw(lvalue: *const *mut T, rvalue: *mut T) { + unsafe { + // use_2 the rvalue + use_2(rvalue as *const T); + // Then associate the lvalue and push it + push(NEXT_TAG, Permission::SHAREDRW, lvalue); + // TAGS[pointer_object(lvalue)][pointer_offset(lvalue)] = NEXT_TAG; + // PERMS[pointer_object(lvalue)][pointer_offset(lvalue)] = Permission::SHAREDRW; + NEXT_TAG += 1; + } + } +} + + + +type PointerValueKind = u32; +/* Uninitialized pointer tag */ +const KIND_UNINITIALIZED: PointerValueKind = 0; +/* Pointer tag with ID */ +const KIND_IDENTIFIED: PointerValueKind = 1; +/* Tag == none -- e.g. shared mutable reference */ +const KIND_NONE: PointerValueKind = 2; + +static mut POINTER_PERMISSIONS: [[PointerValueKind; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS] = + [[KIND_UNINITIALIZED; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS]; + +static mut POINTER_TAGS: [[usize; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS] = + [[0; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS]; + +static mut POINTER_SIZE: [[usize; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS] = + [[0; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS]; + +#[cfg(any(kani))] +fn demonic_nondet() -> bool { + kani::any::() +} + +#[cfg(not(kani))] +fn demonic_nondet() -> bool { + true +} + +fn track_local() -> bool { + demonic_nondet() +} + +#[cfg(not(kani))] +fn any_usize() -> usize { + 0 +} + +#[cfg(any(kani))] +fn any_usize() -> usize { + kani::any() +} + +// #[rustc_diagnostic_item = "KaniPushUnique"] +// fn push_unique(pointer: *const U, kind: &mut usize, tag: &mut usize) { +// push( +// pointer, +// &mut POINTER_PERMISSIONS[SSTATE_MONITOR_OBJECT][SSTATE_MONITOR_OFFSET], +// &mut POINTER_TAGS[SSTATE_MONITOR_OBJECT][SSTATE_MONITOR_OBJECT], +// KIND_UNIQUE, +// ); +// } + +// pub fn push(pointer: *const U, kind: &mut usize, tag: &mut usize, create: PointerValueKind) { +// unsafe { +// *tag = 0; +// if monitored(pointer) { +// if create == KIND_SHARED_RW { +// *tag = SSTATE_NEXT_TAG; +// SSTATE_NEXT_TAG += 1; +// } +// *kind = create; +// let top = STATE_STACK_TOPS; +// assert!(top < STACK_DEPTH); +// SSTATE_STACK_PERMS[top] = *kind; +// SSTATE_STACK_TAGS[top] = *tag; +// SSTATE_STACK_TOPS += 1; +// } +// } +// } + +// #[rustc_diagnostic_item = "KaniUse2"] +// fn use_2(pointer: *const U) { +// unsafe { +// if monitored(pointer) { +// let top = SSTATE_STACK_TOPS; +// let mut found = false; +// assert!(kind != KIND_UNINITIALIZED); +// let needle_kind = POINTER_PERMISSIONS[pointer_object(pointer)][pointer_offset(pointer)]; +// let needle_id = POINTER_IDS[pointer_object(pointer)][pointer_offset(pointer)]; +// let mut i = 0; +// let mut new_top = 0; +// while (i < STACK_DEPTH) && (i < top) { +// if SSTATE_STACK_PERMS[i] == to_find && SSTATE_STACK_TAGS[i] == id { +// new_top = i + 1; +// found = true; +// } +// i += 1; +// } +// SSTATE_STACK_TOPS = new_top; +// if kind != KIND_UNINITIALIZED { +// } else { +// let mut i = 0; +// let mut new_top = 0; +// while (i < STACK_DEPTH) && (i < top) { +// if SSTATE_STACK_PERMS[i] == KIND_SHARED_RW { +// new_top = i + 1; +// found = true; +// } +// i += 1; +// } +// SSTATE_STACK_TOPS = new_top; +// } +// assert!(found, "Stack violated."); +// } +// } +// } + +// #[rustc_diagnostic_item = "KaniNewMutableRef"] +// fn new_mut_ref(reference: *const U, referent: *const T) { +// use_2(referent); +// assert!( +// std::mem::size_of_val(unsafe { &*reference }) +// < std::mem::size_of_val(unsafe { &*referent }) +// ); +// for i in 0..std::mem::size_of_val(unsafe { &*reference }) { +// push_shared(pointer.byte_offset(i as isize), kind, tag, KIND_SHARED_RW); +// } +// } + +// #[rustc_diagnostic_item = "KaniNewMutableRaw"] +// fn new_mutable_raw(pointer: *const U, pointee: *const T) { +// use_2(pointee); +// for i in 0..std::mem::size_of_val(unsafe { &*pointer }) { +// push_shared(pointer.byte_offset(i as isize), kind, tag, KIND_SHARED_RW); +// } +// } + +#[kani::proof] +fn main() { + let mut local: i32; + let temp_ref: &mut i32; + let raw_pointer: *mut i32; + let ref_from_raw_1: &mut i32; + let ref_from_raw_2: &mut i32; + + local = 0; + temp_ref = &mut local; + raw_pointer = local as *mut i32; + unsafe { + ref_from_raw_1 = &*temp_ref; + ref_from_raw_1 = 0; + ref_from_raw_2 = &*temp_ref; + ref_from_raw_2 = 1; + ref_from_raw_1 = 1; + } +} diff --git a/tests/expected/aliasing/write_read_write.expected b/tests/expected/aliasing/write_read_write.expected.fixme similarity index 100% rename from tests/expected/aliasing/write_read_write.expected rename to tests/expected/aliasing/write_read_write.expected.fixme diff --git a/tests/expected/aliasing/write_read_write.rs b/tests/expected/aliasing/write_read_write.rs.fixme similarity index 100% rename from tests/expected/aliasing/write_read_write.rs rename to tests/expected/aliasing/write_read_write.rs.fixme From 060cc6f90df33bda3173953ebddf8d18ee551542 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 9 Aug 2024 11:03:30 -0400 Subject: [PATCH 04/72] Fix dup. write code to take raw pointer from ref, not org. var --- tests/expected/aliasing/duplicate_write.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/expected/aliasing/duplicate_write.rs b/tests/expected/aliasing/duplicate_write.rs index f4cc15504522..169675bd707e 100644 --- a/tests/expected/aliasing/duplicate_write.rs +++ b/tests/expected/aliasing/duplicate_write.rs @@ -1,3 +1,4 @@ +// kani-flags: -Zaliasing #![allow(internal_features)] #![feature(rustc_attrs)] #![feature(vec_into_raw_parts)] @@ -480,12 +481,12 @@ fn main() { local = 0; temp_ref = &mut local; - raw_pointer = local as *mut i32; + raw_pointer = temp_ref as *mut i32; unsafe { - ref_from_raw_1 = &*temp_ref; - ref_from_raw_1 = 0; - ref_from_raw_2 = &*temp_ref; - ref_from_raw_2 = 1; - ref_from_raw_1 = 1; + ref_from_raw_1 = &mut *raw_pointer; + *ref_from_raw_1 = 0; + ref_from_raw_2 = &mut *raw_pointer; + *ref_from_raw_2 = 1; + *ref_from_raw_1 = 2; } } From 63390fbdb5f7fe92db957c71d9d78c71968769ba Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 9 Aug 2024 16:15:46 -0400 Subject: [PATCH 05/72] Remove std/kani functions to get efficient impl. --- .../transform/check_aliasing/mod.rs | 105 ++++++++++++-- .../src/kani_middle/transform/mod.rs | 2 +- .../aliasing/duplicate_write.expected | 2 + tests/expected/aliasing/duplicate_write.rs | 132 ++++++++---------- 4 files changed, 157 insertions(+), 84 deletions(-) create mode 100644 tests/expected/aliasing/duplicate_write.expected diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 6d2dbb91adff..2c6894f72118 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::TyCtxt; use stable_mir::mir::mono::Instance; use std::fmt::Debug; use stable_mir::ty::{ - FnDef, GenericArgKind, GenericArgs, Region, RegionKind, RigidTy, Ty, TyKind, Span + AdtDef, GenericArgKind, GenericArgs, Region, RegionKind, RigidTy, Span, Ty, TyKind }; use stable_mir::mir::{ BasicBlockIdx, Body, Local, LocalDecl, Mutability, Place, TerminatorKind, UnwindAction @@ -19,7 +19,34 @@ use stable_mir::mir::{ use stable_mir::{CrateDef, Error}; use stable_mir::mir::{BasicBlock, BorrowKind, MirVisitor, MutBorrowKind, Operand, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, VarDebugInfo }; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; +use tracing::trace; + +fn instrumented_flag_def(tcx: &TyCtxt) -> AdtDef { + let attr_id = tcx + .all_diagnostic_items(()) + .name_to_id + .get(&rustc_span::symbol::Symbol::intern("KaniAliasingChecked")).unwrap(); + if let TyKind::RigidTy(RigidTy::Adt(def, _)) = + rustc_smir::rustc_internal::stable(tcx.type_of(attr_id)).value.kind() { + def + } else { + panic!("Failure") + } +} + +fn instrumented_flag_type(tcx: &TyCtxt) -> Ty { + let attr_id = tcx + .all_diagnostic_items(()) + .name_to_id + .get(&rustc_span::symbol::Symbol::intern("KaniAliasingChecked")).unwrap(); + if let TyKind::RigidTy(ty) = + rustc_smir::rustc_internal::stable(tcx.type_of(attr_id)).value.kind() { + Ty::from_rigid_kind(ty) + } else { + panic!("Failure") + } +} #[derive(Clone, Debug, Eq, PartialEq)] pub struct FunctionSignature { @@ -59,11 +86,17 @@ pub struct StackedBorrowsPass { } /// Instrument the code with checks for uninitialized memory. -#[derive(Debug)] +#[derive(Debug, Default)] pub struct AliasingPass { cache: FunctionInstanceCache, } +impl AliasingPass { + pub fn new() -> AliasingPass { + Default::default() + } +} + struct InitializedPassState<'tcx, 'cache> { body: Body, tcx: TyCtxt<'tcx>, @@ -99,9 +132,17 @@ impl TransformPass for AliasingPass { } fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { - let pass = InitializedPassState::new(body, tcx, &mut self.cache); - let out = pass.collect_locals().collect_body().finalize(); - (true, out) + trace!(function=?instance.name(), "transform: aliasing pass"); + let mut visitor = CheckInstrumented::new(&tcx); + visitor.visit_body(&body); + if visitor.is_instrumented || instance.name().contains("kani") || instance.name().contains("std::mem::size_of") || instance.name().contains("core::num") || instance.name().contains("std::ptr") || instance.name().contains("get_checked") { + // unsafe {ALREADY_INSTRUMENTED_COUNT += 1}; + (false, body) + } else { + let pass = InitializedPassState::new(body, tcx, &mut self.cache); + let out = pass.collect_locals().collect_body().finalize(); + (true, out) + } } } @@ -125,8 +166,13 @@ struct BodyMutationPassState<'tcx, 'cache> { } impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { + fn mark_instrumented(&mut self) -> Local { + let ty = instrumented_flag_type(&self.tcx); + self.body.new_local(ty, Mutability::Not) + } + fn assign_ref( - &mut self, + body: &mut CachedBodyMutator, lvalue: Local, rvalue: Local, span: Span) { @@ -136,7 +182,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let lvalue = Place::from(lvalue); let rvalue = Rvalue::Ref(region, borrow, Place::from(rvalue)); let kind = StatementKind::Assign(lvalue, rvalue); - self.body.insert_statement(Statement { kind, span }); + body.insert_statement(Statement { kind, span }); } fn instrument_local( @@ -146,10 +192,12 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { // Initialize the constants let ty = self.body.local(local).ty; let ref_ty = Ty::new_ref(Region { kind: RegionKind::ReErased }, ty, Mutability::Not ); + let span = self.body.span().clone(); let body = &mut self.body; let local_ref = self.meta_stack.entry(local).or_insert_with(|| body.new_local(ref_ty, Mutability::Not)); + Self::assign_ref(body, *local_ref, local, span); let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniInitializeLocal", &[GenericArgKind::Type(ty)]))?; - body.call(instance, [local, *local_ref].to_vec(), body.unit); + body.call(instance, [*local_ref].to_vec(), body.unit); Ok(()) } @@ -277,6 +325,7 @@ impl<'tcx, 'cache> BodyMutationPassState<'tcx, 'cache> { } fn finalize(mut self) -> Body { + self.instrumentation_data.mark_instrumented(); self.instrument_locals().unwrap(); self.instrumentation_data.body.finalize_prologue(); self.instrument_instructions().unwrap(); @@ -354,6 +403,26 @@ impl<'tcx, 'cache> LocalPassState<'tcx, 'cache> { } } +struct CheckInstrumented { + marker: AdtDef, + is_instrumented: bool, +} + +impl CheckInstrumented { + fn new(tcx: &TyCtxt) -> CheckInstrumented { + CheckInstrumented { marker: instrumented_flag_def(tcx), is_instrumented: false } + } +} + +impl MirVisitor for CheckInstrumented { + fn visit_local_decl(&mut self, _: Local, decl: &LocalDecl) { + let LocalDecl { ty, .. } = decl; + if let TyKind::RigidTy(RigidTy::Adt(def, _)) = ty.kind() { + self.is_instrumented = self.is_instrumented || self.marker == def; + } + } +} + struct CollectLocalVisitor { values: Vec, } @@ -532,8 +601,13 @@ impl BodyMutator { fn new_index(&self) -> MutatorIndex { let len = self.blocks.len(); - let bb = len - 1; - let idx = if len > 0 { self.blocks[bb].statements.len() - 1 } else { 0 }; + let bb = std::cmp::max(len, 1) - 1; + let idx = if len > 0 { + std::cmp::max(self.blocks[bb].statements.len(), 1) + - 1 + } else { + 0 + }; let span = self.span; MutatorIndex { bb, idx, span } } @@ -544,8 +618,13 @@ impl BodyMutator { status = MutatorIndexStatus::Remaining; } if index.idx > 0 { - index.span = self.blocks[index.bb] - .statements[index.idx].span; + if index.idx < self.blocks[index.bb].statements.len() { + index.span = self.blocks[index.bb] + .statements[index.idx].span; + } else { + index.span = self.blocks[index.bb] + .terminator.span; + } index.idx -= 1; } else if index.bb > 0 { index.bb -= 1; diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index 81d412215d4f..f50dc10c7d97 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -87,7 +87,7 @@ impl BodyTransformation { // Check aliasing transformer.add_pass( queries, - AliasingPass { }, + AliasingPass::new(), ); transformer.add_pass( queries, diff --git a/tests/expected/aliasing/duplicate_write.expected b/tests/expected/aliasing/duplicate_write.expected new file mode 100644 index 000000000000..40b69fbc424a --- /dev/null +++ b/tests/expected/aliasing/duplicate_write.expected @@ -0,0 +1,2 @@ +FAILURE\ +assertion failed: Stack violated diff --git a/tests/expected/aliasing/duplicate_write.rs b/tests/expected/aliasing/duplicate_write.rs index 169675bd707e..447c6a39365f 100644 --- a/tests/expected/aliasing/duplicate_write.rs +++ b/tests/expected/aliasing/duplicate_write.rs @@ -1,4 +1,4 @@ -// kani-flags: -Zaliasing +// kani-flags: -Zghost-state -Zaliasing #![allow(internal_features)] #![feature(rustc_attrs)] #![feature(vec_into_raw_parts)] @@ -10,57 +10,49 @@ #![feature(const_trait_impl)] #![cfg_attr(not(kani), feature(register_tool))] #![cfg_attr(not(kani), register_tool(kani))] -use std::ptr::null; -use std::ptr::addr_of; -use std::convert::TryInto; -const MAX_NUM_OBJECTS: usize = 1024; -const MAX_OBJECT_SIZE: usize = 64; +#[derive(Copy, Clone)] +#[rustc_diagnostic_item = "KaniAliasingChecked"] +struct AliasingChecked { amount: usize } const STACK_DEPTH: usize = 15; type PointerTag = u8; -#[cfg(any(kani))] -fn assume(b: bool) { - kani::assume(b); -} +extern crate kani; +use kani::shadow::ShadowMem; -#[cfg(not(kani))] -fn assume(b: bool) { - assert!(b); +#[inline(never)] +fn get_checked() -> AliasingChecked { + static mut CHECKED: AliasingChecked = AliasingChecked { amount: 0 }; + unsafe { CHECKED.amount = CHECKED.amount.wrapping_add(1); CHECKED } } #[cfg(any(kani))] -fn pointer_object(ptr: *const T) -> usize { - kani::mem::pointer_object(ptr) -} - -#[cfg(not(kani))] -fn pointer_object(ptr: *const T) -> usize { - ptr as usize +fn assume(b: bool) { + let checked = get_checked(); + let _ = checked; + kani::assume(b); } -#[cfg(any(kani))] -fn pointer_offset(_ptr: *const T) -> usize { - 0 -} #[cfg(not(kani))] -fn pointer_offset(ptr: *const T) -> usize { - 0 +fn assume(b: bool) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + assert!(b); } /// The stacked borrows state. pub mod sstate { use super::*; /// Associate every pointer object with a tag - static mut TAGS: [[PointerTag; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS] = - [[0; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS]; + static mut TAGS: ShadowMem = ShadowMem::new(0); /// Next pointer id: the next pointer id in sequence static mut NEXT_TAG: PointerTag = 0; #[non_exhaustive] struct Access; + #[allow(unused)] impl Access { pub(self) const READ: bool = false; pub(self) const WRITE: bool = true; @@ -75,13 +67,14 @@ pub mod sstate { pub(self) const DISABLED: u8 = 3; pub(self) fn grants(access: bool, tag: u8) -> bool { + let checked: AliasingChecked = get_checked(); + let _ = checked; tag != Self::DISABLED && (access != Access::WRITE || tag != Self::SHAREDRO) } } /// Associate every pointer object with a permission - static mut PERMS: [[PointerTag; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS] = - [[Permission::UNIQUE; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS]; + static mut PERMS: ShadowMem = ShadowMem::new(0); pub(super) mod monitors { static mut STATE: bool = false; @@ -141,6 +134,8 @@ pub mod sstate { /// An N+1th monitor is allocated as a "garbage" /// area to be used when no monitor is picked. pub fn prepare_monitors() { + let checked: AliasingChecked = get_checked(); + let _ = checked; unsafe { OBJECT = 0usize; // vec![0usize; size].into_raw_parts().0 as *const (); @@ -160,6 +155,8 @@ pub mod sstate { /// Initialize local when track local is true, picking a monitor, /// and setting its object and offset to within pointer. pub(super) unsafe fn track_local(tag: u8, pointer: *const U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; // Decide whether to initialize the stacks // for location:location+size_of(U). // Offset has already been picked earlier. @@ -188,7 +185,7 @@ pub mod sstate { // } if demonic_nondet() && STATE == MonitorState::UNINIT { STATE = MonitorState::INIT; - OBJECT = pointer_object(pointer); + OBJECT = kani::mem::pointer_object(pointer); assume(OFFSET < std::mem::size_of::()); STACK_TAGS[STACK_TOP] = tag; STACK_PERMS[STACK_TOP] = Permission::UNIQUE; @@ -199,6 +196,8 @@ pub mod sstate { /// Push a tag with a permission perm at pointer pub(super) fn push(tag: u8, perm: u8, pointer: *const U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; // Decide whether to initialize the stacks // for location:location+size_of(U). // Offset has already been picked earlier. @@ -214,8 +213,8 @@ pub mod sstate { // let mut i = sstate_config::MONITORS.try_into().unwrap(); if STATE == MonitorState::INIT && - OBJECT == pointer_object(pointer) && - OFFSET == pointer_offset(pointer) + OBJECT == kani::mem::pointer_object(pointer) && + OFFSET == kani::mem::pointer_offset(pointer) { STACK_TAGS[STACK_TOP + 1] = tag; STACK_PERMS[STACK_TOP + 1] = perm; @@ -225,6 +224,8 @@ pub mod sstate { } pub(super) fn stack_check(tag: u8, access: bool, address: *const U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; unsafe { use self::*; // let states = get_states(); @@ -235,8 +236,8 @@ pub mod sstate { // let tops = get_stack_tops(); // let mut i = sstate_config::MONITORS.try_into().unwrap(); if STATE == MonitorState::INIT && - OFFSET == pointer_offset(address) && - OBJECT == pointer_object(address) { + OFFSET == kani::mem::pointer_offset(address) && + OBJECT == kani::mem::pointer_object(address) { let mut found = false; let mut j = 0; let mut new_top = 0; @@ -252,6 +253,8 @@ pub mod sstate { } j += 1; } + STACK_TOP = new_top; + assert!(found, "Stack violated."); } // while i > 0 { // { @@ -271,16 +274,22 @@ pub mod sstate { #[rustc_diagnostic_item = "KaniInitializeSState"] pub fn initialize() { + let checked: AliasingChecked = get_checked(); + let _ = checked; self::monitors::prepare_monitors(); } /// Run a stack check on the pointer value at the given location. pub fn stack_check(tag: u8, access: bool, address: *const U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; self::monitors::stack_check(tag, access, address) } /// Push the permissions at the given location pub fn push(tag: u8, perm: u8, address: *const U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; self::monitors::push(tag, perm, address) } @@ -294,20 +303,25 @@ pub mod sstate { /// set to true after the first write, a track flag set to the value /// of a query to a demonic nondeterminism oracle (when this feature is used) /// and a reference to the stack location. + #[rustc_diagnostic_item = "KaniInitializeLocal"] pub fn initialize_local(pointer: *const U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; unsafe { let tag = NEXT_TAG; - TAGS[pointer_object(pointer)][pointer_offset(pointer)] = NEXT_TAG; - PERMS[pointer_object(pointer)][pointer_offset(pointer)] = Permission::UNIQUE; + TAGS.set(pointer, tag); + PERMS.set(pointer, Permission::UNIQUE); NEXT_TAG += 1; monitors::track_local(tag, pointer); } } pub fn use_2(ptr: *const T) { + let checked: AliasingChecked = get_checked(); + let _ = checked; unsafe { - let tag = TAGS[pointer_object(ptr)][pointer_offset(ptr)]; - let perm = PERMS[pointer_object(ptr)][pointer_offset(ptr)]; + let tag = TAGS.get(ptr); + // let perm = PERMS[pointer_object(ptr)][pointer_offset(ptr)]; for i in 0..std::mem::size_of::() { stack_check(tag, Access::WRITE, ptr.byte_add(i)); } @@ -317,6 +331,8 @@ pub mod sstate { /// Make a new mutable reference at the rvalue. /// Associate the tag with the lvalue. pub fn new_mut_ref(lvalue: *const &mut T, rvalue: &mut T) { + let checked: AliasingChecked = get_checked(); + let _ = checked; unsafe { // use_2 the rvalue use_2(rvalue as *const T); @@ -331,6 +347,8 @@ pub mod sstate { /// Make a raw mutable reference at the rvalue. /// Associate the tag with the lvalue. pub fn new_mut_raw(lvalue: *const *mut T, rvalue: *mut T) { + let checked: AliasingChecked = get_checked(); + let _ = checked; unsafe { // use_2 the rvalue use_2(rvalue as *const T); @@ -345,47 +363,20 @@ pub mod sstate { -type PointerValueKind = u32; -/* Uninitialized pointer tag */ -const KIND_UNINITIALIZED: PointerValueKind = 0; -/* Pointer tag with ID */ -const KIND_IDENTIFIED: PointerValueKind = 1; -/* Tag == none -- e.g. shared mutable reference */ -const KIND_NONE: PointerValueKind = 2; - -static mut POINTER_PERMISSIONS: [[PointerValueKind; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS] = - [[KIND_UNINITIALIZED; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS]; - -static mut POINTER_TAGS: [[usize; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS] = - [[0; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS]; - -static mut POINTER_SIZE: [[usize; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS] = - [[0; MAX_OBJECT_SIZE]; MAX_NUM_OBJECTS]; - #[cfg(any(kani))] fn demonic_nondet() -> bool { + let checked: AliasingChecked = get_checked(); + let _ = checked; kani::any::() } #[cfg(not(kani))] fn demonic_nondet() -> bool { + let checked: AliasingChecked = get_checked(); + let _ = checked; true } -fn track_local() -> bool { - demonic_nondet() -} - -#[cfg(not(kani))] -fn any_usize() -> usize { - 0 -} - -#[cfg(any(kani))] -fn any_usize() -> usize { - kani::any() -} - // #[rustc_diagnostic_item = "KaniPushUnique"] // fn push_unique(pointer: *const U, kind: &mut usize, tag: &mut usize) { // push( @@ -471,6 +462,7 @@ fn any_usize() -> usize { // } // } + #[kani::proof] fn main() { let mut local: i32; From b503aa0f0132c4fe19de9204d5525d9c45f7cc84 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Sat, 10 Aug 2024 17:41:21 -0400 Subject: [PATCH 06/72] Instrument new stack references. --- .../transform/check_aliasing/mod.rs | 99 +++++++++++++------ tests/expected/aliasing/duplicate_write.rs | 7 +- 2 files changed, 75 insertions(+), 31 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 2c6894f72118..78fbb5450bc8 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -16,10 +16,10 @@ use stable_mir::ty::{ use stable_mir::mir::{ BasicBlockIdx, Body, Local, LocalDecl, Mutability, Place, TerminatorKind, UnwindAction }; -use stable_mir::{CrateDef, Error}; +use stable_mir::Error; use stable_mir::mir::{BasicBlock, BorrowKind, MirVisitor, MutBorrowKind, Operand, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, VarDebugInfo }; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use tracing::trace; fn instrumented_flag_def(tcx: &TyCtxt) -> AdtDef { @@ -115,6 +115,28 @@ impl<'tcx, 'cache> InitializedPassState<'tcx, 'cache> { } } +/// Functions containing any of the following in their +/// prefix or in their name will be ignored. +/// This allows skipping instrumenting functions that +/// are called by the instrumentation functions. +const IGNORED_FUNCTIONS: &'static [&'static str] = &[ + "kani", // Skip kani functions + "std::mem::size_of", // skip size_of:: + "core::num", // Skip numerical ops (like .wrapping_add) + "std::ptr", // Skip pointer manipulation functions + "get_checked" // Skip "get checked", which gives a flag + // specifying whether the function is checked. +]; + +// Currently, the above list of functions is too +// coarse-grained; because all kani functions +// are skipped, all std::ptr functions are +// skipped, and kani functions are skipped, +// this pass cannot be used to verify functions +// in those modules, despite the fact that +// only some of those functions in those modules +// are called by the instrumented code. + impl TransformPass for AliasingPass { fn transformation_type() -> TransformationType where @@ -136,7 +158,6 @@ impl TransformPass for AliasingPass { let mut visitor = CheckInstrumented::new(&tcx); visitor.visit_body(&body); if visitor.is_instrumented || instance.name().contains("kani") || instance.name().contains("std::mem::size_of") || instance.name().contains("core::num") || instance.name().contains("std::ptr") || instance.name().contains("get_checked") { - // unsafe {ALREADY_INSTRUMENTED_COUNT += 1}; (false, body) } else { let pass = InitializedPassState::new(body, tcx, &mut self.cache); @@ -178,41 +199,53 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { span: Span) { let kind = RegionKind::ReErased; let region = Region { kind }; - let borrow = BorrowKind::Mut { kind: MutBorrowKind::Default }; + let borrow_kind = BorrowKind::Shared; let lvalue = Place::from(lvalue); - let rvalue = Rvalue::Ref(region, borrow, Place::from(rvalue)); + let rvalue = Rvalue::Ref(region, borrow_kind, Place::from(rvalue)); + let kind = StatementKind::Assign(lvalue, rvalue); + body.insert_statement(Statement { kind, span }); + } + + fn assign_ptr( + body: &mut CachedBodyMutator, + lvalue: Local, + rvalue: Local, + span: Span) { + let lvalue = Place::from(lvalue); + let rvalue = Rvalue::AddressOf(Mutability::Not, Place::from(rvalue)); let kind = StatementKind::Assign(lvalue, rvalue); body.insert_statement(Statement { kind, span }); } + /// For some local, say let x: T; + /// instrument it with the functions that initialize the stack: + /// let ptr_x: *const T = &raw const x; + /// initialize_local(ptr_x); fn instrument_local( &mut self, local: usize, ) -> Result<(), Error> { - // Initialize the constants let ty = self.body.local(local).ty; - let ref_ty = Ty::new_ref(Region { kind: RegionKind::ReErased }, ty, Mutability::Not ); + let ptr_ty = Ty::new_ptr(ty, Mutability::Not); let span = self.body.span().clone(); let body = &mut self.body; - let local_ref = self.meta_stack.entry(local).or_insert_with(|| body.new_local(ref_ty, Mutability::Not)); - Self::assign_ref(body, *local_ref, local, span); + let local_ptr = self.meta_stack.entry(local).or_insert_with(|| body.new_local(ptr_ty, Mutability::Not)); + Self::assign_ptr(body, *local_ptr, local, span); let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniInitializeLocal", &[GenericArgKind::Type(ty)]))?; - body.call(instance, [*local_ref].to_vec(), body.unit); + body.call(instance, [*local_ptr].to_vec(), body.unit); Ok(()) } // get back to this one - // fn instrument_new_stack_reference(&mut self, idx: &MutatorIndex, to: Local, from: Local) -> Result<(), Error> { - // // Initialize the constants - // let ty_from = self.body.local(from).ty; - // let ty_to = self.body.local(from).ty; - // let from_metadata = self.meta_stack.get(&from).unwrap().clone(); - // let to_metadata = self.meta_value.get(&to).unwrap().clone(); - // let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutableRef", &[GenericArgKind::Type(ty_from), GenericArgKind::Type(ty_to)]))?; - // self.body.call(instance, [&from_metadata.0 as &[Local], &to_metadata.0].concat(), self.body.unit); - // self.body.split(idx); - // Ok(()) - // } + fn instrument_new_stack_reference(&mut self, idx: &MutatorIndex, lvalue: Local, referent: Local) -> Result<(), Error> { + // Initialize the constants + let ty = self.body.local(referent).ty; + let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); + let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutRef", &[GenericArgKind::Type(ty)]))?; + self.body.call(instance, vec![*lvalue_ref, lvalue], self.body.unit); + self.body.split(idx); + Ok(()) + } // And this one // fn instrument_new_raw_from_ref(&mut self, idx: &MutatorIndex, to: Local, from: Local) -> Result<(), Error> { @@ -236,8 +269,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { match projection[..] { [] => { // Direct reference to the stack local - // self.instrument_new_stack_reference(idx, to.local, *local) - Ok(()) + // x = &y + self.instrument_new_stack_reference(idx, to.local, *local) } [ProjectionElem::Deref] => { // to = &*from @@ -245,16 +278,27 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Ok(()) } _ => { + // In the future, field accesses can be handled here Ok(()) } } } StatementKind::Assign(to, Rvalue::AddressOf(Mutability::Mut, from)) => { - // to = &raw *from - if self.body.local(from.local).ty.kind().is_ref() { - // let _ = self.instrument_new_raw_from_ref(idx, to.local, from.local); + let Place { local, projection } = from; + match projection[..] { + [] => { + // x = &raw y; + Ok(()) + }, + [ProjectionElem::Deref] => { + // to = &raw *from + // new mut raw + Ok(()) + }, + _ => { + Ok(()) + } } - Ok(()) } StatementKind::Assign(to, Rvalue::Ref(_, _, from)) => { // immutable reference @@ -313,7 +357,6 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { } } - impl<'tcx, 'cache> BodyMutationPassState<'tcx, 'cache> { fn instrument_locals(&mut self) -> Result<(), Error> { self.instrumentation_data.instrument_locals(&self.values) diff --git a/tests/expected/aliasing/duplicate_write.rs b/tests/expected/aliasing/duplicate_write.rs index 447c6a39365f..0893de13e5c4 100644 --- a/tests/expected/aliasing/duplicate_write.rs +++ b/tests/expected/aliasing/duplicate_write.rs @@ -330,14 +330,15 @@ pub mod sstate { /// Make a new mutable reference at the rvalue. /// Associate the tag with the lvalue. - pub fn new_mut_ref(lvalue: *const &mut T, rvalue: &mut T) { + #[rustc_diagnostic_item = "KaniNewMutRef"] + pub fn new_mut_ref(location: *const &mut T, pointer_value: &mut T) { let checked: AliasingChecked = get_checked(); let _ = checked; unsafe { // use_2 the rvalue - use_2(rvalue as *const T); + use_2(pointer_value as *const T); // Then associate the lvalue and push it - push(NEXT_TAG, Permission::UNIQUE, lvalue); + push(NEXT_TAG, Permission::UNIQUE, location); // TAGS[pointer_object(lvalue)][pointer_offset(lvalue)] = NEXT_TAG; // PERMS[pointer_object(lvalue)][pointer_offset(lvalue)] = Permission::UNIQUE; NEXT_TAG += 1; From bb6045bb7ca7b37f8af302538c824ad74a4039b1 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Sun, 11 Aug 2024 16:15:59 -0400 Subject: [PATCH 07/72] Add case x: *mut T = &raw *(y as &mut T); --- .../transform/check_aliasing/mod.rs | 29 ++++++++++--------- tests/expected/aliasing/duplicate_write.rs | 19 +++++------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 78fbb5450bc8..f0e967009673 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -236,7 +236,6 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Ok(()) } - // get back to this one fn instrument_new_stack_reference(&mut self, idx: &MutatorIndex, lvalue: Local, referent: Local) -> Result<(), Error> { // Initialize the constants let ty = self.body.local(referent).ty; @@ -248,17 +247,19 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { } // And this one - // fn instrument_new_raw_from_ref(&mut self, idx: &MutatorIndex, to: Local, from: Local) -> Result<(), Error> { - // // Initialize the constants - // let ty_from = self.body.local(from).ty; - // let ty_to = self.body.local(from).ty; - // let from_metadata = self.meta_value.get(&from).unwrap().clone(); - // let to_metadata = self.meta_value_mut.get(&to).unwrap().clone(); - // let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutableRaw", &[GenericArgKind::Type(ty_from), GenericArgKind::Type(ty_to)]))?; - // self.body.call(instance, [&from_metadata.0 as &[Local], &to_metadata.0].concat(), self.body.unit); - // self.body.split(idx); - // Ok(()) - // } + fn instrument_new_raw_from_ref(&mut self, idx: &MutatorIndex, lvalue: Local, referent: Local) -> Result<(), Error> { + // Initialize the constants + if let TyKind::RigidTy(RigidTy::Ref(_, ty, _)) = + self.body.local(referent).ty.kind() { + let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); + let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutRaw", &[GenericArgKind::Type(ty)]))?; + self.body.call(instance, vec![*lvalue_ref, lvalue], self.body.unit); + self.body.split(idx); + Ok(()) + } else { + panic!("At this time only dereferences of refs are handled here."); + } + } fn instrument_index(&mut self, _values: &Vec, idx: &MutatorIndex) -> Result<(), Error> { match self.body.inspect(idx) { @@ -292,8 +293,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { }, [ProjectionElem::Deref] => { // to = &raw *from - // new mut raw - Ok(()) + // (raw) reborrow + self.instrument_new_raw_from_ref(idx, to.local, *local) }, _ => { Ok(()) diff --git a/tests/expected/aliasing/duplicate_write.rs b/tests/expected/aliasing/duplicate_write.rs index 0893de13e5c4..3cbe59273bb4 100644 --- a/tests/expected/aliasing/duplicate_write.rs +++ b/tests/expected/aliasing/duplicate_write.rs @@ -328,8 +328,8 @@ pub mod sstate { } } - /// Make a new mutable reference at the rvalue. - /// Associate the tag with the lvalue. + /// Make a new mutable reference at the rvalue (pointer_value). + /// Associate the tag with the lvalue (location). #[rustc_diagnostic_item = "KaniNewMutRef"] pub fn new_mut_ref(location: *const &mut T, pointer_value: &mut T) { let checked: AliasingChecked = get_checked(); @@ -339,24 +339,21 @@ pub mod sstate { use_2(pointer_value as *const T); // Then associate the lvalue and push it push(NEXT_TAG, Permission::UNIQUE, location); - // TAGS[pointer_object(lvalue)][pointer_offset(lvalue)] = NEXT_TAG; - // PERMS[pointer_object(lvalue)][pointer_offset(lvalue)] = Permission::UNIQUE; NEXT_TAG += 1; } } - /// Make a raw mutable reference at the rvalue. - /// Associate the tag with the lvalue. - pub fn new_mut_raw(lvalue: *const *mut T, rvalue: *mut T) { + /// Make a raw mutable reference at the rvalue (pointer_value). + /// Associate the tag with the lvalue (location). + #[rustc_diagnostic_item = "KaniNewMutRaw"] + pub fn new_mut_raw(location: *const *mut T, pointer_value: *mut T) { let checked: AliasingChecked = get_checked(); let _ = checked; unsafe { // use_2 the rvalue - use_2(rvalue as *const T); + use_2(pointer_value); // Then associate the lvalue and push it - push(NEXT_TAG, Permission::SHAREDRW, lvalue); - // TAGS[pointer_object(lvalue)][pointer_offset(lvalue)] = NEXT_TAG; - // PERMS[pointer_object(lvalue)][pointer_offset(lvalue)] = Permission::SHAREDRW; + push(NEXT_TAG, Permission::SHAREDRW, location); NEXT_TAG += 1; } } From 8df3234acebe4a8915f0acc71cdbca1a60c6957a Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Sun, 11 Aug 2024 22:01:07 -0400 Subject: [PATCH 08/72] Match both lvalue and rvalue projections --- .../transform/check_aliasing/mod.rs | 125 ++++++------- tests/expected/aliasing/duplicate_write.rs | 164 ------------------ 2 files changed, 64 insertions(+), 225 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index f0e967009673..1f63623fedfd 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -253,8 +253,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { self.body.local(referent).ty.kind() { let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutRaw", &[GenericArgKind::Type(ty)]))?; - self.body.call(instance, vec![*lvalue_ref, lvalue], self.body.unit); - self.body.split(idx); + // self.body.call(instance, vec![*lvalue_ref, lvalue], self.body.unit); + // self.body.split(idx); Ok(()) } else { panic!("At this time only dereferences of refs are handled here."); @@ -265,74 +265,77 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { match self.body.inspect(idx) { Instruction::Stmt(Statement { kind, ..} ) => { match kind { - StatementKind::Assign(to, Rvalue::Ref(_, BorrowKind::Mut { .. }, from)) => { - let Place { local, projection } = from; - match projection[..] { + StatementKind::Assign(to, rvalue) => { + match to.projection[..] { [] => { - // Direct reference to the stack local - // x = &y - self.instrument_new_stack_reference(idx, to.local, *local) + // Assignment directly to local + match rvalue { + Rvalue::Ref(_, BorrowKind::Mut { .. }, from) => { + match from.projection[..] { + [] => { + // Direct reference to the stack local + // x = &y + self.instrument_new_stack_reference(idx, to.local, from.local)?; + Ok(()) + }, + [ProjectionElem::Deref] => { + // Reborrow + // x = &*y + Ok(()) + }, + _ => { + eprintln!("Field projections not yet handled"); + Ok(()) + } + } + }, + Rvalue::AddressOf(Mutability::Mut, from) => { + match from.projection[..] { + [] => { + // x = &raw y + eprintln!("addr of not yet handled"); + Ok(()) + }, + [ProjectionElem::Deref] => { + self.instrument_new_raw_from_ref(idx, to.local, from.local)?; + Ok(()) + }, + _ => { + Ok(()) + } + } + }, + _ => { + eprintln!("Rvalue kind: {:?} not yet handled", rvalue); + Ok(()) + } + } } [ProjectionElem::Deref] => { - // to = &*from - // (Reborrow) + // *x = rvalue + eprintln!("assignment through reference not yet handled"); Ok(()) } _ => { - // In the future, field accesses can be handled here + eprintln!("Field assignment not yet handled"); Ok(()) } } - } - StatementKind::Assign(to, Rvalue::AddressOf(Mutability::Mut, from)) => { - let Place { local, projection } = from; - match projection[..] { - [] => { - // x = &raw y; - Ok(()) - }, - [ProjectionElem::Deref] => { - // to = &raw *from - // (raw) reborrow - self.instrument_new_raw_from_ref(idx, to.local, *local) - }, - _ => { - Ok(()) - } - } - } - StatementKind::Assign(to, Rvalue::Ref(_, _, from)) => { - // immutable reference - (to, from); - Ok(()) - } - StatementKind::Assign(to, Rvalue::Use(Operand::Copy(from))) => { - // TODO impl use local - (to, from); - Ok(()) - } - StatementKind::Assign(to, Rvalue::Use(Operand::Move(from))) => { - // TODO impl move local - (to, from); - Ok(()) - } - StatementKind::Assign(_, Rvalue::Use(Operand::Constant(_))) => { - // load from static memory, ignore - Ok(()) - } - StatementKind::Assign(place, rvalue) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, - StatementKind::Retag(_, _) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, - StatementKind::FakeRead(_, _) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, - StatementKind::SetDiscriminant { place, variant_index } => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, - StatementKind::Deinit(_) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, - StatementKind::StorageLive(_) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, - StatementKind::StorageDead(_) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, - StatementKind::PlaceMention(_) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, - StatementKind::AscribeUserType { place, projections, variance } => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, - StatementKind::Coverage(_) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, - StatementKind::Intrinsic(_) => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, - StatementKind::ConstEvalCounter => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, - StatementKind::Nop => {eprintln!("not yet implemented: {kind:?}"); Ok(())}, + }, + // The following are not yet handled, however, no info is printed + // to avoid blowups: + StatementKind::Retag(_, _) => Ok(()), + StatementKind::FakeRead(_, _) => Ok(()), + StatementKind::SetDiscriminant { .. } => Ok(()), + StatementKind::Deinit(_) => Ok(()), + StatementKind::StorageLive(_) => Ok(()), + StatementKind::StorageDead(_) => Ok(()), + StatementKind::PlaceMention(_) => Ok(()), + StatementKind::AscribeUserType { .. } => Ok(()), + StatementKind::Coverage(_) => Ok(()), + StatementKind::Intrinsic(_) => Ok(()), + StatementKind::ConstEvalCounter => Ok(()), + StatementKind::Nop => Ok(()), } } Instruction::Term(_) => Ok(()), diff --git a/tests/expected/aliasing/duplicate_write.rs b/tests/expected/aliasing/duplicate_write.rs index 3cbe59273bb4..a95ef73e3c5d 100644 --- a/tests/expected/aliasing/duplicate_write.rs +++ b/tests/expected/aliasing/duplicate_write.rs @@ -92,29 +92,6 @@ pub mod sstate { } use super::*; - // pub fn get_objects() -> *mut usize { - // unsafe { OBJECTS as *mut usize } - // } - - // fn get_offsets() -> *mut usize { - // unsafe { OFFSETS as *mut usize } - // } - - // fn get_states() -> *mut bool { - // unsafe { STATES as *mut bool } - // } - - // fn get_stack_tops() -> *mut usize { - // unsafe { STACK_TOPS as *mut usize } - // } - - // fn get_stack_ids() -> *mut [PointerTag; STACK_DEPTH] { - // unsafe { STACK_TAGS as *mut [PointerTag; STACK_DEPTH] } - // } - - // fn get_stack_permissions() -> *mut [u8; STACK_DEPTH] { - // unsafe { STACK_TAGS as *mut [u8; STACK_DEPTH] } - // } /// Monitors: /// If there are K bytes in the address space, @@ -138,17 +115,11 @@ pub mod sstate { let _ = checked; unsafe { OBJECT = 0usize; - // vec![0usize; size].into_raw_parts().0 as *const (); OFFSET = 0usize; - // vec![0usize; size].into_raw_parts().0 as *const (); STATE = MonitorState::UNINIT; - // vec![MonitorState::UNINIT; size].into_raw_parts().0 as *const (); STACK_TAGS = [NEXT_TAG; STACK_DEPTH]; - // vec![[NEXT_TAG; STACK_DEPTH]; size].into_raw_parts().0 as *const (); STACK_PERMS = [Permission::UNIQUE; STACK_DEPTH]; - // vec![[Permission::UNIQUE; STACK_DEPTH]; size].into_raw_parts().0 as *const (); STACK_TOP = 0usize; - // vec![0usize; size].into_raw_parts().0 as *const (); } } @@ -161,28 +132,6 @@ pub mod sstate { // for location:location+size_of(U). // Offset has already been picked earlier. unsafe { - // Pick a monitor nondeterministically - // use self::*; - // let states = get_states(); - // let objects = get_objects(); - // let offsets = get_offsets(); - // let stack_ids = get_stack_ids(); - // let stack_perms = get_stack_permissions(); - // let tops = get_stack_tops(); - - // let mut i = sstate_config::MONITORS.try_into().unwrap(); - // while i > 0 { - // i -= 1; - // if demonic_nondet() && *states.offset(i) == MonitorState::UNINIT { - // let top = *tops.offset(i); - // *states.offset(i) = MonitorState::INIT; - // *objects.offset(i) = pointer_object(pointer); - // assume(*offsets.offset(i) == 0 || - // *offsets.offset(i) < std::mem::size_of::()); - // (*stack_ids.offset(i))[0] = tag; - // (*stack_perms.offset(i))[0] = Permission::UNIQUE; - // } - // } if demonic_nondet() && STATE == MonitorState::UNINIT { STATE = MonitorState::INIT; OBJECT = kani::mem::pointer_object(pointer); @@ -204,14 +153,6 @@ pub mod sstate { unsafe { // Pick a monitor nondeterministically use self::*; - // let states = get_states(); - // let objects = get_objects(); - // let offsets = get_offsets(); - // let stack_ids = get_stack_ids(); - // let stack_perms = get_stack_permissions(); - // let tops = get_stack_tops(); - - // let mut i = sstate_config::MONITORS.try_into().unwrap(); if STATE == MonitorState::INIT && OBJECT == kani::mem::pointer_object(pointer) && OFFSET == kani::mem::pointer_offset(pointer) @@ -228,13 +169,6 @@ pub mod sstate { let _ = checked; unsafe { use self::*; - // let states = get_states(); - // let objects = get_objects(); - // let offsets = get_offsets(); - // let stack_ids = get_stack_ids(); - // let stack_perms = get_stack_permissions(); - // let tops = get_stack_tops(); - // let mut i = sstate_config::MONITORS.try_into().unwrap(); if STATE == MonitorState::INIT && OFFSET == kani::mem::pointer_offset(address) && OBJECT == kani::mem::pointer_object(address) { @@ -256,18 +190,6 @@ pub mod sstate { STACK_TOP = new_top; assert!(found, "Stack violated."); } - // while i > 0 { - // { - // let top = *tops.offset(i); - // let mut found = false; - // let mut j = STACK_DEPTH; - // let mut new_top = 0; - // while j > 0 { - // } - // assert!(found, "Stack violated."); - // *tops.offset(i) = new_top; - // } - // } } } } @@ -375,92 +297,6 @@ fn demonic_nondet() -> bool { true } -// #[rustc_diagnostic_item = "KaniPushUnique"] -// fn push_unique(pointer: *const U, kind: &mut usize, tag: &mut usize) { -// push( -// pointer, -// &mut POINTER_PERMISSIONS[SSTATE_MONITOR_OBJECT][SSTATE_MONITOR_OFFSET], -// &mut POINTER_TAGS[SSTATE_MONITOR_OBJECT][SSTATE_MONITOR_OBJECT], -// KIND_UNIQUE, -// ); -// } - -// pub fn push(pointer: *const U, kind: &mut usize, tag: &mut usize, create: PointerValueKind) { -// unsafe { -// *tag = 0; -// if monitored(pointer) { -// if create == KIND_SHARED_RW { -// *tag = SSTATE_NEXT_TAG; -// SSTATE_NEXT_TAG += 1; -// } -// *kind = create; -// let top = STATE_STACK_TOPS; -// assert!(top < STACK_DEPTH); -// SSTATE_STACK_PERMS[top] = *kind; -// SSTATE_STACK_TAGS[top] = *tag; -// SSTATE_STACK_TOPS += 1; -// } -// } -// } - -// #[rustc_diagnostic_item = "KaniUse2"] -// fn use_2(pointer: *const U) { -// unsafe { -// if monitored(pointer) { -// let top = SSTATE_STACK_TOPS; -// let mut found = false; -// assert!(kind != KIND_UNINITIALIZED); -// let needle_kind = POINTER_PERMISSIONS[pointer_object(pointer)][pointer_offset(pointer)]; -// let needle_id = POINTER_IDS[pointer_object(pointer)][pointer_offset(pointer)]; -// let mut i = 0; -// let mut new_top = 0; -// while (i < STACK_DEPTH) && (i < top) { -// if SSTATE_STACK_PERMS[i] == to_find && SSTATE_STACK_TAGS[i] == id { -// new_top = i + 1; -// found = true; -// } -// i += 1; -// } -// SSTATE_STACK_TOPS = new_top; -// if kind != KIND_UNINITIALIZED { -// } else { -// let mut i = 0; -// let mut new_top = 0; -// while (i < STACK_DEPTH) && (i < top) { -// if SSTATE_STACK_PERMS[i] == KIND_SHARED_RW { -// new_top = i + 1; -// found = true; -// } -// i += 1; -// } -// SSTATE_STACK_TOPS = new_top; -// } -// assert!(found, "Stack violated."); -// } -// } -// } - -// #[rustc_diagnostic_item = "KaniNewMutableRef"] -// fn new_mut_ref(reference: *const U, referent: *const T) { -// use_2(referent); -// assert!( -// std::mem::size_of_val(unsafe { &*reference }) -// < std::mem::size_of_val(unsafe { &*referent }) -// ); -// for i in 0..std::mem::size_of_val(unsafe { &*reference }) { -// push_shared(pointer.byte_offset(i as isize), kind, tag, KIND_SHARED_RW); -// } -// } - -// #[rustc_diagnostic_item = "KaniNewMutableRaw"] -// fn new_mutable_raw(pointer: *const U, pointee: *const T) { -// use_2(pointee); -// for i in 0..std::mem::size_of_val(unsafe { &*pointer }) { -// push_shared(pointer.byte_offset(i as isize), kind, tag, KIND_SHARED_RW); -// } -// } - - #[kani::proof] fn main() { let mut local: i32; From c732b70eb3f0036d7105637e5b52b3c287642167 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Sun, 11 Aug 2024 22:22:33 -0400 Subject: [PATCH 09/72] Uncomment important lines --- kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 1f63623fedfd..a7137f3483df 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -253,8 +253,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { self.body.local(referent).ty.kind() { let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutRaw", &[GenericArgKind::Type(ty)]))?; - // self.body.call(instance, vec![*lvalue_ref, lvalue], self.body.unit); - // self.body.split(idx); + self.body.call(instance, vec![*lvalue_ref, lvalue], self.body.unit); + self.body.split(idx); Ok(()) } else { panic!("At this time only dereferences of refs are handled here."); From 4e298cecadbad87f87c70ed7ed35d6604ec53aa8 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Sun, 11 Aug 2024 23:25:42 -0400 Subject: [PATCH 10/72] Instrument with use_2 --- .../transform/check_aliasing/mod.rs | 27 ++++++++++++++++--- tests/expected/aliasing/duplicate_write.rs | 5 +++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index a7137f3483df..2137b2389ab4 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -157,7 +157,7 @@ impl TransformPass for AliasingPass { trace!(function=?instance.name(), "transform: aliasing pass"); let mut visitor = CheckInstrumented::new(&tcx); visitor.visit_body(&body); - if visitor.is_instrumented || instance.name().contains("kani") || instance.name().contains("std::mem::size_of") || instance.name().contains("core::num") || instance.name().contains("std::ptr") || instance.name().contains("get_checked") { + if visitor.is_instrumented || !(instance.name().contains("main")) /* for now, just check main for efficiency */ { (false, body) } else { let pass = InitializedPassState::new(body, tcx, &mut self.cache); @@ -246,7 +246,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Ok(()) } - // And this one + fn instrument_new_raw_from_ref(&mut self, idx: &MutatorIndex, lvalue: Local, referent: Local) -> Result<(), Error> { // Initialize the constants if let TyKind::RigidTy(RigidTy::Ref(_, ty, _)) = @@ -261,6 +261,21 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { } } + fn instrument_write_through_pointer(&mut self, idx: &MutatorIndex, lvalue: Local) -> Result<(), Error> { + // Initialize the constants + if let TyKind::RigidTy(RigidTy::Ref(_, ty, _)) | TyKind::RigidTy(RigidTy::RawPtr(ty, _)) = self.body.local(lvalue).ty.kind() { + let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); + let ty = Ty::from_rigid_kind(RigidTy::RawPtr(ty, Mutability::Not)); + let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniWriteThroughPointer", &[GenericArgKind::Type(ty)]))?; + /* Limitation: calls use_2 on a reference or pointer, when use_2's input type is a pointer */ + self.body.call(instance, vec![*lvalue_ref], self.body.unit); + self.body.split(idx); + Ok(()) + } else { + panic!("At this time only dereferences of refs are handled here."); + } + } + fn instrument_index(&mut self, _values: &Vec, idx: &MutatorIndex) -> Result<(), Error> { match self.body.inspect(idx) { Instruction::Stmt(Statement { kind, ..} ) => { @@ -313,7 +328,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { } [ProjectionElem::Deref] => { // *x = rvalue - eprintln!("assignment through reference not yet handled"); + self.instrument_write_through_pointer(idx, to.local)?; Ok(()) } _ => { @@ -542,7 +557,11 @@ impl CachedBodyMutator { } fn local(&self, idx: usize) -> &LocalDecl { - &self.body.locals[idx] + if idx > self.body.locals.len() { + &self.body.ghost_locals[idx - self.body.locals.len()] + } else { + &self.body.locals[idx] + } } fn new_local(&mut self, ty: Ty, mutability: Mutability) -> Local { diff --git a/tests/expected/aliasing/duplicate_write.rs b/tests/expected/aliasing/duplicate_write.rs index a95ef73e3c5d..e8182f020c28 100644 --- a/tests/expected/aliasing/duplicate_write.rs +++ b/tests/expected/aliasing/duplicate_write.rs @@ -238,6 +238,7 @@ pub mod sstate { } } + #[rustc_diagnostic_item = "KaniWriteThroughPointer"] pub fn use_2(ptr: *const T) { let checked: AliasingChecked = get_checked(); let _ = checked; @@ -257,9 +258,10 @@ pub mod sstate { let checked: AliasingChecked = get_checked(); let _ = checked; unsafe { - // use_2 the rvalue + // use_2 the rvalue in the case it is set use_2(pointer_value as *const T); // Then associate the lvalue and push it + TAGS.set(location, NEXT_TAG); push(NEXT_TAG, Permission::UNIQUE, location); NEXT_TAG += 1; } @@ -275,6 +277,7 @@ pub mod sstate { // use_2 the rvalue use_2(pointer_value); // Then associate the lvalue and push it + TAGS.set(location, NEXT_TAG); push(NEXT_TAG, Permission::SHAREDRW, location); NEXT_TAG += 1; } From a8e8470bcad2c5443e0652e121ddc601df54e0a7 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Mon, 12 Aug 2024 17:07:17 -0400 Subject: [PATCH 11/72] Add println statements for failing stack checks --- tests/expected/aliasing/duplicate_write.rs | 267 ++++++++++++++++++--- 1 file changed, 229 insertions(+), 38 deletions(-) diff --git a/tests/expected/aliasing/duplicate_write.rs b/tests/expected/aliasing/duplicate_write.rs index e8182f020c28..c3be6fa8eaa4 100644 --- a/tests/expected/aliasing/duplicate_write.rs +++ b/tests/expected/aliasing/duplicate_write.rs @@ -18,8 +18,48 @@ struct AliasingChecked { amount: usize } const STACK_DEPTH: usize = 15; type PointerTag = u8; +#[cfg(any(kani))] extern crate kani; +#[cfg(any(kani))] use kani::shadow::ShadowMem; +#[cfg(not(kani))] +use std::collections::HashMap; +#[cfg(not(kani))] +#[derive(Debug)] +struct ShadowMem{ + mem: Option>, + default: T +} +#[cfg(not(kani))] +static mut FOUND_POINTERS: Option> = None; +#[cfg(not(kani))] +static mut FOUND_POINTERS_TOP: usize = 0; + +#[cfg(not(kani))] +impl ShadowMem where T: Copy { + #[inline(always)] + const fn new(v: T) -> Self { + ShadowMem { mem: None, default: v } + } + + fn set(&mut self, pointer: *const U, value: T) { + unsafe { + let map = FOUND_POINTERS.get_or_insert(HashMap::new()); + let e = map.entry(pointer as *const u8) + .or_insert_with(|| { let top = FOUND_POINTERS_TOP; FOUND_POINTERS_TOP += 1; top } ); + let mem = self.mem.get_or_insert(HashMap::new()); + mem.insert(*e, value); + } + } + + fn get(&self, pointer: *const U) -> T { + unsafe { + let map = FOUND_POINTERS.get_or_insert(HashMap::new()); + let pointer = pointer as *const u8; + *(self.mem.as_ref().unwrap().get(map.get(&pointer).unwrap()).unwrap_or(&self.default)) + } + } +} #[inline(never)] fn get_checked() -> AliasingChecked { @@ -27,6 +67,30 @@ fn get_checked() -> AliasingChecked { unsafe { CHECKED.amount = CHECKED.amount.wrapping_add(1); CHECKED } } +#[cfg(any(kani))] +fn pointer_object(pointer: *const U) -> usize { + let checked = get_checked(); + let _ = checked; + kani::mem::pointer_object(pointer) +} + +#[cfg(not(kani))] +fn pointer_object(pointer: *const U) -> usize { + pointer as usize +} + +#[cfg(any(kani))] +fn pointer_offset(pointer: *const U) -> usize { + let checked = get_checked(); + let _ = checked; + kani::mem::pointer_offset(pointer) +} + +#[cfg(not(kani))] +fn pointer_offset(_pointer: *const U) -> usize { + 0 +} + #[cfg(any(kani))] fn assume(b: bool) { let checked = get_checked(); @@ -76,6 +140,22 @@ pub mod sstate { /// Associate every pointer object with a permission static mut PERMS: ShadowMem = ShadowMem::new(0); + #[cfg(not(kani))] + pub fn debug() { + unsafe { + println!("tags & perms at this point: tags: {:?} perms: {:?}", sstate::TAGS, sstate::PERMS); + self::monitors::debug_monitors(); + } + } + + #[cfg(any(kani))] + #[inline(always)] + pub fn debug() { + let checked = get_checked(); + let _ = checked; + return; + } + pub(super) mod monitors { static mut STATE: bool = false; static mut OBJECT: usize = 0; @@ -84,6 +164,14 @@ pub mod sstate { static mut STACK_PERMS: [u8; STACK_DEPTH] = [0; STACK_DEPTH]; static mut STACK_TOP: usize = 0; + #[cfg(not(kani))] + pub fn debug_monitors() { + unsafe { + println!("monitors at this point state: {:?} object: {:?} offset: {:?} stags: {:?} sperms: {:?} stop: {:?}", + STATE, OBJECT, OFFSET, STACK_TAGS, STACK_PERMS, STACK_TOP); + } + } + #[non_exhaustive] struct MonitorState; impl MonitorState { @@ -134,7 +222,7 @@ pub mod sstate { unsafe { if demonic_nondet() && STATE == MonitorState::UNINIT { STATE = MonitorState::INIT; - OBJECT = kani::mem::pointer_object(pointer); + OBJECT = pointer_object(pointer); assume(OFFSET < std::mem::size_of::()); STACK_TAGS[STACK_TOP] = tag; STACK_PERMS[STACK_TOP] = Permission::UNIQUE; @@ -151,14 +239,13 @@ pub mod sstate { // for location:location+size_of(U). // Offset has already been picked earlier. unsafe { - // Pick a monitor nondeterministically use self::*; if STATE == MonitorState::INIT && - OBJECT == kani::mem::pointer_object(pointer) && - OFFSET == kani::mem::pointer_offset(pointer) + OBJECT == pointer_object(pointer) && + OFFSET == pointer_offset(pointer) { - STACK_TAGS[STACK_TOP + 1] = tag; - STACK_PERMS[STACK_TOP + 1] = perm; + STACK_TAGS[STACK_TOP] = tag; + STACK_PERMS[STACK_TOP] = perm; STACK_TOP += 1; } } @@ -170,8 +257,8 @@ pub mod sstate { unsafe { use self::*; if STATE == MonitorState::INIT && - OFFSET == kani::mem::pointer_offset(address) && - OBJECT == kani::mem::pointer_object(address) { + OFFSET == pointer_offset(address) && + OBJECT == pointer_object(address) { let mut found = false; let mut j = 0; let mut new_top = 0; @@ -201,13 +288,6 @@ pub mod sstate { self::monitors::prepare_monitors(); } - /// Run a stack check on the pointer value at the given location. - pub fn stack_check(tag: u8, access: bool, address: *const U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - self::monitors::stack_check(tag, access, address) - } - /// Push the permissions at the given location pub fn push(tag: u8, perm: u8, address: *const U) { let checked: AliasingChecked = get_checked(); @@ -238,47 +318,75 @@ pub mod sstate { } } - #[rustc_diagnostic_item = "KaniWriteThroughPointer"] - pub fn use_2(ptr: *const T) { + #[rustc_diagnostic_item = "KaniStackCheckPtr"] + pub fn stack_check_ptr(pointer_value: *const *mut U) { let checked: AliasingChecked = get_checked(); let _ = checked; unsafe { - let tag = TAGS.get(ptr); - // let perm = PERMS[pointer_object(ptr)][pointer_offset(ptr)]; - for i in 0..std::mem::size_of::() { - stack_check(tag, Access::WRITE, ptr.byte_add(i)); + let tag = TAGS.get(pointer_value); + let perm = PERMS.get(pointer_value); + let pointer = *pointer_value; + for i in 0..std::mem::size_of::() { + for access in [false, true] { + if Permission::grants(access, perm) { + self::monitors::stack_check(tag, access, pointer.byte_add(i)); + } + } } } } - /// Make a new mutable reference at the rvalue (pointer_value). - /// Associate the tag with the lvalue (location). - #[rustc_diagnostic_item = "KaniNewMutRef"] - pub fn new_mut_ref(location: *const &mut T, pointer_value: &mut T) { + #[rustc_diagnostic_item = "KaniStackCheckRef"] + pub fn stack_check_ref(pointer_value: *const &mut U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + stack_check_ptr(pointer_value as *const *mut U); + } + + /// Update the stacked borrows state for the case created: &mut T = &mut (referent:T) + /// by associating the location of the created value, stored at pointer_to_created, + /// with a new tag, and pushing the new tag to the created reference, stored at + /// pointer_to_val. + #[rustc_diagnostic_item = "KaniNewMutRefFromLocal"] + pub fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + unsafe { + // Then associate the lvalue and push it + TAGS.set(pointer_to_created, NEXT_TAG); + push(NEXT_TAG, Permission::SHAREDRW, pointer_to_val); + NEXT_TAG += 1; + } + } + + /// Update the stacked borrows state for the case created = (reference: &mut T) as *mut T, + /// associating the location of the created value, stored at pointer_to_created, with a new + /// tag, running a stack check on the tag associated with the reference, accessed by + /// pointer_to_ref, and pushing the tag to the original location. + #[rustc_diagnostic_item = "KaniNewMutRawFromRef"] + pub fn new_mut_raw_from_ref(pointer_to_created: *const *mut T, pointer_to_ref: *const &mut T) { let checked: AliasingChecked = get_checked(); let _ = checked; unsafe { - // use_2 the rvalue in the case it is set - use_2(pointer_value as *const T); // Then associate the lvalue and push it - TAGS.set(location, NEXT_TAG); - push(NEXT_TAG, Permission::UNIQUE, location); + TAGS.set(pointer_to_created, NEXT_TAG); + push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); NEXT_TAG += 1; } } - /// Make a raw mutable reference at the rvalue (pointer_value). - /// Associate the tag with the lvalue (location). - #[rustc_diagnostic_item = "KaniNewMutRaw"] - pub fn new_mut_raw(location: *const *mut T, pointer_value: *mut T) { + /// Update the stacked borrows state for the case created = (reference: &mut T) as *mut T, + /// associating the location of the created value, stored at pointer_to_created, with a new + /// tag, running a stack check on the tag associated with the reference, accessed by + /// pointer_to_ref, and pushing the tag to the original location. + #[rustc_diagnostic_item = "KaniNewMutRefFromRaw"] + pub fn new_mut_ref_from_raw(pointer_to_created: *const &mut T, pointer_to_ref: *const *mut T) { let checked: AliasingChecked = get_checked(); let _ = checked; unsafe { - // use_2 the rvalue - use_2(pointer_value); // Then associate the lvalue and push it - TAGS.set(location, NEXT_TAG); - push(NEXT_TAG, Permission::SHAREDRW, location); + TAGS.set(pointer_to_created, NEXT_TAG); + push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); NEXT_TAG += 1; } } @@ -300,7 +408,65 @@ fn demonic_nondet() -> bool { true } -#[kani::proof] +#[cfg(not(kani))] +static mut LOCAL_COUNT: u32 = 0; + +#[cfg(not(kani))] +fn initialize_local(local: *const T) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + unsafe { + println!("Initializing local {:?}", LOCAL_COUNT); + } + sstate::initialize_local(local); + sstate::debug(); + unsafe { LOCAL_COUNT += 1 }; +} + +#[cfg(not(kani))] +fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + println!("new mut ref from value"); + sstate::new_mut_ref_from_value(pointer_to_created, pointer_to_val); + sstate::debug(); +} + +#[cfg(not(kani))] +pub fn new_mut_raw_from_ref(pointer_to_created: *const *mut T, pointer_to_ref: *const &mut T) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + println!("new mut raw from ref"); + sstate::new_mut_raw_from_ref(pointer_to_created, pointer_to_ref); + sstate::debug(); +} + +#[cfg(not(kani))] +pub fn new_mut_ref_from_raw(pointer_to_created: *const &mut T, pointer_to_ref: *const *mut T) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + println!("new mut ref from raw"); + sstate::new_mut_ref_from_raw(pointer_to_created, pointer_to_ref); + sstate::debug(); +} + +pub fn stack_check_ptr(pointer_value: *const *mut U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + println!("checking ptr on stack"); + sstate::stack_check_ptr(pointer_value); + sstate::debug(); +} + +pub fn stack_check_ref(pointer_value: *const &mut U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + println!("checking ref on stack"); + sstate::stack_check_ref(pointer_value); + sstate::debug(); +} + +#[cfg_attr(any(kani), kani::proof)] fn main() { let mut local: i32; let temp_ref: &mut i32; @@ -309,13 +475,38 @@ fn main() { let ref_from_raw_2: &mut i32; local = 0; + #[cfg(not(kani))] + initialize_local(std::ptr::addr_of!(local)); temp_ref = &mut local; + #[cfg(not(kani))] + initialize_local(std::ptr::addr_of!(temp_ref)); raw_pointer = temp_ref as *mut i32; + #[cfg(not(kani))] + initialize_local(std::ptr::addr_of!(raw_pointer)); + #[cfg(not(kani))] + new_mut_ref_from_value(std::ptr::addr_of!(temp_ref), + temp_ref); + #[cfg(not(kani))] + stack_check_ref(std::ptr::addr_of!(temp_ref)); + #[cfg(not(kani))] + new_mut_raw_from_ref(std::ptr::addr_of!(raw_pointer), std::ptr::addr_of!(temp_ref)); unsafe { ref_from_raw_1 = &mut *raw_pointer; + #[cfg(not(kani))] + new_mut_ref_from_raw(std::ptr::addr_of!(ref_from_raw_1), std::ptr::addr_of!(raw_pointer)); *ref_from_raw_1 = 0; + #[cfg(not(kani))] + stack_check_ref(std::ptr::addr_of!(ref_from_raw_1)); ref_from_raw_2 = &mut *raw_pointer; + #[cfg(not(kani))] + stack_check_ptr(std::ptr::addr_of!(raw_pointer)); + #[cfg(not(kani))] + new_mut_ref_from_raw(std::ptr::addr_of!(ref_from_raw_2), std::ptr::addr_of!(raw_pointer)); *ref_from_raw_2 = 1; + #[cfg(not(kani))] + stack_check_ref(std::ptr::addr_of!(ref_from_raw_2)); *ref_from_raw_1 = 2; + #[cfg(not(kani))] + stack_check_ref(std::ptr::addr_of!(ref_from_raw_1)); } } From 02946882ca8decdf3dda538e8766619fa2368809 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Mon, 12 Aug 2024 17:39:57 -0400 Subject: [PATCH 12/72] To clear noise, instrument for non-kani in macros. --- tests/expected/aliasing/duplicate_write.rs | 522 ++------------------- tests/expected/aliasing/stackfns_ignore.rs | 469 ++++++++++++++++++ 2 files changed, 519 insertions(+), 472 deletions(-) create mode 100644 tests/expected/aliasing/stackfns_ignore.rs diff --git a/tests/expected/aliasing/duplicate_write.rs b/tests/expected/aliasing/duplicate_write.rs index c3be6fa8eaa4..e7ca46499256 100644 --- a/tests/expected/aliasing/duplicate_write.rs +++ b/tests/expected/aliasing/duplicate_write.rs @@ -1,469 +1,59 @@ // kani-flags: -Zghost-state -Zaliasing -#![allow(internal_features)] +#![feature(register_tool)] #![feature(rustc_attrs)] -#![feature(vec_into_raw_parts)] -// Copyright Jacob Salzberg -// SPDX-License-Identifier: Apache-2.0 +mod stackfns_ignore; +use stackfns_ignore::*; -// Basic test from the stacked borrows paper -#![allow(non_snake_case)] -#![feature(const_trait_impl)] -#![cfg_attr(not(kani), feature(register_tool))] -#![cfg_attr(not(kani), register_tool(kani))] - -#[derive(Copy, Clone)] -#[rustc_diagnostic_item = "KaniAliasingChecked"] -struct AliasingChecked { amount: usize } - -const STACK_DEPTH: usize = 15; -type PointerTag = u8; - -#[cfg(any(kani))] -extern crate kani; -#[cfg(any(kani))] -use kani::shadow::ShadowMem; -#[cfg(not(kani))] -use std::collections::HashMap; -#[cfg(not(kani))] -#[derive(Debug)] -struct ShadowMem{ - mem: Option>, - default: T -} -#[cfg(not(kani))] -static mut FOUND_POINTERS: Option> = None; -#[cfg(not(kani))] -static mut FOUND_POINTERS_TOP: usize = 0; - -#[cfg(not(kani))] -impl ShadowMem where T: Copy { - #[inline(always)] - const fn new(v: T) -> Self { - ShadowMem { mem: None, default: v } - } - - fn set(&mut self, pointer: *const U, value: T) { - unsafe { - let map = FOUND_POINTERS.get_or_insert(HashMap::new()); - let e = map.entry(pointer as *const u8) - .or_insert_with(|| { let top = FOUND_POINTERS_TOP; FOUND_POINTERS_TOP += 1; top } ); - let mem = self.mem.get_or_insert(HashMap::new()); - mem.insert(*e, value); - } - } - - fn get(&self, pointer: *const U) -> T { - unsafe { - let map = FOUND_POINTERS.get_or_insert(HashMap::new()); - let pointer = pointer as *const u8; - *(self.mem.as_ref().unwrap().get(map.get(&pointer).unwrap()).unwrap_or(&self.default)) - } +macro_rules! initialize { + () => { + #[cfg(not(kani))] + sstate::initialize(); } } -#[inline(never)] -fn get_checked() -> AliasingChecked { - static mut CHECKED: AliasingChecked = AliasingChecked { amount: 0 }; - unsafe { CHECKED.amount = CHECKED.amount.wrapping_add(1); CHECKED } -} - -#[cfg(any(kani))] -fn pointer_object(pointer: *const U) -> usize { - let checked = get_checked(); - let _ = checked; - kani::mem::pointer_object(pointer) -} - -#[cfg(not(kani))] -fn pointer_object(pointer: *const U) -> usize { - pointer as usize -} - -#[cfg(any(kani))] -fn pointer_offset(pointer: *const U) -> usize { - let checked = get_checked(); - let _ = checked; - kani::mem::pointer_offset(pointer) -} - -#[cfg(not(kani))] -fn pointer_offset(_pointer: *const U) -> usize { - 0 -} - -#[cfg(any(kani))] -fn assume(b: bool) { - let checked = get_checked(); - let _ = checked; - kani::assume(b); -} - - -#[cfg(not(kani))] -fn assume(b: bool) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - assert!(b); -} - -/// The stacked borrows state. -pub mod sstate { - use super::*; - /// Associate every pointer object with a tag - static mut TAGS: ShadowMem = ShadowMem::new(0); - /// Next pointer id: the next pointer id in sequence - static mut NEXT_TAG: PointerTag = 0; - - #[non_exhaustive] - struct Access; - #[allow(unused)] - impl Access { - pub(self) const READ: bool = false; - pub(self) const WRITE: bool = true; - } - - #[non_exhaustive] - struct Permission; - impl Permission { - pub(self) const UNIQUE: u8 = 0; - pub(self) const SHAREDRW: u8 = 1; - pub(self) const SHAREDRO: u8 = 2; - pub(self) const DISABLED: u8 = 3; - - pub(self) fn grants(access: bool, tag: u8) -> bool { - let checked: AliasingChecked = get_checked(); - let _ = checked; - tag != Self::DISABLED && (access != Access::WRITE || tag != Self::SHAREDRO) - } - } - - /// Associate every pointer object with a permission - static mut PERMS: ShadowMem = ShadowMem::new(0); - - #[cfg(not(kani))] - pub fn debug() { - unsafe { - println!("tags & perms at this point: tags: {:?} perms: {:?}", sstate::TAGS, sstate::PERMS); - self::monitors::debug_monitors(); - } - } - - #[cfg(any(kani))] - #[inline(always)] - pub fn debug() { - let checked = get_checked(); - let _ = checked; - return; - } - - pub(super) mod monitors { - static mut STATE: bool = false; - static mut OBJECT: usize = 0; - static mut OFFSET: usize = 0; - static mut STACK_TAGS: [u8; STACK_DEPTH] = [0; STACK_DEPTH]; - static mut STACK_PERMS: [u8; STACK_DEPTH] = [0; STACK_DEPTH]; - static mut STACK_TOP: usize = 0; - +macro_rules! initialize_local { + ($place:ident) => { #[cfg(not(kani))] - pub fn debug_monitors() { - unsafe { - println!("monitors at this point state: {:?} object: {:?} offset: {:?} stags: {:?} sperms: {:?} stop: {:?}", - STATE, OBJECT, OFFSET, STACK_TAGS, STACK_PERMS, STACK_TOP); - } - } - - #[non_exhaustive] - struct MonitorState; - impl MonitorState { - pub(self) const UNINIT: bool = false; - pub(self) const INIT: bool = true; - } - - use super::*; - - /// Monitors: - /// If there are K bytes in the address space, - /// every stacked borrows instrumentation has - /// between 0 and K monitors. - /// These monitors track a single byte of the program, - /// associating it with a stack of pointer values - /// (represented by tags). - /// Whenever a pointer borrows an object containing - /// the byte, its tag is pushed to the stack; - /// when a read or write is performed through this pointer, - /// writes from pointers above its location on the stack - /// are disabled. - /// This function prepares N monitors, - /// writes them to global heap memory, then - /// stores them in pointers. - /// An N+1th monitor is allocated as a "garbage" - /// area to be used when no monitor is picked. - pub fn prepare_monitors() { - let checked: AliasingChecked = get_checked(); - let _ = checked; - unsafe { - OBJECT = 0usize; - OFFSET = 0usize; - STATE = MonitorState::UNINIT; - STACK_TAGS = [NEXT_TAG; STACK_DEPTH]; - STACK_PERMS = [Permission::UNIQUE; STACK_DEPTH]; - STACK_TOP = 0usize; - } - } - - /// Initialize local when track local is true, picking a monitor, - /// and setting its object and offset to within pointer. - pub(super) unsafe fn track_local(tag: u8, pointer: *const U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - // Decide whether to initialize the stacks - // for location:location+size_of(U). - // Offset has already been picked earlier. - unsafe { - if demonic_nondet() && STATE == MonitorState::UNINIT { - STATE = MonitorState::INIT; - OBJECT = pointer_object(pointer); - assume(OFFSET < std::mem::size_of::()); - STACK_TAGS[STACK_TOP] = tag; - STACK_PERMS[STACK_TOP] = Permission::UNIQUE; - STACK_TOP += 1; - } - } - } - - /// Push a tag with a permission perm at pointer - pub(super) fn push(tag: u8, perm: u8, pointer: *const U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - // Decide whether to initialize the stacks - // for location:location+size_of(U). - // Offset has already been picked earlier. - unsafe { - use self::*; - if STATE == MonitorState::INIT && - OBJECT == pointer_object(pointer) && - OFFSET == pointer_offset(pointer) - { - STACK_TAGS[STACK_TOP] = tag; - STACK_PERMS[STACK_TOP] = perm; - STACK_TOP += 1; - } - } - } - - pub(super) fn stack_check(tag: u8, access: bool, address: *const U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - unsafe { - use self::*; - if STATE == MonitorState::INIT && - OFFSET == pointer_offset(address) && - OBJECT == pointer_object(address) { - let mut found = false; - let mut j = 0; - let mut new_top = 0; - assert!(STACK_TOP < STACK_DEPTH); - while j < STACK_DEPTH { - if j < STACK_TOP { - let id = STACK_TAGS[j]; - let kind = STACK_PERMS[j]; - if Permission::grants(access, kind) && id == tag { - new_top = j + 1; - found = true; - } - } - j += 1; - } - STACK_TOP = new_top; - assert!(found, "Stack violated."); - } - } - } - } - - #[rustc_diagnostic_item = "KaniInitializeSState"] - pub fn initialize() { - let checked: AliasingChecked = get_checked(); - let _ = checked; - self::monitors::prepare_monitors(); - } - - /// Push the permissions at the given location - pub fn push(tag: u8, perm: u8, address: *const U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - self::monitors::push(tag, perm, address) - } - - /// Initialize the local stored at reference if initialized is set to false, - /// and track it using a monitor when using demonic nondeterminism. - /// - /// Every function call in the source program stack-allocates - /// the local variables that it uses; references to these - /// variables are only valid after these variables are initialized (first written). - /// Therefore this function can be used by supplying an initialized flag - /// set to true after the first write, a track flag set to the value - /// of a query to a demonic nondeterminism oracle (when this feature is used) - /// and a reference to the stack location. - #[rustc_diagnostic_item = "KaniInitializeLocal"] - pub fn initialize_local(pointer: *const U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - unsafe { - let tag = NEXT_TAG; - TAGS.set(pointer, tag); - PERMS.set(pointer, Permission::UNIQUE); - NEXT_TAG += 1; - monitors::track_local(tag, pointer); - } - } - - #[rustc_diagnostic_item = "KaniStackCheckPtr"] - pub fn stack_check_ptr(pointer_value: *const *mut U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - unsafe { - let tag = TAGS.get(pointer_value); - let perm = PERMS.get(pointer_value); - let pointer = *pointer_value; - for i in 0..std::mem::size_of::() { - for access in [false, true] { - if Permission::grants(access, perm) { - self::monitors::stack_check(tag, access, pointer.byte_add(i)); - } - } - } - } - } - - #[rustc_diagnostic_item = "KaniStackCheckRef"] - pub fn stack_check_ref(pointer_value: *const &mut U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - stack_check_ptr(pointer_value as *const *mut U); + initialize_local(std::ptr::addr_of!($place)); } - - /// Update the stacked borrows state for the case created: &mut T = &mut (referent:T) - /// by associating the location of the created value, stored at pointer_to_created, - /// with a new tag, and pushing the new tag to the created reference, stored at - /// pointer_to_val. - #[rustc_diagnostic_item = "KaniNewMutRefFromLocal"] - pub fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - unsafe { - // Then associate the lvalue and push it - TAGS.set(pointer_to_created, NEXT_TAG); - push(NEXT_TAG, Permission::SHAREDRW, pointer_to_val); - NEXT_TAG += 1; - } - } - - /// Update the stacked borrows state for the case created = (reference: &mut T) as *mut T, - /// associating the location of the created value, stored at pointer_to_created, with a new - /// tag, running a stack check on the tag associated with the reference, accessed by - /// pointer_to_ref, and pushing the tag to the original location. - #[rustc_diagnostic_item = "KaniNewMutRawFromRef"] - pub fn new_mut_raw_from_ref(pointer_to_created: *const *mut T, pointer_to_ref: *const &mut T) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - unsafe { - // Then associate the lvalue and push it - TAGS.set(pointer_to_created, NEXT_TAG); - push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); - NEXT_TAG += 1; - } - } - - /// Update the stacked borrows state for the case created = (reference: &mut T) as *mut T, - /// associating the location of the created value, stored at pointer_to_created, with a new - /// tag, running a stack check on the tag associated with the reference, accessed by - /// pointer_to_ref, and pushing the tag to the original location. - #[rustc_diagnostic_item = "KaniNewMutRefFromRaw"] - pub fn new_mut_ref_from_raw(pointer_to_created: *const &mut T, pointer_to_ref: *const *mut T) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - unsafe { - // Then associate the lvalue and push it - TAGS.set(pointer_to_created, NEXT_TAG); - push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); - NEXT_TAG += 1; - } - } -} - - - -#[cfg(any(kani))] -fn demonic_nondet() -> bool { - let checked: AliasingChecked = get_checked(); - let _ = checked; - kani::any::() } -#[cfg(not(kani))] -fn demonic_nondet() -> bool { - let checked: AliasingChecked = get_checked(); - let _ = checked; - true -} - -#[cfg(not(kani))] -static mut LOCAL_COUNT: u32 = 0; - -#[cfg(not(kani))] -fn initialize_local(local: *const T) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - unsafe { - println!("Initializing local {:?}", LOCAL_COUNT); +macro_rules! new_mut_ref_from_value { + ($reference:ident) => { + #[cfg(not(kani))] + new_mut_ref_from_value(std::ptr::addr_of!($reference), + $reference); } - sstate::initialize_local(local); - sstate::debug(); - unsafe { LOCAL_COUNT += 1 }; } -#[cfg(not(kani))] -fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - println!("new mut ref from value"); - sstate::new_mut_ref_from_value(pointer_to_created, pointer_to_val); - sstate::debug(); -} - -#[cfg(not(kani))] -pub fn new_mut_raw_from_ref(pointer_to_created: *const *mut T, pointer_to_ref: *const &mut T) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - println!("new mut raw from ref"); - sstate::new_mut_raw_from_ref(pointer_to_created, pointer_to_ref); - sstate::debug(); +macro_rules! stack_check_ref { + ($reference:ident) => { + #[cfg(not(kani))] + stack_check_ref(std::ptr::addr_of!($reference)); + } } -#[cfg(not(kani))] -pub fn new_mut_ref_from_raw(pointer_to_created: *const &mut T, pointer_to_ref: *const *mut T) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - println!("new mut ref from raw"); - sstate::new_mut_ref_from_raw(pointer_to_created, pointer_to_ref); - sstate::debug(); +macro_rules! new_mut_raw_from_ref { + ($pointer: ident, $reference: ident) => { + #[cfg(not(kani))] + new_mut_raw_from_ref(std::ptr::addr_of!($pointer), + std::ptr::addr_of!($reference)); + } } -pub fn stack_check_ptr(pointer_value: *const *mut U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - println!("checking ptr on stack"); - sstate::stack_check_ptr(pointer_value); - sstate::debug(); +macro_rules! new_mut_ref_from_raw { + ($pointer: ident, $reference: ident) => { + #[cfg(not(kani))] + new_mut_ref_from_raw(std::ptr::addr_of!($pointer), + std::ptr::addr_of!($reference)); + } } -pub fn stack_check_ref(pointer_value: *const &mut U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - println!("checking ref on stack"); - sstate::stack_check_ref(pointer_value); - sstate::debug(); +macro_rules! stack_check_ptr { + ($pointer: ident) => { + #[cfg(not(kani))] + stack_check_ptr(std::ptr::addr_of!($pointer)); + } } #[cfg_attr(any(kani), kani::proof)] @@ -473,40 +63,28 @@ fn main() { let raw_pointer: *mut i32; let ref_from_raw_1: &mut i32; let ref_from_raw_2: &mut i32; + initialize!(); local = 0; - #[cfg(not(kani))] - initialize_local(std::ptr::addr_of!(local)); + initialize_local!(local); temp_ref = &mut local; - #[cfg(not(kani))] - initialize_local(std::ptr::addr_of!(temp_ref)); + initialize_local!(temp_ref); raw_pointer = temp_ref as *mut i32; - #[cfg(not(kani))] - initialize_local(std::ptr::addr_of!(raw_pointer)); - #[cfg(not(kani))] - new_mut_ref_from_value(std::ptr::addr_of!(temp_ref), - temp_ref); - #[cfg(not(kani))] - stack_check_ref(std::ptr::addr_of!(temp_ref)); - #[cfg(not(kani))] - new_mut_raw_from_ref(std::ptr::addr_of!(raw_pointer), std::ptr::addr_of!(temp_ref)); + initialize_local!(raw_pointer); + new_mut_ref_from_value!(temp_ref); + stack_check_ref!(temp_ref); + new_mut_raw_from_ref!(raw_pointer, temp_ref); unsafe { ref_from_raw_1 = &mut *raw_pointer; - #[cfg(not(kani))] - new_mut_ref_from_raw(std::ptr::addr_of!(ref_from_raw_1), std::ptr::addr_of!(raw_pointer)); + new_mut_ref_from_raw!(ref_from_raw_1, raw_pointer); *ref_from_raw_1 = 0; - #[cfg(not(kani))] - stack_check_ref(std::ptr::addr_of!(ref_from_raw_1)); + stack_check_ref!(ref_from_raw_1); ref_from_raw_2 = &mut *raw_pointer; - #[cfg(not(kani))] - stack_check_ptr(std::ptr::addr_of!(raw_pointer)); - #[cfg(not(kani))] - new_mut_ref_from_raw(std::ptr::addr_of!(ref_from_raw_2), std::ptr::addr_of!(raw_pointer)); + stack_check_ptr!(raw_pointer); + new_mut_ref_from_raw!(ref_from_raw_2, raw_pointer); *ref_from_raw_2 = 1; - #[cfg(not(kani))] - stack_check_ref(std::ptr::addr_of!(ref_from_raw_2)); + stack_check_ref!(ref_from_raw_2); *ref_from_raw_1 = 2; - #[cfg(not(kani))] - stack_check_ref(std::ptr::addr_of!(ref_from_raw_1)); + stack_check_ref!(ref_from_raw_1); } } diff --git a/tests/expected/aliasing/stackfns_ignore.rs b/tests/expected/aliasing/stackfns_ignore.rs new file mode 100644 index 000000000000..c7b45aa12600 --- /dev/null +++ b/tests/expected/aliasing/stackfns_ignore.rs @@ -0,0 +1,469 @@ +// kani-flags: -Zghost-state -Zaliasing +#![allow(internal_features)] +#![feature(rustc_attrs)] +#![feature(vec_into_raw_parts)] +// Copyright Jacob Salzberg +// SPDX-License-Identifier: Apache-2.0 + +// Basic test from the stacked borrows paper +#![allow(non_snake_case)] +#![feature(const_trait_impl)] +#![cfg_attr(not(kani), feature(register_tool))] +#![cfg_attr(not(kani), register_tool(kani))] + +#[derive(Copy, Clone)] +#[rustc_diagnostic_item = "KaniAliasingChecked"] +struct AliasingChecked { amount: usize } + +const STACK_DEPTH: usize = 15; +type PointerTag = u8; + +#[cfg(any(kani))] +extern crate kani; +#[cfg(any(kani))] +use kani::shadow::ShadowMem; +#[cfg(not(kani))] +use std::collections::HashMap; +#[cfg(not(kani))] +#[derive(Debug)] +struct ShadowMem{ + mem: Option>, + default: T +} +#[cfg(not(kani))] +static mut FOUND_POINTERS: Option> = None; +#[cfg(not(kani))] +static mut FOUND_POINTERS_TOP: usize = 0; + +#[cfg(not(kani))] +impl ShadowMem where T: Copy { + #[inline(always)] + const fn new(v: T) -> Self { + ShadowMem { mem: None, default: v } + } + + fn set(&mut self, pointer: *const U, value: T) { + unsafe { + let map = FOUND_POINTERS.get_or_insert(HashMap::new()); + let e = map.entry(pointer as *const u8) + .or_insert_with(|| { let top = FOUND_POINTERS_TOP; FOUND_POINTERS_TOP += 1; top } ); + let mem = self.mem.get_or_insert(HashMap::new()); + mem.insert(*e, value); + } + } + + fn get(&self, pointer: *const U) -> T { + unsafe { + let map = FOUND_POINTERS.get_or_insert(HashMap::new()); + let pointer = pointer as *const u8; + *(self.mem.as_ref().unwrap().get(map.get(&pointer).unwrap()).unwrap_or(&self.default)) + } + } +} + +#[inline(never)] +pub fn get_checked() -> AliasingChecked { + static mut CHECKED: AliasingChecked = AliasingChecked { amount: 0 }; + unsafe { CHECKED.amount = CHECKED.amount.wrapping_add(1); CHECKED } +} + +#[cfg(any(kani))] +pub fn pointer_object(pointer: *const U) -> usize { + let checked = get_checked(); + let _ = checked; + kani::mem::pointer_object(pointer) +} + +#[cfg(not(kani))] +pub fn pointer_object(pointer: *const U) -> usize { + pointer as usize +} + +#[cfg(any(kani))] +pub fn pointer_offset(pointer: *const U) -> usize { + let checked = get_checked(); + let _ = checked; + kani::mem::pointer_offset(pointer) +} + +#[cfg(not(kani))] +pub fn pointer_offset(_pointer: *const U) -> usize { + 0 +} + +#[cfg(any(kani))] +pub fn assume(b: bool) { + let checked = get_checked(); + let _ = checked; + kani::assume(b); +} + +#[cfg(not(kani))] +pub fn assume(b: bool) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + assert!(b); +} + +/// The stacked borrows state. +pub mod sstate { + use super::*; + /// Associate every pointer object with a tag + static mut TAGS: ShadowMem = ShadowMem::new(0); + /// Next pointer id: the next pointer id in sequence + static mut NEXT_TAG: PointerTag = 0; + + #[non_exhaustive] + struct Access; + #[allow(unused)] + impl Access { + pub(self) const READ: bool = false; + pub(self) const WRITE: bool = true; + } + + #[non_exhaustive] + struct Permission; + impl Permission { + pub(self) const UNIQUE: u8 = 0; + pub(self) const SHAREDRW: u8 = 1; + pub(self) const SHAREDRO: u8 = 2; + pub(self) const DISABLED: u8 = 3; + + pub(self) fn grants(access: bool, tag: u8) -> bool { + let checked: AliasingChecked = get_checked(); + let _ = checked; + tag != Self::DISABLED && (access != Access::WRITE || tag != Self::SHAREDRO) + } + } + + /// Associate every pointer object with a permission + static mut PERMS: ShadowMem = ShadowMem::new(0); + + #[cfg(not(kani))] + pub fn debug() { + unsafe { + println!("tags & perms at this point: tags: {:?} perms: {:?}", sstate::TAGS, sstate::PERMS); + self::monitors::debug_monitors(); + } + } + + #[cfg(any(kani))] + #[inline(always)] + pub fn debug() { + let checked = get_checked(); + let _ = checked; + return; + } + + pub(super) mod monitors { + static mut STATE: bool = false; + static mut OBJECT: usize = 0; + static mut OFFSET: usize = 0; + static mut STACK_TAGS: [u8; STACK_DEPTH] = [0; STACK_DEPTH]; + static mut STACK_PERMS: [u8; STACK_DEPTH] = [0; STACK_DEPTH]; + static mut STACK_TOP: usize = 0; + + #[cfg(not(kani))] + pub fn debug_monitors() { + unsafe { + println!("monitors at this point state: {:?} object: {:?} offset: {:?} stags: {:?} sperms: {:?} stop: {:?}", + STATE, OBJECT, OFFSET, STACK_TAGS, STACK_PERMS, STACK_TOP); + } + } + + #[non_exhaustive] + struct MonitorState; + impl MonitorState { + pub(self) const UNINIT: bool = false; + pub(self) const INIT: bool = true; + } + + use super::*; + + /// Monitors: + /// If there are K bytes in the address space, + /// every stacked borrows instrumentation has + /// between 0 and K monitors. + /// These monitors track a single byte of the program, + /// associating it with a stack of pointer values + /// (represented by tags). + /// Whenever a pointer borrows an object containing + /// the byte, its tag is pushed to the stack; + /// when a read or write is performed through this pointer, + /// writes from pointers above its location on the stack + /// are disabled. + /// This function prepares N monitors, + /// writes them to global heap memory, then + /// stores them in pointers. + /// An N+1th monitor is allocated as a "garbage" + /// area to be used when no monitor is picked. + pub fn prepare_monitors() { + let checked: AliasingChecked = get_checked(); + let _ = checked; + unsafe { + OBJECT = 0usize; + OFFSET = 0usize; + STATE = MonitorState::UNINIT; + STACK_TAGS = [NEXT_TAG; STACK_DEPTH]; + STACK_PERMS = [Permission::UNIQUE; STACK_DEPTH]; + STACK_TOP = 0usize; + } + } + + /// Initialize local when track local is true, picking a monitor, + /// and setting its object and offset to within pointer. + pub(super) unsafe fn track_local(tag: u8, pointer: *const U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + // Decide whether to initialize the stacks + // for location:location+size_of(U). + // Offset has already been picked earlier. + unsafe { + if demonic_nondet() && STATE == MonitorState::UNINIT { + STATE = MonitorState::INIT; + OBJECT = pointer_object(pointer); + assume(OFFSET < std::mem::size_of::()); + STACK_TAGS[STACK_TOP] = tag; + STACK_PERMS[STACK_TOP] = Permission::UNIQUE; + STACK_TOP += 1; + } + } + } + + /// Push a tag with a permission perm at pointer + pub(super) fn push(tag: u8, perm: u8, pointer: *const U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + // Decide whether to initialize the stacks + // for location:location+size_of(U). + // Offset has already been picked earlier. + unsafe { + use self::*; + if STATE == MonitorState::INIT && + OBJECT == pointer_object(pointer) && + OFFSET == pointer_offset(pointer) + { + STACK_TAGS[STACK_TOP] = tag; + STACK_PERMS[STACK_TOP] = perm; + STACK_TOP += 1; + } + } + } + + pub(super) fn stack_check(tag: u8, access: bool, address: *const U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + unsafe { + use self::*; + if STATE == MonitorState::INIT && + OFFSET == pointer_offset(address) && + OBJECT == pointer_object(address) { + let mut found = false; + let mut j = 0; + let mut new_top = 0; + assert!(STACK_TOP < STACK_DEPTH); + while j < STACK_DEPTH { + if j < STACK_TOP { + let id = STACK_TAGS[j]; + let kind = STACK_PERMS[j]; + if Permission::grants(access, kind) && id == tag { + new_top = j + 1; + found = true; + } + } + j += 1; + } + STACK_TOP = new_top; + assert!(found, "Stack violated."); + } + } + } + } + + #[rustc_diagnostic_item = "KaniInitializeSState"] + pub fn initialize() { + let checked: AliasingChecked = get_checked(); + let _ = checked; + self::monitors::prepare_monitors(); + } + + /// Push the permissions at the given location + pub fn push(tag: u8, perm: u8, address: *const U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + self::monitors::push(tag, perm, address) + } + + /// Initialize the local stored at reference if initialized is set to false, + /// and track it using a monitor when using demonic nondeterminism. + /// + /// Every function call in the source program stack-allocates + /// the local variables that it uses; references to these + /// variables are only valid after these variables are initialized (first written). + /// Therefore this function can be used by supplying an initialized flag + /// set to true after the first write, a track flag set to the value + /// of a query to a demonic nondeterminism oracle (when this feature is used) + /// and a reference to the stack location. + #[rustc_diagnostic_item = "KaniInitializeLocal"] + pub fn initialize_local(pointer: *const U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + unsafe { + let tag = NEXT_TAG; + TAGS.set(pointer, tag); + PERMS.set(pointer, Permission::UNIQUE); + NEXT_TAG += 1; + monitors::track_local(tag, pointer); + } + } + + #[rustc_diagnostic_item = "KaniStackCheckPtr"] + pub fn stack_check_ptr(pointer_value: *const *mut U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + unsafe { + let tag = TAGS.get(pointer_value); + let perm = PERMS.get(pointer_value); + let pointer = *pointer_value; + for i in 0..std::mem::size_of::() { + for access in [false, true] { + if Permission::grants(access, perm) { + self::monitors::stack_check(tag, access, pointer.byte_add(i)); + } + } + } + } + } + + #[rustc_diagnostic_item = "KaniStackCheckRef"] + pub fn stack_check_ref(pointer_value: *const &mut U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + stack_check_ptr(pointer_value as *const *mut U); + } + + /// Update the stacked borrows state for the case created: &mut T = &mut (referent:T) + /// by associating the location of the created value, stored at pointer_to_created, + /// with a new tag, and pushing the new tag to the created reference, stored at + /// pointer_to_val. + #[rustc_diagnostic_item = "KaniNewMutRefFromLocal"] + pub fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + unsafe { + // Then associate the lvalue and push it + TAGS.set(pointer_to_created, NEXT_TAG); + push(NEXT_TAG, Permission::SHAREDRW, pointer_to_val); + NEXT_TAG += 1; + } + } + + /// Update the stacked borrows state for the case created = (reference: &mut T) as *mut T, + /// associating the location of the created value, stored at pointer_to_created, with a new + /// tag, running a stack check on the tag associated with the reference, accessed by + /// pointer_to_ref, and pushing the tag to the original location. + #[rustc_diagnostic_item = "KaniNewMutRawFromRef"] + pub fn new_mut_raw_from_ref(pointer_to_created: *const *mut T, pointer_to_ref: *const &mut T) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + unsafe { + // Then associate the lvalue and push it + TAGS.set(pointer_to_created, NEXT_TAG); + push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); + NEXT_TAG += 1; + } + } + + /// Update the stacked borrows state for the case created = (reference: &mut T) as *mut T, + /// associating the location of the created value, stored at pointer_to_created, with a new + /// tag, running a stack check on the tag associated with the reference, accessed by + /// pointer_to_ref, and pushing the tag to the original location. + #[rustc_diagnostic_item = "KaniNewMutRefFromRaw"] + pub fn new_mut_ref_from_raw(pointer_to_created: *const &mut T, pointer_to_ref: *const *mut T) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + unsafe { + // Then associate the lvalue and push it + TAGS.set(pointer_to_created, NEXT_TAG); + push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); + NEXT_TAG += 1; + } + } +} + +#[cfg(any(kani))] +pub fn demonic_nondet() -> bool { + let checked: AliasingChecked = get_checked(); + let _ = checked; + kani::any::() +} + +#[cfg(not(kani))] +pub fn demonic_nondet() -> bool { + let checked: AliasingChecked = get_checked(); + let _ = checked; + true +} + +#[cfg(not(kani))] +static mut LOCAL_COUNT: u32 = 0; + +#[cfg(not(kani))] +pub fn initialize_local(local: *const T) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + unsafe { + println!("Initializing local {:?}", LOCAL_COUNT); + } + sstate::initialize_local(local); + sstate::debug(); + unsafe { LOCAL_COUNT += 1 }; +} + +#[cfg(not(kani))] +pub fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + println!("new mut ref from value"); + sstate::new_mut_ref_from_value(pointer_to_created, pointer_to_val); + sstate::debug(); +} + +#[cfg(not(kani))] +pub fn new_mut_raw_from_ref(pointer_to_created: *const *mut T, pointer_to_ref: *const &mut T) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + println!("new mut raw from ref"); + sstate::new_mut_raw_from_ref(pointer_to_created, pointer_to_ref); + sstate::debug(); +} + +#[cfg(not(kani))] +pub fn new_mut_ref_from_raw(pointer_to_created: *const &mut T, pointer_to_ref: *const *mut T) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + println!("new mut ref from raw"); + sstate::new_mut_ref_from_raw(pointer_to_created, pointer_to_ref); + sstate::debug(); +} + +pub fn stack_check_ptr(pointer_value: *const *mut U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + println!("checking ptr on stack"); + sstate::stack_check_ptr(pointer_value); + sstate::debug(); +} + +pub fn stack_check_ref(pointer_value: *const &mut U) { + let checked: AliasingChecked = get_checked(); + let _ = checked; + println!("checking ref on stack"); + sstate::stack_check_ref(pointer_value); + sstate::debug(); +} + +#[cfg_attr(any(kani), kani::proof)] +fn main() { + assert!(true) +} From dca76d962ea5943b4a6a4e6ae86d87eb3e80cac0 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Tue, 13 Aug 2024 13:08:03 -0400 Subject: [PATCH 13/72] Instrument duplicate write in both the rust and kani setting. --- .../transform/check_aliasing/mod.rs | 92 ++++++++++++------- .../aliasing/duplicate_write.expected | 2 - tests/expected/aliasing/duplicate_write.rs | 61 ++---------- .../{stackfns_ignore.rs => stackfns.txt} | 73 +++++++++++---- 4 files changed, 124 insertions(+), 104 deletions(-) delete mode 100644 tests/expected/aliasing/duplicate_write.expected rename tests/expected/aliasing/{stackfns_ignore.rs => stackfns.txt} (91%) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 2137b2389ab4..a73efd94dceb 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -236,44 +236,57 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Ok(()) } - fn instrument_new_stack_reference(&mut self, idx: &MutatorIndex, lvalue: Local, referent: Local) -> Result<(), Error> { + fn instrument_new_stack_reference(&mut self, idx: &MutatorIndex, lvalue: Local, rvalue: Local) -> Result<(), Error> { // Initialize the constants - let ty = self.body.local(referent).ty; + let ty = self.body.local(rvalue).ty; let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); - let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutRef", &[GenericArgKind::Type(ty)]))?; - self.body.call(instance, vec![*lvalue_ref, lvalue], self.body.unit); + let rvalue_ref = self.meta_stack.get(&rvalue).unwrap(); + let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutRefFromValue", &[GenericArgKind::Type(ty)]))?; + self.body.call(instance, vec![*lvalue_ref, *rvalue_ref], self.body.unit); self.body.split(idx); Ok(()) } + fn instrument_stack_check_ref(&mut self, idx: &MutatorIndex, place: Local) -> Result<(), Error> { + // Initialize the constants + let ty = self.body.local(place).ty; + let place_ref = self.meta_stack.get(&place).unwrap(); + let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniStackCheckRef", &[GenericArgKind::Type(ty)]))?; + self.body.call(instance, vec![*place_ref], self.body.unit); + self.body.split(idx); + Ok(()) + } - fn instrument_new_raw_from_ref(&mut self, idx: &MutatorIndex, lvalue: Local, referent: Local) -> Result<(), Error> { + fn instrument_stack_check_ptr(&mut self, idx: &MutatorIndex, place: Local) -> Result<(), Error> { // Initialize the constants - if let TyKind::RigidTy(RigidTy::Ref(_, ty, _)) = - self.body.local(referent).ty.kind() { - let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); - let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutRaw", &[GenericArgKind::Type(ty)]))?; - self.body.call(instance, vec![*lvalue_ref, lvalue], self.body.unit); - self.body.split(idx); - Ok(()) - } else { - panic!("At this time only dereferences of refs are handled here."); - } + let ty = self.body.local(place).ty; + let place_ref = self.meta_stack.get(&place).unwrap(); + let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniStackCheckPtr", &[GenericArgKind::Type(ty)]))?; + self.body.call(instance, vec![*place_ref], self.body.unit); + self.body.split(idx); + Ok(()) } - fn instrument_write_through_pointer(&mut self, idx: &MutatorIndex, lvalue: Local) -> Result<(), Error> { + fn instrument_new_mut_ref_from_raw(&mut self, idx: &MutatorIndex, created: Local, raw: Local) -> Result<(), Error> { // Initialize the constants - if let TyKind::RigidTy(RigidTy::Ref(_, ty, _)) | TyKind::RigidTy(RigidTy::RawPtr(ty, _)) = self.body.local(lvalue).ty.kind() { - let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); - let ty = Ty::from_rigid_kind(RigidTy::RawPtr(ty, Mutability::Not)); - let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniWriteThroughPointer", &[GenericArgKind::Type(ty)]))?; - /* Limitation: calls use_2 on a reference or pointer, when use_2's input type is a pointer */ - self.body.call(instance, vec![*lvalue_ref], self.body.unit); - self.body.split(idx); - Ok(()) - } else { - panic!("At this time only dereferences of refs are handled here."); - } + let ty = self.body.local(created).ty; + let created_ref = self.meta_stack.get(&created).unwrap(); + let reference_ref = self.meta_stack.get(&raw).unwrap(); + let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutRefFromRaw", &[GenericArgKind::Type(ty)]))?; + self.body.call(instance, vec![*created_ref, *reference_ref], self.body.unit); + self.body.split(idx); + Ok(()) + } + + fn instrument_new_mut_raw_from_ref(&mut self, idx: &MutatorIndex, created: Local, reference: Local) -> Result<(), Error> { + // Initialize the constants + let ty = self.body.local(created).ty; + let created_ref = self.meta_stack.get(&created).unwrap(); + let reference_ref = self.meta_stack.get(&reference).unwrap(); + let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutRawFromRef", &[GenericArgKind::Type(ty)]))?; + self.body.call(instance, vec![*created_ref, *reference_ref], self.body.unit); + self.body.split(idx); + Ok(()) } fn instrument_index(&mut self, _values: &Vec, idx: &MutatorIndex) -> Result<(), Error> { @@ -288,14 +301,20 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Rvalue::Ref(_, BorrowKind::Mut { .. }, from) => { match from.projection[..] { [] => { - // Direct reference to the stack local + // Direct reference to stack local // x = &y self.instrument_new_stack_reference(idx, to.local, from.local)?; Ok(()) }, [ProjectionElem::Deref] => { // Reborrow - // x = &*y + // x : &mut T = &*(y : *mut T) + let from = from.local; // Copy to avoid borrow + let to = to.local; // Copy to avoid borrow + if self.body.local(to).ty.kind().is_raw_ptr() { + self.instrument_stack_check_ref(idx, from)?; + self.instrument_new_mut_ref_from_raw(idx, to, from)?; + } Ok(()) }, _ => { @@ -312,7 +331,13 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Ok(()) }, [ProjectionElem::Deref] => { - self.instrument_new_raw_from_ref(idx, to.local, from.local)?; + // x = &raw mut *(y: &mut T) + let from = from.local; // Copy to avoid borrow + let to = to.local; // Copy to avoid borrow + if self.body.local(to).ty.kind().is_ref() { + self.instrument_stack_check_ref(idx, from)?; + self.instrument_new_mut_raw_from_ref(idx, to, from)?; + } Ok(()) }, _ => { @@ -328,7 +353,12 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { } [ProjectionElem::Deref] => { // *x = rvalue - self.instrument_write_through_pointer(idx, to.local)?; + let to = to.local; + if self.body.local(to).ty.kind().is_ref() { + self.instrument_stack_check_ref(idx, to); + } else if self.body.local(to).ty.kind().is_raw_ptr() { + self.instrument_stack_check_ptr(idx, to); + } Ok(()) } _ => { diff --git a/tests/expected/aliasing/duplicate_write.expected b/tests/expected/aliasing/duplicate_write.expected deleted file mode 100644 index 40b69fbc424a..000000000000 --- a/tests/expected/aliasing/duplicate_write.expected +++ /dev/null @@ -1,2 +0,0 @@ -FAILURE\ -assertion failed: Stack violated diff --git a/tests/expected/aliasing/duplicate_write.rs b/tests/expected/aliasing/duplicate_write.rs index e7ca46499256..c10e27667bb0 100644 --- a/tests/expected/aliasing/duplicate_write.rs +++ b/tests/expected/aliasing/duplicate_write.rs @@ -1,60 +1,11 @@ // kani-flags: -Zghost-state -Zaliasing -#![feature(register_tool)] +#![cfg_attr(not(kani), feature(register_tool))] +#![cfg_attr(not(kani), register_tool(kani))] #![feature(rustc_attrs)] -mod stackfns_ignore; -use stackfns_ignore::*; +#![allow(internal_features)] +#![feature(vec_into_raw_parts)] -macro_rules! initialize { - () => { - #[cfg(not(kani))] - sstate::initialize(); - } -} - -macro_rules! initialize_local { - ($place:ident) => { - #[cfg(not(kani))] - initialize_local(std::ptr::addr_of!($place)); - } -} - -macro_rules! new_mut_ref_from_value { - ($reference:ident) => { - #[cfg(not(kani))] - new_mut_ref_from_value(std::ptr::addr_of!($reference), - $reference); - } -} - -macro_rules! stack_check_ref { - ($reference:ident) => { - #[cfg(not(kani))] - stack_check_ref(std::ptr::addr_of!($reference)); - } -} - -macro_rules! new_mut_raw_from_ref { - ($pointer: ident, $reference: ident) => { - #[cfg(not(kani))] - new_mut_raw_from_ref(std::ptr::addr_of!($pointer), - std::ptr::addr_of!($reference)); - } -} - -macro_rules! new_mut_ref_from_raw { - ($pointer: ident, $reference: ident) => { - #[cfg(not(kani))] - new_mut_ref_from_raw(std::ptr::addr_of!($pointer), - std::ptr::addr_of!($reference)); - } -} - -macro_rules! stack_check_ptr { - ($pointer: ident) => { - #[cfg(not(kani))] - stack_check_ptr(std::ptr::addr_of!($pointer)); - } -} +include!{"./stackfns.txt"} #[cfg_attr(any(kani), kani::proof)] fn main() { @@ -69,9 +20,9 @@ fn main() { initialize_local!(local); temp_ref = &mut local; initialize_local!(temp_ref); + new_mut_ref_from_value!(temp_ref); raw_pointer = temp_ref as *mut i32; initialize_local!(raw_pointer); - new_mut_ref_from_value!(temp_ref); stack_check_ref!(temp_ref); new_mut_raw_from_ref!(raw_pointer, temp_ref); unsafe { diff --git a/tests/expected/aliasing/stackfns_ignore.rs b/tests/expected/aliasing/stackfns.txt similarity index 91% rename from tests/expected/aliasing/stackfns_ignore.rs rename to tests/expected/aliasing/stackfns.txt index c7b45aa12600..042b6d44389b 100644 --- a/tests/expected/aliasing/stackfns_ignore.rs +++ b/tests/expected/aliasing/stackfns.txt @@ -1,27 +1,21 @@ // kani-flags: -Zghost-state -Zaliasing -#![allow(internal_features)] -#![feature(rustc_attrs)] -#![feature(vec_into_raw_parts)] // Copyright Jacob Salzberg // SPDX-License-Identifier: Apache-2.0 // Basic test from the stacked borrows paper -#![allow(non_snake_case)] -#![feature(const_trait_impl)] -#![cfg_attr(not(kani), feature(register_tool))] -#![cfg_attr(not(kani), register_tool(kani))] - -#[derive(Copy, Clone)] #[rustc_diagnostic_item = "KaniAliasingChecked"] -struct AliasingChecked { amount: usize } +#[derive(Copy, Clone, Debug)] +pub struct AliasingChecked { amount: usize } const STACK_DEPTH: usize = 15; type PointerTag = u8; -#[cfg(any(kani))] +#[cfg(kani)] extern crate kani; -#[cfg(any(kani))] + +#[cfg(kani)] use kani::shadow::ShadowMem; + #[cfg(not(kani))] use std::collections::HashMap; #[cfg(not(kani))] @@ -346,7 +340,7 @@ pub mod sstate { /// by associating the location of the created value, stored at pointer_to_created, /// with a new tag, and pushing the new tag to the created reference, stored at /// pointer_to_val. - #[rustc_diagnostic_item = "KaniNewMutRefFromLocal"] + #[rustc_diagnostic_item = "KaniNewMutRefFromValue"] pub fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { let checked: AliasingChecked = get_checked(); let _ = checked; @@ -463,7 +457,54 @@ pub fn stack_check_ref(pointer_value: *const &mut U) { sstate::debug(); } -#[cfg_attr(any(kani), kani::proof)] -fn main() { - assert!(true) +macro_rules! initialize { + () => { + #[cfg(not(kani))] + sstate::initialize(); + } +} + +macro_rules! initialize_local { + ($place:ident) => { + #[cfg(not(kani))] + initialize_local(std::ptr::addr_of!($place)); + } +} + +macro_rules! new_mut_ref_from_value { + ($reference:ident) => { + #[cfg(not(kani))] + new_mut_ref_from_value(std::ptr::addr_of!($reference), + $reference); + } +} + +macro_rules! stack_check_ref { + ($reference:ident) => { + #[cfg(not(kani))] + stack_check_ref(std::ptr::addr_of!($reference)); + } +} + +macro_rules! new_mut_raw_from_ref { + ($pointer: ident, $reference: ident) => { + #[cfg(not(kani))] + new_mut_raw_from_ref(std::ptr::addr_of!($pointer), + std::ptr::addr_of!($reference)); + } +} + +macro_rules! new_mut_ref_from_raw { + ($pointer: ident, $reference: ident) => { + #[cfg(not(kani))] + new_mut_ref_from_raw(std::ptr::addr_of!($pointer), + std::ptr::addr_of!($reference)); + } +} + +macro_rules! stack_check_ptr { + ($pointer: ident) => { + #[cfg(not(kani))] + stack_check_ptr(std::ptr::addr_of!($pointer)); + } } From 820584f19290037f9d6d0946c904230c5a99a938 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Tue, 13 Aug 2024 15:37:23 -0400 Subject: [PATCH 14/72] Make test pass --- .../transform/check_aliasing/mod.rs | 127 ++++++++++-------- .../aliasing/duplicate_write.expected | 1 + 2 files changed, 75 insertions(+), 53 deletions(-) create mode 100644 tests/expected/aliasing/duplicate_write.expected diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index a73efd94dceb..0cf563a2e7a8 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -217,6 +217,16 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { body.insert_statement(Statement { kind, span }); } + /// Initialize the monitors + fn instrument_initialize( + &mut self, + ) -> Result<(), Error> { + let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniInitializeSState", &[]))?; + let body = &mut self.body; + body.call(instance, [].to_vec(), body.unit); + Ok(()) + } + /// For some local, say let x: T; /// instrument it with the functions that initialize the stack: /// let ptr_x: *const T = &raw const x; @@ -247,9 +257,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Ok(()) } - fn instrument_stack_check_ref(&mut self, idx: &MutatorIndex, place: Local) -> Result<(), Error> { + fn instrument_stack_check_ref(&mut self, idx: &MutatorIndex, place: Local, ty: Ty) -> Result<(), Error> { // Initialize the constants - let ty = self.body.local(place).ty; let place_ref = self.meta_stack.get(&place).unwrap(); let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniStackCheckRef", &[GenericArgKind::Type(ty)]))?; self.body.call(instance, vec![*place_ref], self.body.unit); @@ -257,9 +266,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Ok(()) } - fn instrument_stack_check_ptr(&mut self, idx: &MutatorIndex, place: Local) -> Result<(), Error> { + fn instrument_stack_check_ptr(&mut self, idx: &MutatorIndex, place: Local, ty: Ty) -> Result<(), Error> { // Initialize the constants - let ty = self.body.local(place).ty; let place_ref = self.meta_stack.get(&place).unwrap(); let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniStackCheckPtr", &[GenericArgKind::Type(ty)]))?; self.body.call(instance, vec![*place_ref], self.body.unit); @@ -294,70 +302,82 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Instruction::Stmt(Statement { kind, ..} ) => { match kind { StatementKind::Assign(to, rvalue) => { - match to.projection[..] { - [] => { - // Assignment directly to local - match rvalue { - Rvalue::Ref(_, BorrowKind::Mut { .. }, from) => { - match from.projection[..] { - [] => { - // Direct reference to stack local - // x = &y - self.instrument_new_stack_reference(idx, to.local, from.local)?; - Ok(()) + let to = to.clone(); + match rvalue { + Rvalue::Ref(_, BorrowKind::Mut { .. }, from) => { + match from.projection[..] { + [] => { + // Direct reference to stack local + // x = &y + self.instrument_new_stack_reference(idx, to.local, from.local)?; + }, + [ProjectionElem::Deref] => { + // Reborrow + // x : &mut T = &*(y : *mut T) + let from = from.local; // Copy to avoid borrow + let to = to.local; // Copy to avoid borrow + match self.body.local(to).ty.kind() { + TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { + eprintln!("Reborrow from reference not yet handled"); }, - [ProjectionElem::Deref] => { - // Reborrow - // x : &mut T = &*(y : *mut T) - let from = from.local; // Copy to avoid borrow - let to = to.local; // Copy to avoid borrow - if self.body.local(to).ty.kind().is_raw_ptr() { - self.instrument_stack_check_ref(idx, from)?; - self.instrument_new_mut_ref_from_raw(idx, to, from)?; - } - Ok(()) + TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { + self.instrument_stack_check_ref(idx, from, ty)?; + self.instrument_new_mut_ref_from_raw(idx, to, from)?; }, - _ => { - eprintln!("Field projections not yet handled"); - Ok(()) - } + _ => {} } }, - Rvalue::AddressOf(Mutability::Mut, from) => { - match from.projection[..] { - [] => { - // x = &raw y - eprintln!("addr of not yet handled"); - Ok(()) - }, - [ProjectionElem::Deref] => { - // x = &raw mut *(y: &mut T) - let from = from.local; // Copy to avoid borrow - let to = to.local; // Copy to avoid borrow - if self.body.local(to).ty.kind().is_ref() { - self.instrument_stack_check_ref(idx, from)?; - self.instrument_new_mut_raw_from_ref(idx, to, from)?; - } - Ok(()) + _ => { + eprintln!("Field projections not yet handled"); + } + } + }, + Rvalue::AddressOf(Mutability::Mut, from) => { + match from.projection[..] { + [] => { + // x = &raw y + eprintln!("addr of not yet handled"); + }, + [ProjectionElem::Deref] => { + // x = &raw mut *(y: &mut T) + let from = from.local; // Copy to avoid borrow + let to = to.local; // Copy to avoid borrow + match self.body.local(to).ty.kind() { + TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { + self.instrument_stack_check_ref(idx, from, ty)?; + self.instrument_new_mut_raw_from_ref(idx, to, from)?; }, - _ => { - Ok(()) + TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { + eprintln!("Pointer to pointer casts not yet handled"); } + _ => {} } }, _ => { - eprintln!("Rvalue kind: {:?} not yet handled", rvalue); - Ok(()) } } + }, + _ => { + eprintln!("Rvalue kind: {:?} not yet handled", rvalue); + } + } + match to.projection[..] { + [] => { + // Assignment directly to local + Ok(()) } [ProjectionElem::Deref] => { // *x = rvalue let to = to.local; - if self.body.local(to).ty.kind().is_ref() { - self.instrument_stack_check_ref(idx, to); - } else if self.body.local(to).ty.kind().is_raw_ptr() { - self.instrument_stack_check_ptr(idx, to); + println!("Self body local to is: {:?}", self.body.local(to)); + match self.body.local(to).ty.kind() { + TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { + self.instrument_stack_check_ref(idx, to, ty)?; + }, + TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { + self.instrument_stack_check_ptr(idx, to, ty)?; + } + _ => {} } Ok(()) } @@ -389,6 +409,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { fn instrument_locals(&mut self, values: &Vec) -> Result<(), Error> { + self.instrument_initialize()?; for local in values { self.instrument_local(*local)? } diff --git a/tests/expected/aliasing/duplicate_write.expected b/tests/expected/aliasing/duplicate_write.expected new file mode 100644 index 000000000000..cb581fbaf28d --- /dev/null +++ b/tests/expected/aliasing/duplicate_write.expected @@ -0,0 +1 @@ +Failed Checks: "Stack violated." From 6327437b82bcb4f558f79a90afa5959dbdbaa271 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Tue, 13 Aug 2024 15:41:46 -0400 Subject: [PATCH 15/72] Add one without macro invocations to show the instrumentation works --- .../aliasing/duplicate_write_silent.expected | 1 + .../aliasing/duplicate_write_silent.rs | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 tests/expected/aliasing/duplicate_write_silent.expected create mode 100644 tests/expected/aliasing/duplicate_write_silent.rs diff --git a/tests/expected/aliasing/duplicate_write_silent.expected b/tests/expected/aliasing/duplicate_write_silent.expected new file mode 100644 index 000000000000..cb581fbaf28d --- /dev/null +++ b/tests/expected/aliasing/duplicate_write_silent.expected @@ -0,0 +1 @@ +Failed Checks: "Stack violated." diff --git a/tests/expected/aliasing/duplicate_write_silent.rs b/tests/expected/aliasing/duplicate_write_silent.rs new file mode 100644 index 000000000000..65a9b9253ce9 --- /dev/null +++ b/tests/expected/aliasing/duplicate_write_silent.rs @@ -0,0 +1,28 @@ +// kani-flags: -Zghost-state -Zaliasing +#![cfg_attr(not(kani), feature(register_tool))] +#![cfg_attr(not(kani), register_tool(kani))] +#![feature(rustc_attrs)] +#![allow(internal_features)] +#![feature(vec_into_raw_parts)] + +include!{"./stackfns.txt"} + +#[cfg_attr(any(kani), kani::proof)] +fn main() { + let mut local: i32; + let temp_ref: &mut i32; + let raw_pointer: *mut i32; + let ref_from_raw_1: &mut i32; + let ref_from_raw_2: &mut i32; + + local = 0; + temp_ref = &mut local; + raw_pointer = temp_ref as *mut i32; + unsafe { + ref_from_raw_1 = &mut *raw_pointer; + *ref_from_raw_1 = 0; + ref_from_raw_2 = &mut *raw_pointer; + *ref_from_raw_2 = 1; + *ref_from_raw_1 = 2; + } +} From 621287c457dbcfe6d39cb8bb6da80ed2aa530362 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Tue, 13 Aug 2024 21:52:56 -0400 Subject: [PATCH 16/72] Clean up for pr review --- .../transform/check_aliasing/mod.rs | 137 ++------- .../kani/src/aliasing.rs | 275 +----------------- library/kani/src/lib.rs | 1 + .../aliasing/duplicate_write.expected | 2 +- tests/expected/aliasing/duplicate_write.rs | 22 +- .../aliasing/duplicate_write_silent.expected | 1 - .../aliasing/duplicate_write_silent.rs | 28 -- 7 files changed, 39 insertions(+), 427 deletions(-) rename tests/expected/aliasing/stackfns.txt => library/kani/src/aliasing.rs (55%) delete mode 100644 tests/expected/aliasing/duplicate_write_silent.expected delete mode 100644 tests/expected/aliasing/duplicate_write_silent.rs diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 0cf563a2e7a8..50ddd0ef109f 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -11,43 +11,16 @@ use rustc_middle::ty::TyCtxt; use stable_mir::mir::mono::Instance; use std::fmt::Debug; use stable_mir::ty::{ - AdtDef, GenericArgKind, GenericArgs, Region, RegionKind, RigidTy, Span, Ty, TyKind + GenericArgKind, GenericArgs, RigidTy, Span, Ty, TyKind }; use stable_mir::mir::{ BasicBlockIdx, Body, Local, LocalDecl, Mutability, Place, TerminatorKind, UnwindAction }; use stable_mir::Error; -use stable_mir::mir::{BasicBlock, BorrowKind, MirVisitor, MutBorrowKind, Operand, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, VarDebugInfo -}; +use stable_mir::mir::{BasicBlock, BorrowKind, MirVisitor, Operand, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, VarDebugInfo}; use std::collections::HashMap; use tracing::trace; -fn instrumented_flag_def(tcx: &TyCtxt) -> AdtDef { - let attr_id = tcx - .all_diagnostic_items(()) - .name_to_id - .get(&rustc_span::symbol::Symbol::intern("KaniAliasingChecked")).unwrap(); - if let TyKind::RigidTy(RigidTy::Adt(def, _)) = - rustc_smir::rustc_internal::stable(tcx.type_of(attr_id)).value.kind() { - def - } else { - panic!("Failure") - } -} - -fn instrumented_flag_type(tcx: &TyCtxt) -> Ty { - let attr_id = tcx - .all_diagnostic_items(()) - .name_to_id - .get(&rustc_span::symbol::Symbol::intern("KaniAliasingChecked")).unwrap(); - if let TyKind::RigidTy(ty) = - rustc_smir::rustc_internal::stable(tcx.type_of(attr_id)).value.kind() { - Ty::from_rigid_kind(ty) - } else { - panic!("Failure") - } -} - #[derive(Clone, Debug, Eq, PartialEq)] pub struct FunctionSignature { name: String, @@ -81,10 +54,6 @@ impl FunctionInstance { #[derive(Default, Debug)] pub struct FunctionInstanceCache(Vec); -pub struct StackedBorrowsPass { - cache: FunctionInstanceCache, -} - /// Instrument the code with checks for uninitialized memory. #[derive(Debug, Default)] pub struct AliasingPass { @@ -119,20 +88,38 @@ impl<'tcx, 'cache> InitializedPassState<'tcx, 'cache> { /// prefix or in their name will be ignored. /// This allows skipping instrumenting functions that /// are called by the instrumentation functions. -const IGNORED_FUNCTIONS: &'static [&'static str] = &[ +const ALIASING_BLACKLIST: &'static [&'static str] = &[ "kani", // Skip kani functions "std::mem::size_of", // skip size_of:: "core::num", // Skip numerical ops (like .wrapping_add) "std::ptr", // Skip pointer manipulation functions - "get_checked" // Skip "get checked", which gives a flag - // specifying whether the function is checked. + "KaniInitializeSState", + "KaniInitializeLocal", + "KaniStackCheckPtr", + "KaniStackCheckRef", + "KaniNewMutRefFromValue", + "KaniNewMutRawFromRef", + "KaniNewMutRefFromRaw", + "std::array", + "std::ops", + "core::panicking", + "std::rt", + "std::panic", + "core::panic", + "std::fmt", + "std::iter", + "core::ub_checks", + "std::cmp", + "core::slice", + "std::mem", + // This blacklist needs expansion. ]; // Currently, the above list of functions is too // coarse-grained; because all kani functions -// are skipped, all std::ptr functions are -// skipped, and kani functions are skipped, -// this pass cannot be used to verify functions +// are skipped, many std modules are skipped, +// and kani functions are skipped, this pass +// cannot be used to verify functions // in those modules, despite the fact that // only some of those functions in those modules // are called by the instrumented code. @@ -155,9 +142,7 @@ impl TransformPass for AliasingPass { fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { trace!(function=?instance.name(), "transform: aliasing pass"); - let mut visitor = CheckInstrumented::new(&tcx); - visitor.visit_body(&body); - if visitor.is_instrumented || !(instance.name().contains("main")) /* for now, just check main for efficiency */ { + if ALIASING_BLACKLIST.iter().fold(false, |blacklisted, member| blacklisted || instance.name().contains(member)) { (false, body) } else { let pass = InitializedPassState::new(body, tcx, &mut self.cache); @@ -187,25 +172,6 @@ struct BodyMutationPassState<'tcx, 'cache> { } impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { - fn mark_instrumented(&mut self) -> Local { - let ty = instrumented_flag_type(&self.tcx); - self.body.new_local(ty, Mutability::Not) - } - - fn assign_ref( - body: &mut CachedBodyMutator, - lvalue: Local, - rvalue: Local, - span: Span) { - let kind = RegionKind::ReErased; - let region = Region { kind }; - let borrow_kind = BorrowKind::Shared; - let lvalue = Place::from(lvalue); - let rvalue = Rvalue::Ref(region, borrow_kind, Place::from(rvalue)); - let kind = StatementKind::Assign(lvalue, rvalue); - body.insert_statement(Statement { kind, span }); - } - fn assign_ptr( body: &mut CachedBodyMutator, lvalue: Local, @@ -317,7 +283,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let from = from.local; // Copy to avoid borrow let to = to.local; // Copy to avoid borrow match self.body.local(to).ty.kind() { - TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { + TyKind::RigidTy(RigidTy::Ref(_, _ty, _)) => { eprintln!("Reborrow from reference not yet handled"); }, TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { @@ -347,7 +313,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { self.instrument_stack_check_ref(idx, from, ty)?; self.instrument_new_mut_raw_from_ref(idx, to, from)?; }, - TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { + TyKind::RigidTy(RigidTy::RawPtr(_ty, _)) => { eprintln!("Pointer to pointer casts not yet handled"); } _ => {} @@ -438,7 +404,6 @@ impl<'tcx, 'cache> BodyMutationPassState<'tcx, 'cache> { } fn finalize(mut self) -> Body { - self.instrumentation_data.mark_instrumented(); self.instrument_locals().unwrap(); self.instrumentation_data.body.finalize_prologue(); self.instrument_instructions().unwrap(); @@ -516,26 +481,6 @@ impl<'tcx, 'cache> LocalPassState<'tcx, 'cache> { } } -struct CheckInstrumented { - marker: AdtDef, - is_instrumented: bool, -} - -impl CheckInstrumented { - fn new(tcx: &TyCtxt) -> CheckInstrumented { - CheckInstrumented { marker: instrumented_flag_def(tcx), is_instrumented: false } - } -} - -impl MirVisitor for CheckInstrumented { - fn visit_local_decl(&mut self, _: Local, decl: &LocalDecl) { - let LocalDecl { ty, .. } = decl; - if let TyKind::RigidTy(RigidTy::Adt(def, _)) = ty.kind() { - self.is_instrumented = self.is_instrumented || self.marker == def; - } - } -} - struct CollectLocalVisitor { values: Vec, } @@ -565,10 +510,6 @@ impl MirVisitor for CollectLocalVisitor { } impl FunctionInstanceCache { - fn new() -> Self { - Self (Vec::new()) - } - fn register(&mut self, ctx: &TyCtxt, sig: FunctionSignature) -> Result<&Instance, Error> { let FunctionInstanceCache(cache) = self; for i in 0..cache.len() { @@ -639,10 +580,6 @@ impl CachedBodyMutator { self.body.ghost_statements.push(stmt); } - fn assign_ref(&mut self, lvalue: Local, rvalue: Local) { - self.body.assign_ref(lvalue, rvalue) - } - fn new_index(&mut self) -> MutatorIndex { self.body.new_index() } @@ -683,6 +620,7 @@ enum MutatorIndexStatus { enum Instruction<'a> { Stmt(&'a Statement), + #[allow(unused)] Term(&'a Terminator) } @@ -771,21 +709,6 @@ impl BodyMutator { self.insert_bb(term); } - fn insert_statement(&mut self, stmt: Statement) { - self.ghost_statements.push(stmt); - } - - fn assign_ref(&mut self, lvalue: Local, rvalue: Local) { - let kind = RegionKind::ReErased; - let region = Region { kind }; - let borrow = BorrowKind::Mut { kind: MutBorrowKind::Default }; - let lvalue = Place::from(lvalue); - let rvalue = Rvalue::Ref(region, borrow, Place::from(rvalue)); - let kind = StatementKind::Assign(lvalue, rvalue); - let span = self.span; - self.insert_statement(Statement { kind, span }); - } - fn next_block(&self) -> usize { self.blocks.len() + self.ghost_blocks.len() + 1 } diff --git a/tests/expected/aliasing/stackfns.txt b/library/kani/src/aliasing.rs similarity index 55% rename from tests/expected/aliasing/stackfns.txt rename to library/kani/src/aliasing.rs index 042b6d44389b..e703b2c9137d 100644 --- a/tests/expected/aliasing/stackfns.txt +++ b/library/kani/src/aliasing.rs @@ -1,103 +1,10 @@ -// kani-flags: -Zghost-state -Zaliasing -// Copyright Jacob Salzberg -// SPDX-License-Identifier: Apache-2.0 - -// Basic test from the stacked borrows paper -#[rustc_diagnostic_item = "KaniAliasingChecked"] -#[derive(Copy, Clone, Debug)] -pub struct AliasingChecked { amount: usize } - const STACK_DEPTH: usize = 15; type PointerTag = u8; -#[cfg(kani)] -extern crate kani; - -#[cfg(kani)] -use kani::shadow::ShadowMem; - -#[cfg(not(kani))] -use std::collections::HashMap; -#[cfg(not(kani))] -#[derive(Debug)] -struct ShadowMem{ - mem: Option>, - default: T -} -#[cfg(not(kani))] -static mut FOUND_POINTERS: Option> = None; -#[cfg(not(kani))] -static mut FOUND_POINTERS_TOP: usize = 0; - -#[cfg(not(kani))] -impl ShadowMem where T: Copy { - #[inline(always)] - const fn new(v: T) -> Self { - ShadowMem { mem: None, default: v } - } - - fn set(&mut self, pointer: *const U, value: T) { - unsafe { - let map = FOUND_POINTERS.get_or_insert(HashMap::new()); - let e = map.entry(pointer as *const u8) - .or_insert_with(|| { let top = FOUND_POINTERS_TOP; FOUND_POINTERS_TOP += 1; top } ); - let mem = self.mem.get_or_insert(HashMap::new()); - mem.insert(*e, value); - } - } - - fn get(&self, pointer: *const U) -> T { - unsafe { - let map = FOUND_POINTERS.get_or_insert(HashMap::new()); - let pointer = pointer as *const u8; - *(self.mem.as_ref().unwrap().get(map.get(&pointer).unwrap()).unwrap_or(&self.default)) - } - } -} - -#[inline(never)] -pub fn get_checked() -> AliasingChecked { - static mut CHECKED: AliasingChecked = AliasingChecked { amount: 0 }; - unsafe { CHECKED.amount = CHECKED.amount.wrapping_add(1); CHECKED } -} - -#[cfg(any(kani))] -pub fn pointer_object(pointer: *const U) -> usize { - let checked = get_checked(); - let _ = checked; - kani::mem::pointer_object(pointer) -} - -#[cfg(not(kani))] -pub fn pointer_object(pointer: *const U) -> usize { - pointer as usize -} - -#[cfg(any(kani))] -pub fn pointer_offset(pointer: *const U) -> usize { - let checked = get_checked(); - let _ = checked; - kani::mem::pointer_offset(pointer) -} - -#[cfg(not(kani))] -pub fn pointer_offset(_pointer: *const U) -> usize { - 0 -} - -#[cfg(any(kani))] -pub fn assume(b: bool) { - let checked = get_checked(); - let _ = checked; - kani::assume(b); -} - -#[cfg(not(kani))] -pub fn assume(b: bool) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - assert!(b); -} +use crate::shadow::ShadowMem; +use crate::mem::pointer_object; +use crate::mem::pointer_offset; +use crate::{assume, any}; /// The stacked borrows state. pub mod sstate { @@ -124,8 +31,6 @@ pub mod sstate { pub(self) const DISABLED: u8 = 3; pub(self) fn grants(access: bool, tag: u8) -> bool { - let checked: AliasingChecked = get_checked(); - let _ = checked; tag != Self::DISABLED && (access != Access::WRITE || tag != Self::SHAREDRO) } } @@ -133,22 +38,6 @@ pub mod sstate { /// Associate every pointer object with a permission static mut PERMS: ShadowMem = ShadowMem::new(0); - #[cfg(not(kani))] - pub fn debug() { - unsafe { - println!("tags & perms at this point: tags: {:?} perms: {:?}", sstate::TAGS, sstate::PERMS); - self::monitors::debug_monitors(); - } - } - - #[cfg(any(kani))] - #[inline(always)] - pub fn debug() { - let checked = get_checked(); - let _ = checked; - return; - } - pub(super) mod monitors { static mut STATE: bool = false; static mut OBJECT: usize = 0; @@ -157,14 +46,6 @@ pub mod sstate { static mut STACK_PERMS: [u8; STACK_DEPTH] = [0; STACK_DEPTH]; static mut STACK_TOP: usize = 0; - #[cfg(not(kani))] - pub fn debug_monitors() { - unsafe { - println!("monitors at this point state: {:?} object: {:?} offset: {:?} stags: {:?} sperms: {:?} stop: {:?}", - STATE, OBJECT, OFFSET, STACK_TAGS, STACK_PERMS, STACK_TOP); - } - } - #[non_exhaustive] struct MonitorState; impl MonitorState { @@ -192,8 +73,6 @@ pub mod sstate { /// An N+1th monitor is allocated as a "garbage" /// area to be used when no monitor is picked. pub fn prepare_monitors() { - let checked: AliasingChecked = get_checked(); - let _ = checked; unsafe { OBJECT = 0usize; OFFSET = 0usize; @@ -207,8 +86,6 @@ pub mod sstate { /// Initialize local when track local is true, picking a monitor, /// and setting its object and offset to within pointer. pub(super) unsafe fn track_local(tag: u8, pointer: *const U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; // Decide whether to initialize the stacks // for location:location+size_of(U). // Offset has already been picked earlier. @@ -226,8 +103,6 @@ pub mod sstate { /// Push a tag with a permission perm at pointer pub(super) fn push(tag: u8, perm: u8, pointer: *const U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; // Decide whether to initialize the stacks // for location:location+size_of(U). // Offset has already been picked earlier. @@ -245,8 +120,6 @@ pub mod sstate { } pub(super) fn stack_check(tag: u8, access: bool, address: *const U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; unsafe { use self::*; if STATE == MonitorState::INIT && @@ -268,7 +141,7 @@ pub mod sstate { j += 1; } STACK_TOP = new_top; - assert!(found, "Stack violated."); + crate::assert(found, "Stack violated."); } } } @@ -276,15 +149,11 @@ pub mod sstate { #[rustc_diagnostic_item = "KaniInitializeSState"] pub fn initialize() { - let checked: AliasingChecked = get_checked(); - let _ = checked; self::monitors::prepare_monitors(); } /// Push the permissions at the given location pub fn push(tag: u8, perm: u8, address: *const U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; self::monitors::push(tag, perm, address) } @@ -300,8 +169,6 @@ pub mod sstate { /// and a reference to the stack location. #[rustc_diagnostic_item = "KaniInitializeLocal"] pub fn initialize_local(pointer: *const U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; unsafe { let tag = NEXT_TAG; TAGS.set(pointer, tag); @@ -313,8 +180,6 @@ pub mod sstate { #[rustc_diagnostic_item = "KaniStackCheckPtr"] pub fn stack_check_ptr(pointer_value: *const *mut U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; unsafe { let tag = TAGS.get(pointer_value); let perm = PERMS.get(pointer_value); @@ -331,8 +196,6 @@ pub mod sstate { #[rustc_diagnostic_item = "KaniStackCheckRef"] pub fn stack_check_ref(pointer_value: *const &mut U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; stack_check_ptr(pointer_value as *const *mut U); } @@ -342,8 +205,6 @@ pub mod sstate { /// pointer_to_val. #[rustc_diagnostic_item = "KaniNewMutRefFromValue"] pub fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { - let checked: AliasingChecked = get_checked(); - let _ = checked; unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); @@ -358,8 +219,6 @@ pub mod sstate { /// pointer_to_ref, and pushing the tag to the original location. #[rustc_diagnostic_item = "KaniNewMutRawFromRef"] pub fn new_mut_raw_from_ref(pointer_to_created: *const *mut T, pointer_to_ref: *const &mut T) { - let checked: AliasingChecked = get_checked(); - let _ = checked; unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); @@ -374,8 +233,6 @@ pub mod sstate { /// pointer_to_ref, and pushing the tag to the original location. #[rustc_diagnostic_item = "KaniNewMutRefFromRaw"] pub fn new_mut_ref_from_raw(pointer_to_created: *const &mut T, pointer_to_ref: *const *mut T) { - let checked: AliasingChecked = get_checked(); - let _ = checked; unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); @@ -385,126 +242,6 @@ pub mod sstate { } } -#[cfg(any(kani))] pub fn demonic_nondet() -> bool { - let checked: AliasingChecked = get_checked(); - let _ = checked; - kani::any::() -} - -#[cfg(not(kani))] -pub fn demonic_nondet() -> bool { - let checked: AliasingChecked = get_checked(); - let _ = checked; - true -} - -#[cfg(not(kani))] -static mut LOCAL_COUNT: u32 = 0; - -#[cfg(not(kani))] -pub fn initialize_local(local: *const T) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - unsafe { - println!("Initializing local {:?}", LOCAL_COUNT); - } - sstate::initialize_local(local); - sstate::debug(); - unsafe { LOCAL_COUNT += 1 }; -} - -#[cfg(not(kani))] -pub fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - println!("new mut ref from value"); - sstate::new_mut_ref_from_value(pointer_to_created, pointer_to_val); - sstate::debug(); -} - -#[cfg(not(kani))] -pub fn new_mut_raw_from_ref(pointer_to_created: *const *mut T, pointer_to_ref: *const &mut T) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - println!("new mut raw from ref"); - sstate::new_mut_raw_from_ref(pointer_to_created, pointer_to_ref); - sstate::debug(); -} - -#[cfg(not(kani))] -pub fn new_mut_ref_from_raw(pointer_to_created: *const &mut T, pointer_to_ref: *const *mut T) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - println!("new mut ref from raw"); - sstate::new_mut_ref_from_raw(pointer_to_created, pointer_to_ref); - sstate::debug(); -} - -pub fn stack_check_ptr(pointer_value: *const *mut U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - println!("checking ptr on stack"); - sstate::stack_check_ptr(pointer_value); - sstate::debug(); -} - -pub fn stack_check_ref(pointer_value: *const &mut U) { - let checked: AliasingChecked = get_checked(); - let _ = checked; - println!("checking ref on stack"); - sstate::stack_check_ref(pointer_value); - sstate::debug(); -} - -macro_rules! initialize { - () => { - #[cfg(not(kani))] - sstate::initialize(); - } -} - -macro_rules! initialize_local { - ($place:ident) => { - #[cfg(not(kani))] - initialize_local(std::ptr::addr_of!($place)); - } -} - -macro_rules! new_mut_ref_from_value { - ($reference:ident) => { - #[cfg(not(kani))] - new_mut_ref_from_value(std::ptr::addr_of!($reference), - $reference); - } -} - -macro_rules! stack_check_ref { - ($reference:ident) => { - #[cfg(not(kani))] - stack_check_ref(std::ptr::addr_of!($reference)); - } -} - -macro_rules! new_mut_raw_from_ref { - ($pointer: ident, $reference: ident) => { - #[cfg(not(kani))] - new_mut_raw_from_ref(std::ptr::addr_of!($pointer), - std::ptr::addr_of!($reference)); - } -} - -macro_rules! new_mut_ref_from_raw { - ($pointer: ident, $reference: ident) => { - #[cfg(not(kani))] - new_mut_ref_from_raw(std::ptr::addr_of!($pointer), - std::ptr::addr_of!($reference)); - } -} - -macro_rules! stack_check_ptr { - ($pointer: ident) => { - #[cfg(not(kani))] - stack_check_ptr(std::ptr::addr_of!($pointer)); - } + any::() } diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index 046c6e7a0667..603597fb2d60 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -23,6 +23,7 @@ // Allow us to use `kani::` to access crate features. extern crate self as kani; +pub mod aliasing; pub mod arbitrary; #[cfg(feature = "concrete_playback")] mod concrete_playback; diff --git a/tests/expected/aliasing/duplicate_write.expected b/tests/expected/aliasing/duplicate_write.expected index cb581fbaf28d..35a510028c4e 100644 --- a/tests/expected/aliasing/duplicate_write.expected +++ b/tests/expected/aliasing/duplicate_write.expected @@ -1 +1 @@ -Failed Checks: "Stack violated." +Failed Checks: Stack violated. diff --git a/tests/expected/aliasing/duplicate_write.rs b/tests/expected/aliasing/duplicate_write.rs index c10e27667bb0..62ecec832f21 100644 --- a/tests/expected/aliasing/duplicate_write.rs +++ b/tests/expected/aliasing/duplicate_write.rs @@ -1,41 +1,21 @@ // kani-flags: -Zghost-state -Zaliasing -#![cfg_attr(not(kani), feature(register_tool))] -#![cfg_attr(not(kani), register_tool(kani))] -#![feature(rustc_attrs)] -#![allow(internal_features)] -#![feature(vec_into_raw_parts)] -include!{"./stackfns.txt"} - -#[cfg_attr(any(kani), kani::proof)] +#[kani::proof] fn main() { let mut local: i32; let temp_ref: &mut i32; let raw_pointer: *mut i32; let ref_from_raw_1: &mut i32; let ref_from_raw_2: &mut i32; - initialize!(); local = 0; - initialize_local!(local); temp_ref = &mut local; - initialize_local!(temp_ref); - new_mut_ref_from_value!(temp_ref); raw_pointer = temp_ref as *mut i32; - initialize_local!(raw_pointer); - stack_check_ref!(temp_ref); - new_mut_raw_from_ref!(raw_pointer, temp_ref); unsafe { ref_from_raw_1 = &mut *raw_pointer; - new_mut_ref_from_raw!(ref_from_raw_1, raw_pointer); *ref_from_raw_1 = 0; - stack_check_ref!(ref_from_raw_1); ref_from_raw_2 = &mut *raw_pointer; - stack_check_ptr!(raw_pointer); - new_mut_ref_from_raw!(ref_from_raw_2, raw_pointer); *ref_from_raw_2 = 1; - stack_check_ref!(ref_from_raw_2); *ref_from_raw_1 = 2; - stack_check_ref!(ref_from_raw_1); } } diff --git a/tests/expected/aliasing/duplicate_write_silent.expected b/tests/expected/aliasing/duplicate_write_silent.expected deleted file mode 100644 index cb581fbaf28d..000000000000 --- a/tests/expected/aliasing/duplicate_write_silent.expected +++ /dev/null @@ -1 +0,0 @@ -Failed Checks: "Stack violated." diff --git a/tests/expected/aliasing/duplicate_write_silent.rs b/tests/expected/aliasing/duplicate_write_silent.rs deleted file mode 100644 index 65a9b9253ce9..000000000000 --- a/tests/expected/aliasing/duplicate_write_silent.rs +++ /dev/null @@ -1,28 +0,0 @@ -// kani-flags: -Zghost-state -Zaliasing -#![cfg_attr(not(kani), feature(register_tool))] -#![cfg_attr(not(kani), register_tool(kani))] -#![feature(rustc_attrs)] -#![allow(internal_features)] -#![feature(vec_into_raw_parts)] - -include!{"./stackfns.txt"} - -#[cfg_attr(any(kani), kani::proof)] -fn main() { - let mut local: i32; - let temp_ref: &mut i32; - let raw_pointer: *mut i32; - let ref_from_raw_1: &mut i32; - let ref_from_raw_2: &mut i32; - - local = 0; - temp_ref = &mut local; - raw_pointer = temp_ref as *mut i32; - unsafe { - ref_from_raw_1 = &mut *raw_pointer; - *ref_from_raw_1 = 0; - ref_from_raw_2 = &mut *raw_pointer; - *ref_from_raw_2 = 1; - *ref_from_raw_1 = 2; - } -} From 05032da8942af65349382b0d1ab0a12aab3b8a5c Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Tue, 13 Aug 2024 21:57:49 -0400 Subject: [PATCH 17/72] apply formatter --- .../transform/check_aliasing/mod.rs | 257 +++++++++++------- .../src/kani_middle/transform/mod.rs | 5 +- library/kani/src/aliasing.rs | 63 +++-- 3 files changed, 201 insertions(+), 124 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 50ddd0ef109f..5da224d7b292 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -9,16 +9,17 @@ use crate::kani_middle::transform::{TransformPass, TransformationType}; use crate::kani_queries::QueryDb; use rustc_middle::ty::TyCtxt; use stable_mir::mir::mono::Instance; -use std::fmt::Debug; -use stable_mir::ty::{ - GenericArgKind, GenericArgs, RigidTy, Span, Ty, TyKind +use stable_mir::mir::{ + BasicBlock, BorrowKind, MirVisitor, Operand, ProjectionElem, Rvalue, Statement, StatementKind, + Terminator, VarDebugInfo, }; use stable_mir::mir::{ - BasicBlockIdx, Body, Local, LocalDecl, Mutability, Place, TerminatorKind, UnwindAction + BasicBlockIdx, Body, Local, LocalDecl, Mutability, Place, TerminatorKind, UnwindAction, }; +use stable_mir::ty::{GenericArgKind, GenericArgs, RigidTy, Span, Ty, TyKind}; use stable_mir::Error; -use stable_mir::mir::{BasicBlock, BorrowKind, MirVisitor, Operand, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, VarDebugInfo}; use std::collections::HashMap; +use std::fmt::Debug; use tracing::trace; #[derive(Clone, Debug, Eq, PartialEq)] @@ -29,10 +30,7 @@ pub struct FunctionSignature { impl FunctionSignature { pub fn new(name: &str, args: &[GenericArgKind]) -> FunctionSignature { - FunctionSignature { - name: name.to_string(), - args: args.to_vec(), - } + FunctionSignature { name: name.to_string(), args: args.to_vec() } } } @@ -44,10 +42,7 @@ pub struct FunctionInstance { impl FunctionInstance { pub fn new(signature: FunctionSignature, instance: Instance) -> FunctionInstance { - FunctionInstance { - signature, - instance, - } + FunctionInstance { signature, instance } } } @@ -89,10 +84,10 @@ impl<'tcx, 'cache> InitializedPassState<'tcx, 'cache> { /// This allows skipping instrumenting functions that /// are called by the instrumentation functions. const ALIASING_BLACKLIST: &'static [&'static str] = &[ - "kani", // Skip kani functions + "kani", // Skip kani functions "std::mem::size_of", // skip size_of:: - "core::num", // Skip numerical ops (like .wrapping_add) - "std::ptr", // Skip pointer manipulation functions + "core::num", // Skip numerical ops (like .wrapping_add) + "std::ptr", // Skip pointer manipulation functions "KaniInitializeSState", "KaniInitializeLocal", "KaniStackCheckPtr", @@ -142,7 +137,10 @@ impl TransformPass for AliasingPass { fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { trace!(function=?instance.name(), "transform: aliasing pass"); - if ALIASING_BLACKLIST.iter().fold(false, |blacklisted, member| blacklisted || instance.name().contains(member)) { + if ALIASING_BLACKLIST + .iter() + .fold(false, |blacklisted, member| blacklisted || instance.name().contains(member)) + { (false, body) } else { let pass = InitializedPassState::new(body, tcx, &mut self.cache); @@ -172,11 +170,7 @@ struct BodyMutationPassState<'tcx, 'cache> { } impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { - fn assign_ptr( - body: &mut CachedBodyMutator, - lvalue: Local, - rvalue: Local, - span: Span) { + fn assign_ptr(body: &mut CachedBodyMutator, lvalue: Local, rvalue: Local, span: Span) { let lvalue = Place::from(lvalue); let rvalue = Rvalue::AddressOf(Mutability::Not, Place::from(rvalue)); let kind = StatementKind::Assign(lvalue, rvalue); @@ -184,10 +178,9 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { } /// Initialize the monitors - fn instrument_initialize( - &mut self, - ) -> Result<(), Error> { - let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniInitializeSState", &[]))?; + fn instrument_initialize(&mut self) -> Result<(), Error> { + let instance = + self.cache.register(&self.tcx, FunctionSignature::new("KaniInitializeSState", &[]))?; let body = &mut self.body; body.call(instance, [].to_vec(), body.unit); Ok(()) @@ -197,67 +190,108 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// instrument it with the functions that initialize the stack: /// let ptr_x: *const T = &raw const x; /// initialize_local(ptr_x); - fn instrument_local( - &mut self, - local: usize, - ) -> Result<(), Error> { + fn instrument_local(&mut self, local: usize) -> Result<(), Error> { let ty = self.body.local(local).ty; let ptr_ty = Ty::new_ptr(ty, Mutability::Not); let span = self.body.span().clone(); let body = &mut self.body; - let local_ptr = self.meta_stack.entry(local).or_insert_with(|| body.new_local(ptr_ty, Mutability::Not)); + let local_ptr = + self.meta_stack.entry(local).or_insert_with(|| body.new_local(ptr_ty, Mutability::Not)); Self::assign_ptr(body, *local_ptr, local, span); - let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniInitializeLocal", &[GenericArgKind::Type(ty)]))?; + let instance = self.cache.register( + &self.tcx, + FunctionSignature::new("KaniInitializeLocal", &[GenericArgKind::Type(ty)]), + )?; body.call(instance, [*local_ptr].to_vec(), body.unit); Ok(()) } - fn instrument_new_stack_reference(&mut self, idx: &MutatorIndex, lvalue: Local, rvalue: Local) -> Result<(), Error> { + fn instrument_new_stack_reference( + &mut self, + idx: &MutatorIndex, + lvalue: Local, + rvalue: Local, + ) -> Result<(), Error> { // Initialize the constants let ty = self.body.local(rvalue).ty; let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); let rvalue_ref = self.meta_stack.get(&rvalue).unwrap(); - let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutRefFromValue", &[GenericArgKind::Type(ty)]))?; + let instance = self.cache.register( + &self.tcx, + FunctionSignature::new("KaniNewMutRefFromValue", &[GenericArgKind::Type(ty)]), + )?; self.body.call(instance, vec![*lvalue_ref, *rvalue_ref], self.body.unit); self.body.split(idx); Ok(()) } - fn instrument_stack_check_ref(&mut self, idx: &MutatorIndex, place: Local, ty: Ty) -> Result<(), Error> { + fn instrument_stack_check_ref( + &mut self, + idx: &MutatorIndex, + place: Local, + ty: Ty, + ) -> Result<(), Error> { // Initialize the constants let place_ref = self.meta_stack.get(&place).unwrap(); - let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniStackCheckRef", &[GenericArgKind::Type(ty)]))?; + let instance = self.cache.register( + &self.tcx, + FunctionSignature::new("KaniStackCheckRef", &[GenericArgKind::Type(ty)]), + )?; self.body.call(instance, vec![*place_ref], self.body.unit); self.body.split(idx); Ok(()) } - fn instrument_stack_check_ptr(&mut self, idx: &MutatorIndex, place: Local, ty: Ty) -> Result<(), Error> { + fn instrument_stack_check_ptr( + &mut self, + idx: &MutatorIndex, + place: Local, + ty: Ty, + ) -> Result<(), Error> { // Initialize the constants let place_ref = self.meta_stack.get(&place).unwrap(); - let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniStackCheckPtr", &[GenericArgKind::Type(ty)]))?; + let instance = self.cache.register( + &self.tcx, + FunctionSignature::new("KaniStackCheckPtr", &[GenericArgKind::Type(ty)]), + )?; self.body.call(instance, vec![*place_ref], self.body.unit); self.body.split(idx); Ok(()) } - fn instrument_new_mut_ref_from_raw(&mut self, idx: &MutatorIndex, created: Local, raw: Local) -> Result<(), Error> { + fn instrument_new_mut_ref_from_raw( + &mut self, + idx: &MutatorIndex, + created: Local, + raw: Local, + ) -> Result<(), Error> { // Initialize the constants let ty = self.body.local(created).ty; let created_ref = self.meta_stack.get(&created).unwrap(); let reference_ref = self.meta_stack.get(&raw).unwrap(); - let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutRefFromRaw", &[GenericArgKind::Type(ty)]))?; + let instance = self.cache.register( + &self.tcx, + FunctionSignature::new("KaniNewMutRefFromRaw", &[GenericArgKind::Type(ty)]), + )?; self.body.call(instance, vec![*created_ref, *reference_ref], self.body.unit); self.body.split(idx); Ok(()) } - fn instrument_new_mut_raw_from_ref(&mut self, idx: &MutatorIndex, created: Local, reference: Local) -> Result<(), Error> { + fn instrument_new_mut_raw_from_ref( + &mut self, + idx: &MutatorIndex, + created: Local, + reference: Local, + ) -> Result<(), Error> { // Initialize the constants let ty = self.body.local(created).ty; let created_ref = self.meta_stack.get(&created).unwrap(); let reference_ref = self.meta_stack.get(&reference).unwrap(); - let instance = self.cache.register(&self.tcx, FunctionSignature::new("KaniNewMutRawFromRef", &[GenericArgKind::Type(ty)]))?; + let instance = self.cache.register( + &self.tcx, + FunctionSignature::new("KaniNewMutRawFromRef", &[GenericArgKind::Type(ty)]), + )?; self.body.call(instance, vec![*created_ref, *reference_ref], self.body.unit); self.body.split(idx); Ok(()) @@ -265,7 +299,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { fn instrument_index(&mut self, _values: &Vec, idx: &MutatorIndex) -> Result<(), Error> { match self.body.inspect(idx) { - Instruction::Stmt(Statement { kind, ..} ) => { + Instruction::Stmt(Statement { kind, .. }) => { match kind { StatementKind::Assign(to, rvalue) => { let to = to.clone(); @@ -275,35 +309,41 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { [] => { // Direct reference to stack local // x = &y - self.instrument_new_stack_reference(idx, to.local, from.local)?; - }, + self.instrument_new_stack_reference( + idx, to.local, from.local, + )?; + } [ProjectionElem::Deref] => { // Reborrow // x : &mut T = &*(y : *mut T) let from = from.local; // Copy to avoid borrow - let to = to.local; // Copy to avoid borrow + let to = to.local; // Copy to avoid borrow match self.body.local(to).ty.kind() { TyKind::RigidTy(RigidTy::Ref(_, _ty, _)) => { - eprintln!("Reborrow from reference not yet handled"); - }, + eprintln!( + "Reborrow from reference not yet handled" + ); + } TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { self.instrument_stack_check_ref(idx, from, ty)?; - self.instrument_new_mut_ref_from_raw(idx, to, from)?; - }, + self.instrument_new_mut_ref_from_raw( + idx, to, from, + )?; + } _ => {} } - }, + } _ => { eprintln!("Field projections not yet handled"); } } - }, + } Rvalue::AddressOf(Mutability::Mut, from) => { match from.projection[..] { [] => { // x = &raw y eprintln!("addr of not yet handled"); - }, + } [ProjectionElem::Deref] => { // x = &raw mut *(y: &mut T) let from = from.local; // Copy to avoid borrow @@ -311,18 +351,21 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { match self.body.local(to).ty.kind() { TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { self.instrument_stack_check_ref(idx, from, ty)?; - self.instrument_new_mut_raw_from_ref(idx, to, from)?; - }, + self.instrument_new_mut_raw_from_ref( + idx, to, from, + )?; + } TyKind::RigidTy(RigidTy::RawPtr(_ty, _)) => { - eprintln!("Pointer to pointer casts not yet handled"); + eprintln!( + "Pointer to pointer casts not yet handled" + ); } _ => {} } - }, - _ => { } + _ => {} } - }, + } _ => { eprintln!("Rvalue kind: {:?} not yet handled", rvalue); } @@ -339,7 +382,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { match self.body.local(to).ty.kind() { TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { self.instrument_stack_check_ref(idx, to, ty)?; - }, + } TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { self.instrument_stack_check_ptr(idx, to, ty)?; } @@ -352,7 +395,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Ok(()) } } - }, + } // The following are not yet handled, however, no info is printed // to avoid blowups: StatementKind::Retag(_, _) => Ok(()), @@ -373,8 +416,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { } } - fn instrument_locals(&mut self, - values: &Vec) -> Result<(), Error> { + fn instrument_locals(&mut self, values: &Vec) -> Result<(), Error> { self.instrument_initialize()?; for local in values { self.instrument_local(*local)? @@ -431,8 +473,28 @@ struct CachedBodyMutator { } impl BodyMutator { - fn new(blocks: Vec, locals: Vec, arg_count: usize, var_debug_info: Vec, spread_arg: Option, span: Span, ghost_locals: Vec, ghost_blocks: Vec, statements: Vec) -> Self { - BodyMutator { blocks, locals, arg_count, var_debug_info, spread_arg, span, ghost_locals, ghost_blocks, ghost_statements: statements } + fn new( + blocks: Vec, + locals: Vec, + arg_count: usize, + var_debug_info: Vec, + spread_arg: Option, + span: Span, + ghost_locals: Vec, + ghost_blocks: Vec, + statements: Vec, + ) -> Self { + BodyMutator { + blocks, + locals, + arg_count, + var_debug_info, + spread_arg, + span, + ghost_locals, + ghost_blocks, + ghost_statements: statements, + } } fn gen_bb0(body: &mut Body) -> BasicBlock { @@ -461,7 +523,17 @@ impl BodyMutator { let spread_arg = body.spread_arg(); let debug_info = body.var_debug_info; let statements = Vec::new(); - BodyMutator::new(body.blocks, locals, arg_count, debug_info, spread_arg, body.span, ghost_locals, ghost_blocks, statements) + BodyMutator::new( + body.blocks, + locals, + arg_count, + debug_info, + spread_arg, + body.span, + ghost_locals, + ghost_blocks, + statements, + ) } } @@ -474,10 +546,7 @@ impl<'tcx, 'cache> LocalPassState<'tcx, 'cache> { meta_stack: HashMap::new(), body: CachedBodyMutator::from(self.body), }; - BodyMutationPassState { - values, - instrumentation_data - } + BodyMutationPassState { values, instrumentation_data } } } @@ -517,8 +586,7 @@ impl FunctionInstanceCache { return Ok(&cache[i].instance); } } - let fndef = - super::super::find_fn_def(*ctx, &sig.name) + let fndef = super::super::find_fn_def(*ctx, &sig.name) .ok_or(Error::new(format!("Not found: {}", &sig.name)))?; let instance = Instance::resolve(fndef, &GenericArgs(sig.args.clone()))?; cache.push(FunctionInstance::new(sig, instance)); @@ -528,10 +596,7 @@ impl FunctionInstanceCache { #[allow(unused)] fn get(&self, sig: &FunctionSignature) -> Result<&Instance, Error> { let FunctionInstanceCache(cache) = self; - for FunctionInstance { - signature, - instance, - } in cache { + for FunctionInstance { signature, instance } in cache { if *sig == *signature { return Ok(instance); } @@ -566,7 +631,9 @@ impl CachedBodyMutator { let cache = &mut self.cache; let body = &mut self.body; { - func_local = cache.entry(*callee).or_insert_with(|| body.new_local(callee.ty(), Mutability::Not)); + func_local = cache + .entry(*callee) + .or_insert_with(|| body.new_local(callee.ty(), Mutability::Not)); } } self.body.call(*func_local, args, local); @@ -609,19 +676,19 @@ impl CachedBodyMutator { struct MutatorIndex { bb: BasicBlockIdx, idx: usize, - span: Span + span: Span, } #[derive(PartialEq, Eq)] enum MutatorIndexStatus { Remaining, - Done + Done, } enum Instruction<'a> { Stmt(&'a Statement), #[allow(unused)] - Term(&'a Terminator) + Term(&'a Terminator), } impl BodyMutator { @@ -636,7 +703,10 @@ impl BodyMutator { fn call(&mut self, callee: Local, args: Vec, local: Local) { let projection = Vec::new(); let destination = Place { local, projection }; - let args = args.into_iter().map(|v| Operand::Copy(Place { local: v, projection: vec![] } )).collect(); + let args = args + .into_iter() + .map(|v| Operand::Copy(Place { local: v, projection: vec![] })) + .collect(); let func = Operand::Copy(Place::from(callee)); let unwind = UnwindAction::Terminate; let target = Some(self.next_block()); @@ -657,12 +727,7 @@ impl BodyMutator { fn new_index(&self) -> MutatorIndex { let len = self.blocks.len(); let bb = std::cmp::max(len, 1) - 1; - let idx = if len > 0 { - std::cmp::max(self.blocks[bb].statements.len(), 1) - - 1 - } else { - 0 - }; + let idx = if len > 0 { std::cmp::max(self.blocks[bb].statements.len(), 1) - 1 } else { 0 }; let span = self.span; MutatorIndex { bb, idx, span } } @@ -674,11 +739,9 @@ impl BodyMutator { } if index.idx > 0 { if index.idx < self.blocks[index.bb].statements.len() { - index.span = self.blocks[index.bb] - .statements[index.idx].span; + index.span = self.blocks[index.bb].statements[index.idx].span; } else { - index.span = self.blocks[index.bb] - .terminator.span; + index.span = self.blocks[index.bb].terminator.span; } index.idx -= 1; } else if index.bb > 0 { @@ -721,7 +784,17 @@ impl BodyMutator { fn finalize(self) -> Body { match self { - BodyMutator { mut blocks, mut locals, arg_count, var_debug_info, spread_arg, span, ghost_locals, ghost_blocks, ghost_statements } => { + BodyMutator { + mut blocks, + mut locals, + arg_count, + var_debug_info, + spread_arg, + span, + ghost_locals, + ghost_blocks, + ghost_statements, + } => { assert!(ghost_statements.len() == 0); blocks.extend(ghost_blocks.into_iter()); locals.extend(ghost_locals.into_iter()); diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index f50dc10c7d97..8c4e776773d2 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -85,10 +85,7 @@ impl BodyTransformation { }, ); // Check aliasing - transformer.add_pass( - queries, - AliasingPass::new(), - ); + transformer.add_pass(queries, AliasingPass::new()); transformer.add_pass( queries, IntrinsicGeneratorPass { diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index e703b2c9137d..1efaa72b9d87 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -1,10 +1,10 @@ const STACK_DEPTH: usize = 15; type PointerTag = u8; -use crate::shadow::ShadowMem; use crate::mem::pointer_object; use crate::mem::pointer_offset; -use crate::{assume, any}; +use crate::shadow::ShadowMem; +use crate::{any, assume}; /// The stacked borrows state. pub mod sstate { @@ -25,7 +25,7 @@ pub mod sstate { #[non_exhaustive] struct Permission; impl Permission { - pub(self) const UNIQUE: u8 = 0; + pub(self) const UNIQUE: u8 = 0; pub(self) const SHAREDRW: u8 = 1; pub(self) const SHAREDRO: u8 = 2; pub(self) const DISABLED: u8 = 3; @@ -108,9 +108,9 @@ pub mod sstate { // Offset has already been picked earlier. unsafe { use self::*; - if STATE == MonitorState::INIT && - OBJECT == pointer_object(pointer) && - OFFSET == pointer_offset(pointer) + if STATE == MonitorState::INIT + && OBJECT == pointer_object(pointer) + && OFFSET == pointer_offset(pointer) { STACK_TAGS[STACK_TOP] = tag; STACK_PERMS[STACK_TOP] = perm; @@ -122,26 +122,27 @@ pub mod sstate { pub(super) fn stack_check(tag: u8, access: bool, address: *const U) { unsafe { use self::*; - if STATE == MonitorState::INIT && - OFFSET == pointer_offset(address) && - OBJECT == pointer_object(address) { - let mut found = false; - let mut j = 0; - let mut new_top = 0; - assert!(STACK_TOP < STACK_DEPTH); - while j < STACK_DEPTH { - if j < STACK_TOP { - let id = STACK_TAGS[j]; - let kind = STACK_PERMS[j]; - if Permission::grants(access, kind) && id == tag { - new_top = j + 1; - found = true; - } - } - j += 1; - } - STACK_TOP = new_top; - crate::assert(found, "Stack violated."); + if STATE == MonitorState::INIT + && OFFSET == pointer_offset(address) + && OBJECT == pointer_object(address) + { + let mut found = false; + let mut j = 0; + let mut new_top = 0; + assert!(STACK_TOP < STACK_DEPTH); + while j < STACK_DEPTH { + if j < STACK_TOP { + let id = STACK_TAGS[j]; + let kind = STACK_PERMS[j]; + if Permission::grants(access, kind) && id == tag { + new_top = j + 1; + found = true; + } + } + j += 1; + } + STACK_TOP = new_top; + crate::assert(found, "Stack violated."); } } } @@ -218,7 +219,10 @@ pub mod sstate { /// tag, running a stack check on the tag associated with the reference, accessed by /// pointer_to_ref, and pushing the tag to the original location. #[rustc_diagnostic_item = "KaniNewMutRawFromRef"] - pub fn new_mut_raw_from_ref(pointer_to_created: *const *mut T, pointer_to_ref: *const &mut T) { + pub fn new_mut_raw_from_ref( + pointer_to_created: *const *mut T, + pointer_to_ref: *const &mut T, + ) { unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); @@ -232,7 +236,10 @@ pub mod sstate { /// tag, running a stack check on the tag associated with the reference, accessed by /// pointer_to_ref, and pushing the tag to the original location. #[rustc_diagnostic_item = "KaniNewMutRefFromRaw"] - pub fn new_mut_ref_from_raw(pointer_to_created: *const &mut T, pointer_to_ref: *const *mut T) { + pub fn new_mut_ref_from_raw( + pointer_to_created: *const &mut T, + pointer_to_ref: *const *mut T, + ) { unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); From c39e8ca4f9d1321244f4099d1c0d2ee995a913f4 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Wed, 14 Aug 2024 15:25:56 -0400 Subject: [PATCH 18/72] Commit and refactor --- .../transform/check_aliasing/body_mutation.rs | 28 + .../transform/check_aliasing/body_mutator.rs | 258 ++++++ .../check_aliasing/cached_body_mutator.rs | 94 +++ .../check_aliasing/function_cache.rs | 66 ++ .../check_aliasing/instrumentation.rs | 300 +++++++ .../check_aliasing/local_collection.rs | 64 ++ .../transform/check_aliasing/mod.rs | 756 ++---------------- 7 files changed, 857 insertions(+), 709 deletions(-) create mode 100644 kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs create mode 100644 kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs create mode 100644 kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs create mode 100644 kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs create mode 100644 kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs create mode 100644 kani-compiler/src/kani_middle/transform/check_aliasing/local_collection.rs diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs new file mode 100644 index 000000000000..e1b7b73dca30 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs @@ -0,0 +1,28 @@ +use super::{Local, InstrumentationData, MirError, Body}; + +pub struct BodyMutationPassState<'tcx, 'cache> { + values: Vec, + instrumentation_data: InstrumentationData<'tcx, 'cache>, +} + +impl<'tcx, 'cache> BodyMutationPassState<'tcx, 'cache> { + pub fn new(values: Vec, instrumentation_data: InstrumentationData<'tcx, 'cache>) -> Self { + BodyMutationPassState { values, instrumentation_data } + } + + pub fn instrument_locals(&mut self) -> Result<(), MirError> { + self.instrumentation_data.instrument_locals(&self.values) + } + + pub fn instrument_instructions(&mut self) -> Result<(), MirError> { + self.instrumentation_data.instrument_instructions()?; + Ok(()) + } + + pub fn finalize(mut self) -> Body { + self.instrument_locals().unwrap(); + self.instrumentation_data.body.finalize_prologue(); + self.instrument_instructions().unwrap(); + self.instrumentation_data.body.finalize() + } +} diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs new file mode 100644 index 000000000000..1e94743faf4d --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs @@ -0,0 +1,258 @@ +use super::{ + BasicBlock, BasicBlockIdx, Body, Local, LocalDecl, Mutability, Operand, Place, + Span, Statement, Terminator, TerminatorKind, Ty, UnwindAction, VarDebugInfo, +}; + +/// BodyMutator combines the data of the function body +/// with "ghost" basic block and local data, allowing the user +/// to instrument the original body with instructions in the +/// "ghost" section while iterating over the original data from the +/// function body. +pub struct BodyMutator { + blocks: Vec, + locals: Vec, + arg_count: usize, + var_debug_info: Vec, + spread_arg: Option, + span: Span, + + ghost_locals: Vec, + ghost_blocks: Vec, + ghost_statements: Vec, +} + +impl BodyMutator { + /// Instantiate the body mutator + pub fn new( + blocks: Vec, + locals: Vec, + arg_count: usize, + var_debug_info: Vec, + spread_arg: Option, + span: Span, + ghost_locals: Vec, + ghost_blocks: Vec, + statements: Vec, + ) -> Self { + BodyMutator { + blocks, + locals, + arg_count, + var_debug_info, + spread_arg, + span, + ghost_locals, + ghost_blocks, + ghost_statements: statements, + } + } + + /// Generate bb0 which jumps to the "ghost" basic blocks + pub fn gen_bb0(body: &mut Body) -> BasicBlock { + let target = body.blocks.len() + 1; + let kind = TerminatorKind::Goto { target }; + let span = body.span; + let terminator = Terminator { kind, span }; + let statements = Vec::new(); + std::mem::replace(&mut body.blocks[0], BasicBlock { statements, terminator }) + } + + /// Generate a unit local variable to be + /// used as the destination of function calls + pub fn gen_unit(body: &Body) -> LocalDecl { + let ty = Ty::new_tuple(&[]); + let span = body.span; + let mutability = Mutability::Not; + LocalDecl { ty, span, mutability } + } + + /// Create this body from Mir's Body + pub fn from(mut body: Body) -> Self { + let bb0 = Self::gen_bb0(&mut body); + body.blocks.push(bb0); + let ghost_locals = vec![Self::gen_unit(&body)]; + let ghost_blocks = vec![]; + let locals = body.locals().to_vec(); + let arg_count = body.arg_locals().len(); + let spread_arg = body.spread_arg(); + let debug_info = body.var_debug_info; + let statements = Vec::new(); + BodyMutator::new( + body.blocks, + locals, + arg_count, + debug_info, + spread_arg, + body.span, + ghost_locals, + ghost_blocks, + statements, + ) + } + + /// Index into the locals + pub fn local(&self, idx: usize) -> &LocalDecl { + if idx > self.locals.len() { + &self.ghost_locals[idx - self.locals.len()] + } else { + &self.locals[idx] + } + } + + /// Create a new "ghost" local + pub fn new_local(&mut self, ty: Ty, mutability: Mutability) -> Local { + let span = self.span; + let decl = LocalDecl { ty, span, mutability }; + let local = self.locals.len() + self.ghost_locals.len(); + self.ghost_locals.push(decl); + local + } + + /// Insert a call into the function body of the function stored at + /// callee with the arguments in args. + pub fn call(&mut self, callee: Local, args: Vec, local: Local) { + let projection = Vec::new(); + let destination = Place { local, projection }; + let args = args + .into_iter() + .map(|v| Operand::Copy(Place { local: v, projection: vec![] })) + .collect(); + let func = Operand::Copy(Place::from(callee)); + let unwind = UnwindAction::Terminate; + let target = Some(self.next_block()); + let kind = TerminatorKind::Call { func, args, destination, target, unwind }; + let span = self.span; + let terminator = Terminator { kind, span }; + let statements = std::mem::replace(&mut self.ghost_statements, Vec::new()); + self.ghost_blocks.push(BasicBlock { statements, terminator }); + } + + /// Finalize the prologue that initializes the variable data. + pub fn finalize_prologue(&mut self) { + let kind = TerminatorKind::Goto { target: self.blocks.len() - 1 }; + let span = self.span; + let terminator = Terminator { kind, span }; + self.insert_bb(terminator); + } + + /// Insert a ghost statement + pub fn insert_statement(&mut self, stmt: Statement) { + self.ghost_statements.push(stmt); + } + + /// Get an index with which to iterate over the body + pub fn new_index(&self) -> MutatorIndex { + let len = self.blocks.len(); + let bb = std::cmp::max(len, 1) - 1; + let idx = if len > 0 { std::cmp::max(self.blocks[bb].statements.len(), 1) - 1 } else { 0 }; + let span = self.span; + MutatorIndex { bb, idx, span } + } + + /// Decrement the index + pub fn decrement(&self, index: &mut MutatorIndex) -> MutatorIndexStatus { + let mut status = MutatorIndexStatus::Done; + if index.idx > 0 || index.bb > 0 { + status = MutatorIndexStatus::Remaining; + } + if index.idx > 0 { + if index.idx < self.blocks[index.bb].statements.len() { + index.span = self.blocks[index.bb].statements[index.idx].span; + } else { + index.span = self.blocks[index.bb].terminator.span; + } + index.idx -= 1; + } else if index.bb > 0 { + index.bb -= 1; + index.span = self.blocks[index.bb].terminator.span; + index.idx = self.blocks[index.bb].statements.len() + } + status + } + + /// Inspect the index yielding the current statement or terminator + pub fn inspect(&self, index: &MutatorIndex) -> Instruction { + if index.idx >= self.blocks[index.bb].statements.len() { + Instruction::Term(&self.blocks[index.bb].terminator) + } else { + Instruction::Stmt(&self.blocks[index.bb].statements[index.idx]) + } + } + + /// Split at the given index, causing the current ghost code to be called + /// and control flow to return from the ghost code to after the current index + pub fn split(&mut self, index: &MutatorIndex) { + let kind = TerminatorKind::Goto { target: self.blocks.len() + self.ghost_blocks.len() - 1 }; + let span = index.span; + let term = Terminator { kind, span }; + let len = self.blocks[index.bb].statements.len(); + if index.idx < len { + self.ghost_statements.extend(self.blocks[index.bb].statements.split_off(index.idx + 1)); + } + let term = std::mem::replace(&mut self.blocks[index.bb].terminator, term); + self.insert_bb(term); + } + + /// Get the index of the next basic block + pub fn next_block(&self) -> usize { + self.blocks.len() + self.ghost_blocks.len() + 1 + } + + /// Insert a basic block with the given terminator + pub fn insert_bb(&mut self, terminator: Terminator) { + let statements = std::mem::replace(&mut self.ghost_statements, Vec::new()); + let execute_original_body = BasicBlock { statements, terminator }; + self.ghost_blocks.push(execute_original_body); + } + + // Finalize the body mutator yielding a body + pub fn finalize(self) -> Body { + match self { + BodyMutator { + mut blocks, + mut locals, + arg_count, + var_debug_info, + spread_arg, + span, + ghost_locals, + ghost_blocks, + ghost_statements, + } => { + assert!(ghost_statements.len() == 0); + blocks.extend(ghost_blocks.into_iter()); + locals.extend(ghost_locals.into_iter()); + Body::new(blocks, locals, arg_count, var_debug_info, spread_arg, span) + } + } + } + + /// Get the span + pub fn span(&self) -> Span { + self.span + } +} + +/// Mutator index with which to iterate over the function body. +/// when idx = len(blocks[bb]), you are at the terminator, otherwise, +/// you are at the statement idx in the basic block blocks[bb]. +#[derive(Debug)] +pub struct MutatorIndex { + bb: BasicBlockIdx, + idx: usize, + span: Span, +} + +/// Whether or not there is remaining code +#[derive(PartialEq, Eq)] +pub enum MutatorIndexStatus { + Remaining, + Done, +} + +/// The instruction under inspection +pub enum Instruction<'a> { + Stmt(&'a Statement), + #[allow(unused)] + Term(&'a Terminator), +} diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs new file mode 100644 index 000000000000..441b1a3862c7 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs @@ -0,0 +1,94 @@ +use std::collections::HashMap; +use super::{BodyMutator, Local, MirInstance, Mutability, LocalDecl, Body, Ty, Statement, MutatorIndex, MutatorIndexStatus, Instruction, Span}; + +/// Body mutator which wraps the BodyMutator +/// interface with a cache of the locals that +/// store function calls. +pub struct CachedBodyMutator { + body: BodyMutator, + unit: Local, + cache: HashMap, +} + +impl CachedBodyMutator { + /// Create a new cached body mutator + pub fn from(body: Body) -> Self { + let mut body = BodyMutator::from(body); + let unit = body.new_local(Ty::new_tuple(&[]), Mutability::Not); + let cache = HashMap::new(); + CachedBodyMutator { body, unit, cache } + } + + /// Get the local at idx + pub fn local(&self, idx: usize) -> &LocalDecl { + self.body.local(idx) + } + + /// Get a new local + pub fn new_local(&mut self, ty: Ty, mutability: Mutability) -> Local { + self.body.new_local(ty, mutability) + } + + /// Insert a call to the function stored at local with the args + /// stored at args + pub fn call(&mut self, callee: &MirInstance, args: Vec, local: Local) { + let func_local; + { + let cache = &mut self.cache; + let body = &mut self.body; + { + func_local = cache + .entry(*callee) + .or_insert_with(|| body.new_local(callee.ty(), Mutability::Not)); + } + } + self.body.call(*func_local, args, local); + } + + /// Finalize the prologue, initializing all of the locals + pub fn finalize_prologue(&mut self) { + self.body.finalize_prologue(); + } + + /// Insert a ghost statement + pub fn insert_statement(&mut self, stmt: Statement) { + self.body.insert_statement(stmt); + } + + /// Get an index with which to iterate over the body + pub fn new_index(&mut self) -> MutatorIndex { + self.body.new_index() + } + + /// Decrement the index + pub fn decrement_index(&mut self, idx: &mut MutatorIndex) -> MutatorIndexStatus { + self.body.decrement(idx) + } + + /// Split at the index causing the ghost code to be called + /// after that index + pub fn split(&mut self, idx: &MutatorIndex) { + self.body.split(idx); + } + + /// Inspect the instruction at the index + pub fn inspect(&self, idx: &MutatorIndex) -> Instruction { + self.body.inspect(idx) + } + + /// Finalize the body + pub fn finalize(self) -> Body { + self.body.finalize() + } + + /// Get the span + pub fn span(&self) -> Span { + self.body.span() + + } + + /// Get the unit local + pub fn unit(&self) -> Local { + self.unit + } +} diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs new file mode 100644 index 000000000000..1a10b85f7029 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs @@ -0,0 +1,66 @@ +pub use stable_mir::ty::GenericArgKind as GenericArg; +pub use stable_mir::ty::GenericArgs; +use super::{MirInstance, MirError, TyCtxt, super::super::find_fn_def}; +/// FunctionSignature encapsulates the data +/// for rust functions with generic arguments +/// to ensure that it can be cached. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Signature { + name: String, + args: Vec, +} + +impl Signature { + pub fn new(name: &str, args: &[GenericArg]) -> Signature { + Signature { name: name.to_string(), args: args.to_vec() } + } +} + +/// FunctionInstance encapsulates the +/// data for a resolved rust function with +/// generic arguments to ensure that it can be cached. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Instance { + signature: Signature, + instance: MirInstance, +} + +impl Instance { + pub fn new(signature: Signature, instance: super::MirInstance) -> Instance { + Instance { signature, instance } + } +} + +/// Caches function instances for later lookups. +#[derive(Default, Debug)] +pub struct Cache { + cache: Vec, +} + +impl Cache { + pub fn register(&mut self, ctx: &TyCtxt, sig: Signature) -> + Result<&MirInstance, MirError> { + let Cache { cache } = self; + for i in 0..cache.len() { + if sig == cache[i].signature { + return Ok(&cache[i].instance); + } + } + let fndef = find_fn_def(*ctx, &sig.name) + .ok_or(MirError::new(format!("Not found: {}", &sig.name)))?; + let instance = super::MirInstance::resolve(fndef, &GenericArgs(sig.args.clone()))?; + cache.push(Instance::new(sig, instance)); + Ok(&cache[cache.len() - 1].instance) + } + + #[allow(unused)] + fn get(&self, sig: &Signature) -> Result<&MirInstance, MirError> { + let Cache { cache } = self; + for Instance { signature, instance } in cache { + if *sig == *signature { + return Ok(instance); + } + } + Err(MirError::new(format!("Not found: {:?}", sig))) + } +} diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs new file mode 100644 index 000000000000..c77cea803df3 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -0,0 +1,300 @@ +use std::collections::HashMap; +use super::{TyCtxt, CachedBodyMutator, Cache, Local, Place, Rvalue, Mutability, StatementKind, Statement, MirError, Signature, Span, Ty, GenericArg, MutatorIndex, Instruction, BorrowKind, ProjectionElem, TyKind, RigidTy, MutatorIndexStatus}; + +pub struct InstrumentationData<'tcx, 'cache> { + tcx: TyCtxt<'tcx>, + cache: &'cache mut Cache, + meta_stack: HashMap, + pub body: CachedBodyMutator, +} + +impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { + pub fn new(tcx: TyCtxt<'tcx>, cache: &'cache mut Cache, meta_stack: HashMap, body: CachedBodyMutator) -> Self { + InstrumentationData { tcx, cache, meta_stack, body } + } + + /// Assign lvalue to the address of rvalue with the given span. + pub fn assign_ptr(body: &mut CachedBodyMutator, lvalue: Local, rvalue: Local, span: Span) { + let lvalue = Place::from(lvalue); + let rvalue = Rvalue::AddressOf(Mutability::Not, Place::from(rvalue)); + let kind = StatementKind::Assign(lvalue, rvalue); + body.insert_statement(Statement { kind, span }); + } + + /// Instrument the code with a call to initialize the monitors. + pub fn instrument_initialize(&mut self) -> Result<(), MirError> { + let instance = + self.cache.register(&self.tcx, Signature::new("KaniInitializeSState", &[]))?; + let body = &mut self.body; + body.call(instance, [].to_vec(), body.unit()); + Ok(()) + } + + /// For some local, say let x: T; + /// instrument it with the functions that initialize the stack: + /// let ptr_x: *const T = &raw const x; + /// initialize_local(ptr_x); + pub fn instrument_local(&mut self, local: usize) -> Result<(), MirError> { + let ty = self.body.local(local).ty; + let ptr_ty = Ty::new_ptr(ty, Mutability::Not); + let span = self.body.span().clone(); + let body = &mut self.body; + let local_ptr = + self.meta_stack.entry(local).or_insert_with(|| body.new_local(ptr_ty, Mutability::Not)); + Self::assign_ptr(body, *local_ptr, local, span); + let instance = self.cache.register( + &self.tcx, + Signature::new("KaniInitializeLocal", + &[GenericArg::Type(ty)]))?; + body.call(instance, [*local_ptr].to_vec(), body.unit()); + Ok(()) + } + + /// Instrument a stack reference of the form + /// lvalue = &rvalue + /// with an update to the stacked borrows state, + /// at the code index idx. + pub fn instrument_new_stack_reference( + &mut self, + idx: &MutatorIndex, + lvalue: Local, + rvalue: Local, + ) -> Result<(), MirError> { + // Initialize the constants + let ty = self.body.local(rvalue).ty; + let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); + let rvalue_ref = self.meta_stack.get(&rvalue).unwrap(); + let instance = self.cache.register( + &self.tcx, + Signature::new("KaniNewMutRefFromValue", &[GenericArg::Type(ty)]), + )?; + self.body.call(instance, vec![*lvalue_ref, *rvalue_ref], self.body.unit()); + self.body.split(idx); + Ok(()) + } + + /// Instrument a validity assertion on the stacked borrows state + /// at idx for (place: &mut T). + pub fn instrument_stack_check_ref( + &mut self, + idx: &MutatorIndex, + place: Local, + ty: Ty, + ) -> Result<(), MirError> { + // Initialize the constants + let place_ref = self.meta_stack.get(&place).unwrap(); + let instance = self.cache.register( + &self.tcx, + Signature::new("KaniStackCheckRef", &[GenericArg::Type(ty)]), + )?; + self.body.call(instance, vec![*place_ref], self.body.unit()); + self.body.split(idx); + Ok(()) + } + + /// Instrument a validity assertion on the stacked borrows state + /// at idx for (place: *const T). + pub fn instrument_stack_check_ptr( + &mut self, + idx: &MutatorIndex, + place: Local, + ty: Ty, + ) -> Result<(), MirError> { + // Initialize the constants + let place_ref = self.meta_stack.get(&place).unwrap(); + let instance = self.cache.register( + &self.tcx, + Signature::new("KaniStackCheckPtr", &[GenericArg::Type(ty)]), + )?; + self.body.call(instance, vec![*place_ref], self.body.unit()); + self.body.split(idx); + Ok(()) + } + + /// Instrument code of the form + /// created = &mut *(raw: const *T). + pub fn instrument_new_mut_ref_from_raw( + &mut self, + idx: &MutatorIndex, + created: Local, + raw: Local, + ) -> Result<(), MirError> { + // Initialize the constants + let ty = self.body.local(created).ty; + let created_ref = self.meta_stack.get(&created).unwrap(); + let reference_ref = self.meta_stack.get(&raw).unwrap(); + let instance = self.cache.register( + &self.tcx, + Signature::new("KaniNewMutRefFromRaw", &[GenericArg::Type(ty)]), + )?; + self.body.call(instance, vec![*created_ref, *reference_ref], self.body.unit()); + self.body.split(idx); + Ok(()) + } + + /// Instrument code of the form + /// created = (ref: &mut T) as *mut T + pub fn instrument_new_mut_raw_from_ref( + &mut self, + idx: &MutatorIndex, + created: Local, + reference: Local, + ) -> Result<(), MirError> { + // Initialize the constants + let ty = self.body.local(created).ty; + let created_ref = self.meta_stack.get(&created).unwrap(); + let reference_ref = self.meta_stack.get(&reference).unwrap(); + let instance = self.cache.register( + &self.tcx, + Signature::new("KaniNewMutRawFromRef", &[GenericArg::Type(ty)]), + )?; + self.body.call(instance, vec![*created_ref, *reference_ref], self.body.unit()); + self.body.split(idx); + Ok(()) + } + + /// Instrument at the code index idx with the appropriate updates + /// to the stacked borrows state and with assertions for the validity + /// of that state. + pub fn instrument_index(&mut self, idx: &MutatorIndex) -> Result<(), MirError> { + match self.body.inspect(idx) { + Instruction::Stmt(Statement { kind, .. }) => { + match kind { + StatementKind::Assign(to, rvalue) => { + let to = to.clone(); + match rvalue { + Rvalue::Ref(_, BorrowKind::Mut { .. }, from) => { + match from.projection[..] { + [] => { + // Direct reference to stack local + // x = &y + self.instrument_new_stack_reference( + idx, to.local, from.local, + )?; + } + [ProjectionElem::Deref] => { + // Reborrow + // x : &mut T = &*(y : *mut T) + let from = from.local; // Copy to avoid borrow + let to = to.local; // Copy to avoid borrow + match self.body.local(to).ty.kind() { + TyKind::RigidTy(RigidTy::Ref(_, _ty, _)) => { + eprintln!( + "Reborrow from reference not yet handled" + ); + } + TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { + self.instrument_stack_check_ref(idx, from, ty)?; + self.instrument_new_mut_ref_from_raw( + idx, to, from, + )?; + } + _ => {} + } + } + _ => { + eprintln!("Field projections not yet handled"); + } + } + } + Rvalue::AddressOf(Mutability::Mut, from) => { + match from.projection[..] { + [] => { + // x = &raw y + eprintln!("addr of not yet handled"); + } + [ProjectionElem::Deref] => { + // x = &raw mut *(y: &mut T) + let from = from.local; // Copy to avoid borrow + let to = to.local; // Copy to avoid borrow + match self.body.local(to).ty.kind() { + TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { + self.instrument_stack_check_ref(idx, from, ty)?; + self.instrument_new_mut_raw_from_ref( + idx, to, from, + )?; + } + TyKind::RigidTy(RigidTy::RawPtr(_ty, _)) => { + eprintln!( + "Pointer to pointer casts not yet handled" + ); + } + _ => {} + } + } + _ => {} + } + } + _ => { + eprintln!("Rvalue kind: {:?} not yet handled", rvalue); + } + } + match to.projection[..] { + [] => { + // Assignment directly to local + Ok(()) + } + [ProjectionElem::Deref] => { + // *x = rvalue + let to = to.local; + println!("Self body local to is: {:?}", self.body.local(to)); + match self.body.local(to).ty.kind() { + TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { + self.instrument_stack_check_ref(idx, to, ty)?; + } + TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { + self.instrument_stack_check_ptr(idx, to, ty)?; + } + _ => {} + } + Ok(()) + } + _ => { + eprintln!("Field assignment not yet handled"); + Ok(()) + } + } + } + // The following are not yet handled, however, no info is printed + // to avoid blowups: + StatementKind::Retag(_, _) => Ok(()), + StatementKind::FakeRead(_, _) => Ok(()), + StatementKind::SetDiscriminant { .. } => Ok(()), + StatementKind::Deinit(_) => Ok(()), + StatementKind::StorageLive(_) => Ok(()), + StatementKind::StorageDead(_) => Ok(()), + StatementKind::PlaceMention(_) => Ok(()), + StatementKind::AscribeUserType { .. } => Ok(()), + StatementKind::Coverage(_) => Ok(()), + StatementKind::Intrinsic(_) => Ok(()), + StatementKind::ConstEvalCounter => Ok(()), + StatementKind::Nop => Ok(()), + } + } + Instruction::Term(_) => Ok(()), + } + } + + /// Instrument each of the locals collected into values with + /// initialization data. + pub fn instrument_locals(&mut self, values: &Vec) -> Result<(), MirError> { + self.instrument_initialize()?; + for local in values { + self.instrument_local(*local)? + } + Ok(()) + } + + /// Instrument all of the instructions and terminators in the function body + /// with appropriate updates to the stacked borrows state + /// and with validity assertions on the stacked borrows state. + pub fn instrument_instructions(&mut self) -> Result<(), MirError> { + let mut index = self.body.new_index(); + let mut status = MutatorIndexStatus::Remaining; + while status == MutatorIndexStatus::Remaining { + self.instrument_index(&index)?; + status = self.body.decrement_index(&mut index); + } + Ok(()) + } +} diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/local_collection.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/local_collection.rs new file mode 100644 index 000000000000..80d587aefd5b --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/local_collection.rs @@ -0,0 +1,64 @@ +use super::function_cache::*; +use super::{ + Body, BodyMutationPassState, CachedBodyMutator, InstrumentationData, Local, LocalDecl, + MirVisitor, TyCtxt, +}; +use std::collections::HashMap; + +/// Collect local visitor visits the body +/// and collects all of the (non argument) +/// locals. In the future it will collect +/// argument locals and initialize arguments. +struct CollectLocalVisitor { + values: Vec, +} + +impl CollectLocalVisitor { + fn new() -> Self { + let values = Vec::new(); + CollectLocalVisitor { values } + } +} + +impl MirVisitor for CollectLocalVisitor { + fn visit_local_decl(&mut self, local: Local, decl: &LocalDecl) { + let _ = decl; + self.values.push(local); + } +} + +/// The local collection pass state collects locals +/// from function bodies based on whether they are +/// function arguments or locals from the function body. +pub struct LocalCollectionPassState<'tcx, 'cache> { + /// The function body + body: Body, + /// The compilation context + tcx: TyCtxt<'tcx>, + /// The function instance cache, which may + /// be populated by previous runs of the aliasing + /// pass. + cache: &'cache mut Cache, + /// Values + values: CollectLocalVisitor, +} + +impl<'tcx, 'cache> LocalCollectionPassState<'tcx, 'cache> { + pub fn new(body: Body, tcx: TyCtxt<'tcx>, cache: &'cache mut Cache) -> Self { + let values = CollectLocalVisitor::new(); + Self { body, tcx, cache, values } + } + + pub fn collect_locals(mut self) -> Self { + self.values.visit_body(&self.body); + self + } + + pub fn collect_body(self) -> BodyMutationPassState<'tcx, 'cache> { + let values = self.values.values; + let body = CachedBodyMutator::from(self.body); + let instrumentation_data = + InstrumentationData::new(self.tcx, self.cache, HashMap::new(), body); + BodyMutationPassState::new(values, instrumentation_data) + } +} diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 5da224d7b292..90b3e01d6f85 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -1,14 +1,28 @@ // 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. +//! Implement a pass that instruments code with assertions +//! that will fail when the aliasing model is violated. + +use stable_mir::Error as MirError; +use stable_mir::mir::mono::Instance as MirInstance; +mod function_cache; +use function_cache::*; +mod local_collection; +use local_collection::*; +mod cached_body_mutator; +use cached_body_mutator::*; +mod body_mutator; +use body_mutator::*; +mod body_mutation; +use body_mutation::*; +mod instrumentation; +use instrumentation::*; use crate::args::ExtraChecks; 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::{ BasicBlock, BorrowKind, MirVisitor, Operand, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, VarDebugInfo, @@ -16,43 +30,39 @@ use stable_mir::mir::{ use stable_mir::mir::{ BasicBlockIdx, Body, Local, LocalDecl, Mutability, Place, TerminatorKind, UnwindAction, }; -use stable_mir::ty::{GenericArgKind, GenericArgs, RigidTy, Span, Ty, TyKind}; -use stable_mir::Error; -use std::collections::HashMap; +use stable_mir::ty::{RigidTy, Span, Ty, TyKind}; use std::fmt::Debug; use tracing::trace; -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct FunctionSignature { - name: String, - args: Vec, -} - -impl FunctionSignature { - pub fn new(name: &str, args: &[GenericArgKind]) -> FunctionSignature { - FunctionSignature { name: name.to_string(), args: args.to_vec() } - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct FunctionInstance { - signature: FunctionSignature, - instance: Instance, -} - -impl FunctionInstance { - pub fn new(signature: FunctionSignature, instance: Instance) -> FunctionInstance { - FunctionInstance { signature, instance } - } -} - -#[derive(Default, Debug)] -pub struct FunctionInstanceCache(Vec); - -/// Instrument the code with checks for uninitialized memory. +/// Instrument the code with checks for aliasing model +/// violations. +/// Cache functions in-between applications of the pass. +/// Architecturally, this is implemented as the composition +/// of several sub passes on functions: +/// First, information is collected on the variables in the +/// function body and on the arguments to the function. +/// (LocalCollectionPassState) +/// Then, enough information from the body +/// is collected for instrumentation. +/// +/// The body is transformed into a CachedBodyMutator to +/// be used in the BodyMutationPass, which combines the +/// body with (initially empty) storage for +/// instrumented locals and instrumented instructions, +/// and which caches function items referring to +/// resolved function instances. +/// +/// The prologue of the function is then instrumented with data for every +/// stack allocation referenced by a local (instrument_locals). +/// Pointers to these locals are kept in InstrumentationData, +/// which then checks all instructions that modify memory for +/// aliasing violations (instrument_instructions). +/// +/// Finally, a new body is made from the code + the instrumented +/// code. #[derive(Debug, Default)] pub struct AliasingPass { - cache: FunctionInstanceCache, + cache: Cache, } impl AliasingPass { @@ -61,24 +71,6 @@ impl AliasingPass { } } -struct InitializedPassState<'tcx, 'cache> { - body: Body, - tcx: TyCtxt<'tcx>, - cache: &'cache mut FunctionInstanceCache, -} - -impl<'tcx, 'cache> InitializedPassState<'tcx, 'cache> { - fn new(body: Body, tcx: TyCtxt<'tcx>, cache: &'cache mut FunctionInstanceCache) -> Self { - Self { body, tcx, cache } - } - - fn collect_locals(self) -> LocalPassState<'tcx, 'cache> { - let mut visitor = CollectLocalVisitor::new(); - visitor.visit_body(&self.body); - LocalPassState { tcx: self.tcx, cache: self.cache, values: visitor.values, body: self.body } - } -} - /// Functions containing any of the following in their /// prefix or in their name will be ignored. /// This allows skipping instrumenting functions that @@ -135,7 +127,7 @@ impl TransformPass for AliasingPass { args.ub_check.contains(&ExtraChecks::Aliasing) } - fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { + fn transform(&mut self, tcx: TyCtxt, body: Body, instance: MirInstance) -> (bool, Body) { trace!(function=?instance.name(), "transform: aliasing pass"); if ALIASING_BLACKLIST .iter() @@ -143,663 +135,9 @@ impl TransformPass for AliasingPass { { (false, body) } else { - let pass = InitializedPassState::new(body, tcx, &mut self.cache); + let pass = LocalCollectionPassState::new(body, tcx, &mut self.cache); let out = pass.collect_locals().collect_body().finalize(); (true, out) } } } - -struct LocalPassState<'tcx, 'cache> { - body: Body, - tcx: TyCtxt<'tcx>, - cache: &'cache mut FunctionInstanceCache, - values: Vec, -} - -struct InstrumentationData<'tcx, 'cache> { - tcx: TyCtxt<'tcx>, - cache: &'cache mut FunctionInstanceCache, - meta_stack: HashMap, - body: CachedBodyMutator, -} - -struct BodyMutationPassState<'tcx, 'cache> { - values: Vec, - instrumentation_data: InstrumentationData<'tcx, 'cache>, -} - -impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { - fn assign_ptr(body: &mut CachedBodyMutator, lvalue: Local, rvalue: Local, span: Span) { - let lvalue = Place::from(lvalue); - let rvalue = Rvalue::AddressOf(Mutability::Not, Place::from(rvalue)); - let kind = StatementKind::Assign(lvalue, rvalue); - body.insert_statement(Statement { kind, span }); - } - - /// Initialize the monitors - fn instrument_initialize(&mut self) -> Result<(), Error> { - let instance = - self.cache.register(&self.tcx, FunctionSignature::new("KaniInitializeSState", &[]))?; - let body = &mut self.body; - body.call(instance, [].to_vec(), body.unit); - Ok(()) - } - - /// For some local, say let x: T; - /// instrument it with the functions that initialize the stack: - /// let ptr_x: *const T = &raw const x; - /// initialize_local(ptr_x); - fn instrument_local(&mut self, local: usize) -> Result<(), Error> { - let ty = self.body.local(local).ty; - let ptr_ty = Ty::new_ptr(ty, Mutability::Not); - let span = self.body.span().clone(); - let body = &mut self.body; - let local_ptr = - self.meta_stack.entry(local).or_insert_with(|| body.new_local(ptr_ty, Mutability::Not)); - Self::assign_ptr(body, *local_ptr, local, span); - let instance = self.cache.register( - &self.tcx, - FunctionSignature::new("KaniInitializeLocal", &[GenericArgKind::Type(ty)]), - )?; - body.call(instance, [*local_ptr].to_vec(), body.unit); - Ok(()) - } - - fn instrument_new_stack_reference( - &mut self, - idx: &MutatorIndex, - lvalue: Local, - rvalue: Local, - ) -> Result<(), Error> { - // Initialize the constants - let ty = self.body.local(rvalue).ty; - let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); - let rvalue_ref = self.meta_stack.get(&rvalue).unwrap(); - let instance = self.cache.register( - &self.tcx, - FunctionSignature::new("KaniNewMutRefFromValue", &[GenericArgKind::Type(ty)]), - )?; - self.body.call(instance, vec![*lvalue_ref, *rvalue_ref], self.body.unit); - self.body.split(idx); - Ok(()) - } - - fn instrument_stack_check_ref( - &mut self, - idx: &MutatorIndex, - place: Local, - ty: Ty, - ) -> Result<(), Error> { - // Initialize the constants - let place_ref = self.meta_stack.get(&place).unwrap(); - let instance = self.cache.register( - &self.tcx, - FunctionSignature::new("KaniStackCheckRef", &[GenericArgKind::Type(ty)]), - )?; - self.body.call(instance, vec![*place_ref], self.body.unit); - self.body.split(idx); - Ok(()) - } - - fn instrument_stack_check_ptr( - &mut self, - idx: &MutatorIndex, - place: Local, - ty: Ty, - ) -> Result<(), Error> { - // Initialize the constants - let place_ref = self.meta_stack.get(&place).unwrap(); - let instance = self.cache.register( - &self.tcx, - FunctionSignature::new("KaniStackCheckPtr", &[GenericArgKind::Type(ty)]), - )?; - self.body.call(instance, vec![*place_ref], self.body.unit); - self.body.split(idx); - Ok(()) - } - - fn instrument_new_mut_ref_from_raw( - &mut self, - idx: &MutatorIndex, - created: Local, - raw: Local, - ) -> Result<(), Error> { - // Initialize the constants - let ty = self.body.local(created).ty; - let created_ref = self.meta_stack.get(&created).unwrap(); - let reference_ref = self.meta_stack.get(&raw).unwrap(); - let instance = self.cache.register( - &self.tcx, - FunctionSignature::new("KaniNewMutRefFromRaw", &[GenericArgKind::Type(ty)]), - )?; - self.body.call(instance, vec![*created_ref, *reference_ref], self.body.unit); - self.body.split(idx); - Ok(()) - } - - fn instrument_new_mut_raw_from_ref( - &mut self, - idx: &MutatorIndex, - created: Local, - reference: Local, - ) -> Result<(), Error> { - // Initialize the constants - let ty = self.body.local(created).ty; - let created_ref = self.meta_stack.get(&created).unwrap(); - let reference_ref = self.meta_stack.get(&reference).unwrap(); - let instance = self.cache.register( - &self.tcx, - FunctionSignature::new("KaniNewMutRawFromRef", &[GenericArgKind::Type(ty)]), - )?; - self.body.call(instance, vec![*created_ref, *reference_ref], self.body.unit); - self.body.split(idx); - Ok(()) - } - - fn instrument_index(&mut self, _values: &Vec, idx: &MutatorIndex) -> Result<(), Error> { - match self.body.inspect(idx) { - Instruction::Stmt(Statement { kind, .. }) => { - match kind { - StatementKind::Assign(to, rvalue) => { - let to = to.clone(); - match rvalue { - Rvalue::Ref(_, BorrowKind::Mut { .. }, from) => { - match from.projection[..] { - [] => { - // Direct reference to stack local - // x = &y - self.instrument_new_stack_reference( - idx, to.local, from.local, - )?; - } - [ProjectionElem::Deref] => { - // Reborrow - // x : &mut T = &*(y : *mut T) - let from = from.local; // Copy to avoid borrow - let to = to.local; // Copy to avoid borrow - match self.body.local(to).ty.kind() { - TyKind::RigidTy(RigidTy::Ref(_, _ty, _)) => { - eprintln!( - "Reborrow from reference not yet handled" - ); - } - TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { - self.instrument_stack_check_ref(idx, from, ty)?; - self.instrument_new_mut_ref_from_raw( - idx, to, from, - )?; - } - _ => {} - } - } - _ => { - eprintln!("Field projections not yet handled"); - } - } - } - Rvalue::AddressOf(Mutability::Mut, from) => { - match from.projection[..] { - [] => { - // x = &raw y - eprintln!("addr of not yet handled"); - } - [ProjectionElem::Deref] => { - // x = &raw mut *(y: &mut T) - let from = from.local; // Copy to avoid borrow - let to = to.local; // Copy to avoid borrow - match self.body.local(to).ty.kind() { - TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { - self.instrument_stack_check_ref(idx, from, ty)?; - self.instrument_new_mut_raw_from_ref( - idx, to, from, - )?; - } - TyKind::RigidTy(RigidTy::RawPtr(_ty, _)) => { - eprintln!( - "Pointer to pointer casts not yet handled" - ); - } - _ => {} - } - } - _ => {} - } - } - _ => { - eprintln!("Rvalue kind: {:?} not yet handled", rvalue); - } - } - match to.projection[..] { - [] => { - // Assignment directly to local - Ok(()) - } - [ProjectionElem::Deref] => { - // *x = rvalue - let to = to.local; - println!("Self body local to is: {:?}", self.body.local(to)); - match self.body.local(to).ty.kind() { - TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { - self.instrument_stack_check_ref(idx, to, ty)?; - } - TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { - self.instrument_stack_check_ptr(idx, to, ty)?; - } - _ => {} - } - Ok(()) - } - _ => { - eprintln!("Field assignment not yet handled"); - Ok(()) - } - } - } - // The following are not yet handled, however, no info is printed - // to avoid blowups: - StatementKind::Retag(_, _) => Ok(()), - StatementKind::FakeRead(_, _) => Ok(()), - StatementKind::SetDiscriminant { .. } => Ok(()), - StatementKind::Deinit(_) => Ok(()), - StatementKind::StorageLive(_) => Ok(()), - StatementKind::StorageDead(_) => Ok(()), - StatementKind::PlaceMention(_) => Ok(()), - StatementKind::AscribeUserType { .. } => Ok(()), - StatementKind::Coverage(_) => Ok(()), - StatementKind::Intrinsic(_) => Ok(()), - StatementKind::ConstEvalCounter => Ok(()), - StatementKind::Nop => Ok(()), - } - } - Instruction::Term(_) => Ok(()), - } - } - - fn instrument_locals(&mut self, values: &Vec) -> Result<(), Error> { - self.instrument_initialize()?; - for local in values { - self.instrument_local(*local)? - } - Ok(()) - } - - fn instrument_instructions(&mut self, values: &Vec) -> Result<(), Error> { - let mut index = self.body.new_index(); - let mut status = MutatorIndexStatus::Remaining; - while status == MutatorIndexStatus::Remaining { - self.instrument_index(values, &index)?; - status = self.body.decrement_index(&mut index); - } - Ok(()) - } -} - -impl<'tcx, 'cache> BodyMutationPassState<'tcx, 'cache> { - fn instrument_locals(&mut self) -> Result<(), Error> { - self.instrumentation_data.instrument_locals(&self.values) - } - - fn instrument_instructions(&mut self) -> Result<(), Error> { - self.instrumentation_data.instrument_instructions(&self.values)?; - Ok(()) - } - - fn finalize(mut self) -> Body { - self.instrument_locals().unwrap(); - self.instrumentation_data.body.finalize_prologue(); - self.instrument_instructions().unwrap(); - self.instrumentation_data.body.finalize() - } -} - -struct BodyMutator { - blocks: Vec, - locals: Vec, - arg_count: usize, - var_debug_info: Vec, - spread_arg: Option, - span: Span, - - ghost_locals: Vec, - ghost_blocks: Vec, - ghost_statements: Vec, -} - -struct CachedBodyMutator { - body: BodyMutator, - unit: Local, - cache: HashMap, -} - -impl BodyMutator { - fn new( - blocks: Vec, - locals: Vec, - arg_count: usize, - var_debug_info: Vec, - spread_arg: Option, - span: Span, - ghost_locals: Vec, - ghost_blocks: Vec, - statements: Vec, - ) -> Self { - BodyMutator { - blocks, - locals, - arg_count, - var_debug_info, - spread_arg, - span, - ghost_locals, - ghost_blocks, - ghost_statements: statements, - } - } - - fn gen_bb0(body: &mut Body) -> BasicBlock { - let target = body.blocks.len() + 1; - let kind = TerminatorKind::Goto { target }; - let span = body.span; - let terminator = Terminator { kind, span }; - let statements = Vec::new(); - std::mem::replace(&mut body.blocks[0], BasicBlock { statements, terminator }) - } - - fn gen_unit(body: &Body) -> LocalDecl { - let ty = Ty::new_tuple(&[]); - let span = body.span; - let mutability = Mutability::Not; - LocalDecl { ty, span, mutability } - } - - fn from(mut body: Body) -> Self { - let bb0 = Self::gen_bb0(&mut body); - body.blocks.push(bb0); - let ghost_locals = vec![Self::gen_unit(&body)]; - let ghost_blocks = vec![]; - let locals = body.locals().to_vec(); - let arg_count = body.arg_locals().len(); - let spread_arg = body.spread_arg(); - let debug_info = body.var_debug_info; - let statements = Vec::new(); - BodyMutator::new( - body.blocks, - locals, - arg_count, - debug_info, - spread_arg, - body.span, - ghost_locals, - ghost_blocks, - statements, - ) - } -} - -impl<'tcx, 'cache> LocalPassState<'tcx, 'cache> { - fn collect_body(self) -> BodyMutationPassState<'tcx, 'cache> { - let values = self.values; - let instrumentation_data = InstrumentationData { - tcx: self.tcx, - cache: self.cache, - meta_stack: HashMap::new(), - body: CachedBodyMutator::from(self.body), - }; - BodyMutationPassState { values, instrumentation_data } - } -} - -struct CollectLocalVisitor { - values: Vec, -} - -impl CollectLocalVisitor { - fn new() -> Self { - let values = Vec::new(); - CollectLocalVisitor { values } - } -} - -impl MirVisitor for CollectLocalVisitor { - fn visit_local_decl(&mut self, local: Local, decl: &LocalDecl) { - // // For now collect em all - let _ = decl; - self.values.push(local); - // if let TyKind::RigidTy(ty) = decl.ty.kind() { - // if function_ty(&ty) { - // eprintln!("WARN: Function types not yet supported ") - // } else if value_ty(&ty) { - // self.values.push(local); - // } else if !value_reference_ty(&ty) { - // panic!("Type {:?} not supported by the analysis.", ty); - // } - // } - } -} - -impl FunctionInstanceCache { - fn register(&mut self, ctx: &TyCtxt, sig: FunctionSignature) -> Result<&Instance, Error> { - let FunctionInstanceCache(cache) = self; - for i in 0..cache.len() { - if sig == cache[i].signature { - return Ok(&cache[i].instance); - } - } - let fndef = super::super::find_fn_def(*ctx, &sig.name) - .ok_or(Error::new(format!("Not found: {}", &sig.name)))?; - let instance = Instance::resolve(fndef, &GenericArgs(sig.args.clone()))?; - cache.push(FunctionInstance::new(sig, instance)); - Ok(&cache[cache.len() - 1].instance) - } - - #[allow(unused)] - fn get(&self, sig: &FunctionSignature) -> Result<&Instance, Error> { - let FunctionInstanceCache(cache) = self; - for FunctionInstance { signature, instance } in cache { - if *sig == *signature { - return Ok(instance); - } - } - Err(Error::new(format!("Not found: {:?}", sig))) - } -} - -impl CachedBodyMutator { - fn from(body: Body) -> Self { - let mut body = BodyMutator::from(body); - let unit = body.new_local(Ty::new_tuple(&[]), Mutability::Not); - let cache = HashMap::new(); - CachedBodyMutator { body, unit, cache } - } - - fn local(&self, idx: usize) -> &LocalDecl { - if idx > self.body.locals.len() { - &self.body.ghost_locals[idx - self.body.locals.len()] - } else { - &self.body.locals[idx] - } - } - - fn new_local(&mut self, ty: Ty, mutability: Mutability) -> Local { - self.body.new_local(ty, mutability) - } - - fn call(&mut self, callee: &Instance, args: Vec, local: Local) { - let func_local; - { - let cache = &mut self.cache; - let body = &mut self.body; - { - func_local = cache - .entry(*callee) - .or_insert_with(|| body.new_local(callee.ty(), Mutability::Not)); - } - } - self.body.call(*func_local, args, local); - } - - fn finalize_prologue(&mut self) { - self.body.finalize_prologue(); - } - - fn insert_statement(&mut self, stmt: Statement) { - self.body.ghost_statements.push(stmt); - } - - fn new_index(&mut self) -> MutatorIndex { - self.body.new_index() - } - - fn decrement_index(&mut self, idx: &mut MutatorIndex) -> MutatorIndexStatus { - self.body.decrement(idx) - } - - fn split(&mut self, idx: &MutatorIndex) { - self.body.split(idx); - } - - fn inspect(&self, idx: &MutatorIndex) -> Instruction { - self.body.inspect(idx) - } - - fn finalize(self) -> Body { - self.body.finalize() - } - - fn span(&self) -> Span { - self.body.span - } -} - -#[derive(Debug)] -struct MutatorIndex { - bb: BasicBlockIdx, - idx: usize, - span: Span, -} - -#[derive(PartialEq, Eq)] -enum MutatorIndexStatus { - Remaining, - Done, -} - -enum Instruction<'a> { - Stmt(&'a Statement), - #[allow(unused)] - Term(&'a Terminator), -} - -impl BodyMutator { - fn new_local(&mut self, ty: Ty, mutability: Mutability) -> Local { - let span = self.span; - let decl = LocalDecl { ty, span, mutability }; - let local = self.locals.len() + self.ghost_locals.len(); - self.ghost_locals.push(decl); - local - } - - fn call(&mut self, callee: Local, args: Vec, local: Local) { - let projection = Vec::new(); - let destination = Place { local, projection }; - let args = args - .into_iter() - .map(|v| Operand::Copy(Place { local: v, projection: vec![] })) - .collect(); - let func = Operand::Copy(Place::from(callee)); - let unwind = UnwindAction::Terminate; - let target = Some(self.next_block()); - let kind = TerminatorKind::Call { func, args, destination, target, unwind }; - let span = self.span; - let terminator = Terminator { kind, span }; - let statements = std::mem::replace(&mut self.ghost_statements, Vec::new()); - self.ghost_blocks.push(BasicBlock { statements, terminator }); - } - - fn finalize_prologue(&mut self) { - let kind = TerminatorKind::Goto { target: self.blocks.len() - 1 }; - let span = self.span; - let terminator = Terminator { kind, span }; - self.insert_bb(terminator); - } - - fn new_index(&self) -> MutatorIndex { - let len = self.blocks.len(); - let bb = std::cmp::max(len, 1) - 1; - let idx = if len > 0 { std::cmp::max(self.blocks[bb].statements.len(), 1) - 1 } else { 0 }; - let span = self.span; - MutatorIndex { bb, idx, span } - } - - fn decrement(&self, index: &mut MutatorIndex) -> MutatorIndexStatus { - let mut status = MutatorIndexStatus::Done; - if index.idx > 0 || index.bb > 0 { - status = MutatorIndexStatus::Remaining; - } - if index.idx > 0 { - if index.idx < self.blocks[index.bb].statements.len() { - index.span = self.blocks[index.bb].statements[index.idx].span; - } else { - index.span = self.blocks[index.bb].terminator.span; - } - index.idx -= 1; - } else if index.bb > 0 { - index.bb -= 1; - index.span = self.blocks[index.bb].terminator.span; - index.idx = self.blocks[index.bb].statements.len() - } - status - } - - fn inspect(&self, index: &MutatorIndex) -> Instruction { - if index.idx >= self.blocks[index.bb].statements.len() { - Instruction::Term(&self.blocks[index.bb].terminator) - } else { - Instruction::Stmt(&self.blocks[index.bb].statements[index.idx]) - } - } - - fn split(&mut self, index: &MutatorIndex) { - let kind = TerminatorKind::Goto { target: self.blocks.len() + self.ghost_blocks.len() - 1 }; - let span = index.span; - let term = Terminator { kind, span }; - let len = self.blocks[index.bb].statements.len(); - if index.idx < len { - self.ghost_statements.extend(self.blocks[index.bb].statements.split_off(index.idx + 1)); - } - let term = std::mem::replace(&mut self.blocks[index.bb].terminator, term); - self.insert_bb(term); - } - - fn next_block(&self) -> usize { - self.blocks.len() + self.ghost_blocks.len() + 1 - } - - fn insert_bb(&mut self, terminator: Terminator) { - let statements = std::mem::replace(&mut self.ghost_statements, Vec::new()); - let execute_original_body = BasicBlock { statements, terminator }; - self.ghost_blocks.push(execute_original_body); - } - - fn finalize(self) -> Body { - match self { - BodyMutator { - mut blocks, - mut locals, - arg_count, - var_debug_info, - spread_arg, - span, - ghost_locals, - ghost_blocks, - ghost_statements, - } => { - assert!(ghost_statements.len() == 0); - blocks.extend(ghost_blocks.into_iter()); - locals.extend(ghost_locals.into_iter()); - Body::new(blocks, locals, arg_count, var_debug_info, spread_arg, span) - } - } - } -} From e6126f4ee9c2f3b7d005e1f78afd7f9c69919f25 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 15 Aug 2024 10:46:53 -0400 Subject: [PATCH 19/72] Add tests for C99 format locals and for control flow. The control flow graph of the MIR produced from the control_flow.rs file appears correct on manual inspection. --- .../transform/check_aliasing/mod.rs | 1 + tests/expected/aliasing/control_flow.expected | 1 + tests/expected/aliasing/control_flow.rs | 28 +++++++++++++++++++ .../duplicate_write_compressed.expected | 1 + .../aliasing/duplicate_write_compressed.rs | 14 ++++++++++ 5 files changed, 45 insertions(+) create mode 100644 tests/expected/aliasing/control_flow.expected create mode 100644 tests/expected/aliasing/control_flow.rs create mode 100644 tests/expected/aliasing/duplicate_write_compressed.expected create mode 100644 tests/expected/aliasing/duplicate_write_compressed.rs diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 90b3e01d6f85..7e5861421695 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -32,6 +32,7 @@ use stable_mir::mir::{ }; use stable_mir::ty::{RigidTy, Span, Ty, TyKind}; use std::fmt::Debug; +use std::io::stderr; use tracing::trace; /// Instrument the code with checks for aliasing model diff --git a/tests/expected/aliasing/control_flow.expected b/tests/expected/aliasing/control_flow.expected new file mode 100644 index 000000000000..35a510028c4e --- /dev/null +++ b/tests/expected/aliasing/control_flow.expected @@ -0,0 +1 @@ +Failed Checks: Stack violated. diff --git a/tests/expected/aliasing/control_flow.rs b/tests/expected/aliasing/control_flow.rs new file mode 100644 index 000000000000..60e5ba059776 --- /dev/null +++ b/tests/expected/aliasing/control_flow.rs @@ -0,0 +1,28 @@ +// kani-flags: -Zghost-state -Zaliasing + +#[kani::proof] +fn main() { + let mut local: i32 = 10; + let mut referent_1: i32 = 0; + let mut referent_2: i32 = 0; + let mut ref_from_raw_1: &mut i32 = &mut referent_1; + #[allow(unused)] + let mut ref_from_raw_2: &mut i32 = &mut referent_2; + let raw_pointer: *mut i32 = &mut local as *mut i32; + let mut state = false; + let mut iters = 0; + unsafe { + while iters < 2 { + if state { + ref_from_raw_1 = &mut *raw_pointer; + *ref_from_raw_1 = 0; + } else { + ref_from_raw_2 = &mut *raw_pointer; + *ref_from_raw_2 = 1; + *ref_from_raw_1 = 2; + } + state = true; + iters += 1; + } + } +} diff --git a/tests/expected/aliasing/duplicate_write_compressed.expected b/tests/expected/aliasing/duplicate_write_compressed.expected new file mode 100644 index 000000000000..35a510028c4e --- /dev/null +++ b/tests/expected/aliasing/duplicate_write_compressed.expected @@ -0,0 +1 @@ +Failed Checks: Stack violated. diff --git a/tests/expected/aliasing/duplicate_write_compressed.rs b/tests/expected/aliasing/duplicate_write_compressed.rs new file mode 100644 index 000000000000..1703b4799ee5 --- /dev/null +++ b/tests/expected/aliasing/duplicate_write_compressed.rs @@ -0,0 +1,14 @@ +// kani-flags: -Zghost-state -Zaliasing + +#[kani::proof] +fn main() { + let mut local: i32 = 0; + let raw_pointer = &mut local as *mut i32; + unsafe { + let ref_from_raw_1 = &mut *raw_pointer; + *ref_from_raw_1 = 0; + let ref_from_raw_2 = &mut *raw_pointer; + *ref_from_raw_2 = 1; + *ref_from_raw_1 = 2; + } +} From 26623ce8e40472a2ae44c51a761803cc05e067e4 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 15 Aug 2024 12:00:20 -0400 Subject: [PATCH 20/72] Refactor imports --- .../transform/check_aliasing/body_mutation.rs | 4 ++- .../transform/check_aliasing/body_mutator.rs | 6 ++-- .../check_aliasing/cached_body_mutator.rs | 5 +++- .../check_aliasing/function_cache.rs | 30 ++++++++++++++----- .../check_aliasing/instrumentation.rs | 17 ++++++----- .../check_aliasing/local_collection.rs | 5 ++-- .../transform/check_aliasing/mod.rs | 18 +++++------ 7 files changed, 51 insertions(+), 34 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs index e1b7b73dca30..b1c8487c35f5 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs @@ -1,4 +1,6 @@ -use super::{Local, InstrumentationData, MirError, Body}; +use stable_mir::mir::{Local, Body}; +use super::MirError; +use super::InstrumentationData; pub struct BodyMutationPassState<'tcx, 'cache> { values: Vec, diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs index 1e94743faf4d..a19f2d2a0d2d 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs @@ -1,7 +1,5 @@ -use super::{ - BasicBlock, BasicBlockIdx, Body, Local, LocalDecl, Mutability, Operand, Place, - Span, Statement, Terminator, TerminatorKind, Ty, UnwindAction, VarDebugInfo, -}; +use stable_mir::mir::{BasicBlock, BasicBlockIdx, Body, Local, LocalDecl, Mutability, Operand, Place, Statement, Terminator, TerminatorKind, UnwindAction, VarDebugInfo}; +use stable_mir::ty::{Ty, Span}; /// BodyMutator combines the data of the function body /// with "ghost" basic block and local data, allowing the user diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs index 441b1a3862c7..9c6e22c0fe24 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs @@ -1,5 +1,8 @@ use std::collections::HashMap; -use super::{BodyMutator, Local, MirInstance, Mutability, LocalDecl, Body, Ty, Statement, MutatorIndex, MutatorIndexStatus, Instruction, Span}; +use super::{MirInstance, BodyMutator}; +use stable_mir::mir::{Local, Mutability, LocalDecl, Statement, Body}; +use stable_mir::ty::{Ty, Span}; +use super::{MutatorIndex, MutatorIndexStatus, Instruction}; /// Body mutator which wraps the BodyMutator /// interface with a cache of the locals that diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs index 1a10b85f7029..3134fb8c7325 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs @@ -1,18 +1,23 @@ -pub use stable_mir::ty::GenericArgKind as GenericArg; -pub use stable_mir::ty::GenericArgs; -use super::{MirInstance, MirError, TyCtxt, super::super::find_fn_def}; +use super::{MirInstance, MirError}; +use rustc_middle::ty::TyCtxt; +use stable_mir::ty::{GenericArgKind as GenericArg, GenericArgs}; +use crate::kani_middle::find_fn_def; + /// FunctionSignature encapsulates the data /// for rust functions with generic arguments /// to ensure that it can be cached. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Signature { - name: String, + /// The diagnostic string associated with the function + diagnostic: String, + /// The generic arguments applied args: Vec, } impl Signature { + /// Create a new signature from the name and args pub fn new(name: &str, args: &[GenericArg]) -> Signature { - Signature { name: name.to_string(), args: args.to_vec() } + Signature { diagnostic: name.to_string(), args: args.to_vec() } } } @@ -21,11 +26,17 @@ impl Signature { /// generic arguments to ensure that it can be cached. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Instance { + /// The "key" with which the function instance + /// is created, and with which the function instance + /// can be looked up signature: Signature, + /// The "value", the resolved function instance itself instance: MirInstance, } impl Instance { + /// Create a new cacheable instance with the given signature and + /// instance pub fn new(signature: Signature, instance: super::MirInstance) -> Instance { Instance { signature, instance } } @@ -34,10 +45,13 @@ impl Instance { /// Caches function instances for later lookups. #[derive(Default, Debug)] pub struct Cache { + /// The cache cache: Vec, } impl Cache { + /// Register the signature the to the cache + /// in the given compilation context, ctx pub fn register(&mut self, ctx: &TyCtxt, sig: Signature) -> Result<&MirInstance, MirError> { let Cache { cache } = self; @@ -46,14 +60,14 @@ impl Cache { return Ok(&cache[i].instance); } } - let fndef = find_fn_def(*ctx, &sig.name) - .ok_or(MirError::new(format!("Not found: {}", &sig.name)))?; + let fndef = find_fn_def(*ctx, &sig.diagnostic) + .ok_or(MirError::new(format!("Not found: {}", &sig.diagnostic)))?; let instance = super::MirInstance::resolve(fndef, &GenericArgs(sig.args.clone()))?; cache.push(Instance::new(sig, instance)); Ok(&cache[cache.len() - 1].instance) } - #[allow(unused)] + /// Fetch the signature sig from the cache fn get(&self, sig: &Signature) -> Result<&MirInstance, MirError> { let Cache { cache } = self; for Instance { signature, instance } in cache { diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index c77cea803df3..58487b69ba4b 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -1,5 +1,8 @@ use std::collections::HashMap; -use super::{TyCtxt, CachedBodyMutator, Cache, Local, Place, Rvalue, Mutability, StatementKind, Statement, MirError, Signature, Span, Ty, GenericArg, MutatorIndex, Instruction, BorrowKind, ProjectionElem, TyKind, RigidTy, MutatorIndexStatus}; +use rustc_middle::ty::TyCtxt; +use stable_mir::mir::{Local, Place, Rvalue, Mutability, StatementKind, Statement, BorrowKind, ProjectionElem}; +use stable_mir::ty::{GenericArgKind, Ty, Span, TyKind, RigidTy}; +use super::{MirError, CachedBodyMutator, Cache, Signature, MutatorIndex, Instruction, MutatorIndexStatus}; pub struct InstrumentationData<'tcx, 'cache> { tcx: TyCtxt<'tcx>, @@ -45,7 +48,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let instance = self.cache.register( &self.tcx, Signature::new("KaniInitializeLocal", - &[GenericArg::Type(ty)]))?; + &[GenericArgKind::Type(ty)]))?; body.call(instance, [*local_ptr].to_vec(), body.unit()); Ok(()) } @@ -66,7 +69,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let rvalue_ref = self.meta_stack.get(&rvalue).unwrap(); let instance = self.cache.register( &self.tcx, - Signature::new("KaniNewMutRefFromValue", &[GenericArg::Type(ty)]), + Signature::new("KaniNewMutRefFromValue", &[GenericArgKind::Type(ty)]), )?; self.body.call(instance, vec![*lvalue_ref, *rvalue_ref], self.body.unit()); self.body.split(idx); @@ -85,7 +88,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let place_ref = self.meta_stack.get(&place).unwrap(); let instance = self.cache.register( &self.tcx, - Signature::new("KaniStackCheckRef", &[GenericArg::Type(ty)]), + Signature::new("KaniStackCheckRef", &[GenericArgKind::Type(ty)]), )?; self.body.call(instance, vec![*place_ref], self.body.unit()); self.body.split(idx); @@ -104,7 +107,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let place_ref = self.meta_stack.get(&place).unwrap(); let instance = self.cache.register( &self.tcx, - Signature::new("KaniStackCheckPtr", &[GenericArg::Type(ty)]), + Signature::new("KaniStackCheckPtr", &[GenericArgKind::Type(ty)]), )?; self.body.call(instance, vec![*place_ref], self.body.unit()); self.body.split(idx); @@ -125,7 +128,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let reference_ref = self.meta_stack.get(&raw).unwrap(); let instance = self.cache.register( &self.tcx, - Signature::new("KaniNewMutRefFromRaw", &[GenericArg::Type(ty)]), + Signature::new("KaniNewMutRefFromRaw", &[GenericArgKind::Type(ty)]), )?; self.body.call(instance, vec![*created_ref, *reference_ref], self.body.unit()); self.body.split(idx); @@ -146,7 +149,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let reference_ref = self.meta_stack.get(&reference).unwrap(); let instance = self.cache.register( &self.tcx, - Signature::new("KaniNewMutRawFromRef", &[GenericArg::Type(ty)]), + Signature::new("KaniNewMutRawFromRef", &[GenericArgKind::Type(ty)]), )?; self.body.call(instance, vec![*created_ref, *reference_ref], self.body.unit()); self.body.split(idx); diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/local_collection.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/local_collection.rs index 80d587aefd5b..0a0144a7a7d5 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/local_collection.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/local_collection.rs @@ -1,7 +1,8 @@ use super::function_cache::*; +use stable_mir::mir::{Body, Local, LocalDecl, MirVisitor}; +use rustc_middle::ty::TyCtxt; use super::{ - Body, BodyMutationPassState, CachedBodyMutator, InstrumentationData, Local, LocalDecl, - MirVisitor, TyCtxt, + BodyMutationPassState, CachedBodyMutator, InstrumentationData, }; use std::collections::HashMap; diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 7e5861421695..9f9aaa412397 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -4,8 +4,12 @@ //! Implement a pass that instruments code with assertions //! that will fail when the aliasing model is violated. -use stable_mir::Error as MirError; -use stable_mir::mir::mono::Instance as MirInstance; +// Reimport components of mir that conflict with +// parts of the sub-pass's API. +pub use stable_mir::Error as MirError; +pub use stable_mir::mir::mono::Instance as MirInstance; + + mod function_cache; use function_cache::*; mod local_collection; @@ -22,17 +26,9 @@ use instrumentation::*; use crate::args::ExtraChecks; use crate::kani_middle::transform::{TransformPass, TransformationType}; use crate::kani_queries::QueryDb; +use stable_mir::mir::Body; use rustc_middle::ty::TyCtxt; -use stable_mir::mir::{ - BasicBlock, BorrowKind, MirVisitor, Operand, ProjectionElem, Rvalue, Statement, StatementKind, - Terminator, VarDebugInfo, -}; -use stable_mir::mir::{ - BasicBlockIdx, Body, Local, LocalDecl, Mutability, Place, TerminatorKind, UnwindAction, -}; -use stable_mir::ty::{RigidTy, Span, Ty, TyKind}; use std::fmt::Debug; -use std::io::stderr; use tracing::trace; /// Instrument the code with checks for aliasing model From 5155f36fe1a7c09b9567e44b661d66d04e18b1d0 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 15 Aug 2024 12:32:44 -0400 Subject: [PATCH 21/72] Factor out local collection; local indices now computed directly --- .../transform/check_aliasing/body_mutation.rs | 13 ++-- .../transform/check_aliasing/body_mutator.rs | 15 +++++ .../check_aliasing/cached_body_mutator.rs | 10 +++ .../check_aliasing/function_cache.rs | 7 +- .../check_aliasing/instrumentation.rs | 19 ++++-- .../check_aliasing/local_collection.rs | 65 ------------------- .../transform/check_aliasing/mod.rs | 7 +- .../aliasing/multiple_referents.expected | 1 + tests/expected/aliasing/multiple_referents.rs | 28 ++++++++ 9 files changed, 83 insertions(+), 82 deletions(-) delete mode 100644 kani-compiler/src/kani_middle/transform/check_aliasing/local_collection.rs create mode 100644 tests/expected/aliasing/multiple_referents.expected create mode 100644 tests/expected/aliasing/multiple_referents.rs diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs index b1c8487c35f5..baaad0528830 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs @@ -1,19 +1,18 @@ -use stable_mir::mir::{Local, Body}; +use stable_mir::mir::Body; use super::MirError; use super::InstrumentationData; pub struct BodyMutationPassState<'tcx, 'cache> { - values: Vec, instrumentation_data: InstrumentationData<'tcx, 'cache>, } impl<'tcx, 'cache> BodyMutationPassState<'tcx, 'cache> { - pub fn new(values: Vec, instrumentation_data: InstrumentationData<'tcx, 'cache>) -> Self { - BodyMutationPassState { values, instrumentation_data } + pub fn new(instrumentation_data: InstrumentationData<'tcx, 'cache>) -> Self { + BodyMutationPassState { instrumentation_data } } pub fn instrument_locals(&mut self) -> Result<(), MirError> { - self.instrumentation_data.instrument_locals(&self.values) + self.instrumentation_data.instrument_locals() } pub fn instrument_instructions(&mut self) -> Result<(), MirError> { @@ -23,8 +22,8 @@ impl<'tcx, 'cache> BodyMutationPassState<'tcx, 'cache> { pub fn finalize(mut self) -> Body { self.instrument_locals().unwrap(); - self.instrumentation_data.body.finalize_prologue(); + self.instrumentation_data.finalize_prologue(); self.instrument_instructions().unwrap(); - self.instrumentation_data.body.finalize() + self.instrumentation_data.finalize() } } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs index a19f2d2a0d2d..e3f452de48cb 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs @@ -88,6 +88,16 @@ impl BodyMutator { ) } + /// Locals in `self` that correspond to this function's arguments. + pub fn arg_locals(&self) -> &[LocalDecl] { + &self.locals[1..][..self.arg_count] + } + + // Get the inner locals + pub fn inner_locals(&self) -> &[LocalDecl] { + &self.locals[self.arg_count + 1..] + } + /// Index into the locals pub fn local(&self, idx: usize) -> &LocalDecl { if idx > self.locals.len() { @@ -229,6 +239,11 @@ impl BodyMutator { pub fn span(&self) -> Span { self.span } + + /// Get the locals + pub fn locals(&self) -> &[LocalDecl] { + &self.locals + } } /// Mutator index with which to iterate over the function body. diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs index 9c6e22c0fe24..3f1ef61bd078 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs @@ -32,6 +32,16 @@ impl CachedBodyMutator { self.body.new_local(ty, mutability) } + /// Locals in `self` that correspond to this function's arguments. + pub fn arg_locals(&self) -> &[LocalDecl] { + self.body.arg_locals() + } + + /// Locals + pub fn locals(&self) -> &[LocalDecl] { + self.body.locals() + } + /// Insert a call to the function stored at local with the args /// stored at args pub fn call(&mut self, callee: &MirInstance, args: Vec, local: Local) { diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs index 3134fb8c7325..36458f2ad3b3 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs @@ -50,6 +50,11 @@ pub struct Cache { } impl Cache { + /// Instantiate the cache + pub fn new() -> Self { + Cache::default() + } + /// Register the signature the to the cache /// in the given compilation context, ctx pub fn register(&mut self, ctx: &TyCtxt, sig: Signature) -> @@ -68,7 +73,7 @@ impl Cache { } /// Fetch the signature sig from the cache - fn get(&self, sig: &Signature) -> Result<&MirInstance, MirError> { + pub fn get(&self, sig: &Signature) -> Result<&MirInstance, MirError> { let Cache { cache } = self; for Instance { signature, instance } in cache { if *sig == *signature { diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 58487b69ba4b..21380ddbc3db 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -8,11 +8,12 @@ pub struct InstrumentationData<'tcx, 'cache> { tcx: TyCtxt<'tcx>, cache: &'cache mut Cache, meta_stack: HashMap, - pub body: CachedBodyMutator, + body: CachedBodyMutator, } impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { - pub fn new(tcx: TyCtxt<'tcx>, cache: &'cache mut Cache, meta_stack: HashMap, body: CachedBodyMutator) -> Self { + pub fn new(tcx: TyCtxt<'tcx>, cache: &'cache mut Cache, body: CachedBodyMutator) -> Self { + let meta_stack = HashMap::new(); InstrumentationData { tcx, cache, meta_stack, body } } @@ -280,10 +281,10 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// Instrument each of the locals collected into values with /// initialization data. - pub fn instrument_locals(&mut self, values: &Vec) -> Result<(), MirError> { + pub fn instrument_locals(&mut self) -> Result<(), MirError> { self.instrument_initialize()?; - for local in values { - self.instrument_local(*local)? + for local in (self.body.arg_locals().len() + 1)..self.body.locals().len() { + self.instrument_local(local)? } Ok(()) } @@ -300,4 +301,12 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { } Ok(()) } + + pub fn finalize_prologue(&mut self) { + self.body.finalize_prologue(); + } + + pub fn finalize(self) -> stable_mir::mir::Body { + self.body.finalize() + } } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/local_collection.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/local_collection.rs deleted file mode 100644 index 0a0144a7a7d5..000000000000 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/local_collection.rs +++ /dev/null @@ -1,65 +0,0 @@ -use super::function_cache::*; -use stable_mir::mir::{Body, Local, LocalDecl, MirVisitor}; -use rustc_middle::ty::TyCtxt; -use super::{ - BodyMutationPassState, CachedBodyMutator, InstrumentationData, -}; -use std::collections::HashMap; - -/// Collect local visitor visits the body -/// and collects all of the (non argument) -/// locals. In the future it will collect -/// argument locals and initialize arguments. -struct CollectLocalVisitor { - values: Vec, -} - -impl CollectLocalVisitor { - fn new() -> Self { - let values = Vec::new(); - CollectLocalVisitor { values } - } -} - -impl MirVisitor for CollectLocalVisitor { - fn visit_local_decl(&mut self, local: Local, decl: &LocalDecl) { - let _ = decl; - self.values.push(local); - } -} - -/// The local collection pass state collects locals -/// from function bodies based on whether they are -/// function arguments or locals from the function body. -pub struct LocalCollectionPassState<'tcx, 'cache> { - /// The function body - body: Body, - /// The compilation context - tcx: TyCtxt<'tcx>, - /// The function instance cache, which may - /// be populated by previous runs of the aliasing - /// pass. - cache: &'cache mut Cache, - /// Values - values: CollectLocalVisitor, -} - -impl<'tcx, 'cache> LocalCollectionPassState<'tcx, 'cache> { - pub fn new(body: Body, tcx: TyCtxt<'tcx>, cache: &'cache mut Cache) -> Self { - let values = CollectLocalVisitor::new(); - Self { body, tcx, cache, values } - } - - pub fn collect_locals(mut self) -> Self { - self.values.visit_body(&self.body); - self - } - - pub fn collect_body(self) -> BodyMutationPassState<'tcx, 'cache> { - let values = self.values.values; - let body = CachedBodyMutator::from(self.body); - let instrumentation_data = - InstrumentationData::new(self.tcx, self.cache, HashMap::new(), body); - BodyMutationPassState::new(values, instrumentation_data) - } -} diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 9f9aaa412397..6781b59a9379 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -12,8 +12,6 @@ pub use stable_mir::mir::mono::Instance as MirInstance; mod function_cache; use function_cache::*; -mod local_collection; -use local_collection::*; mod cached_body_mutator; use cached_body_mutator::*; mod body_mutator; @@ -132,8 +130,9 @@ impl TransformPass for AliasingPass { { (false, body) } else { - let pass = LocalCollectionPassState::new(body, tcx, &mut self.cache); - let out = pass.collect_locals().collect_body().finalize(); + let body = CachedBodyMutator::from(body); + let instrumentation_data = InstrumentationData::new(tcx, &mut self.cache, body); + let out = BodyMutationPassState::new(instrumentation_data).finalize(); (true, out) } } diff --git a/tests/expected/aliasing/multiple_referents.expected b/tests/expected/aliasing/multiple_referents.expected new file mode 100644 index 000000000000..35a510028c4e --- /dev/null +++ b/tests/expected/aliasing/multiple_referents.expected @@ -0,0 +1 @@ +Failed Checks: Stack violated. diff --git a/tests/expected/aliasing/multiple_referents.rs b/tests/expected/aliasing/multiple_referents.rs new file mode 100644 index 000000000000..6f60f9a71e27 --- /dev/null +++ b/tests/expected/aliasing/multiple_referents.rs @@ -0,0 +1,28 @@ +// kani-flags: -Zghost-state -Zaliasing + +#[kani::proof] +fn main() { + let mut local: i32 = 10; + let mut referent_1: i32 = 0; + let mut referent_2: i32 = 0; + let mut ref_from_raw_1: &mut i32 = &mut referent_1; + let mut ref_from_raw_2: &mut i32 = &mut referent_2; + let temp_ref: &mut i32 = &mut local; + let raw_pointer: *mut i32 = &mut local as *mut i32; + let mut state = false; + let mut iters = 0; + unsafe { + while iters < 2 { + if state { + ref_from_raw_1 = &mut *raw_pointer; + *ref_from_raw_1 = 0; + } else { + ref_from_raw_2 = &mut *raw_pointer; + *ref_from_raw_2 = 1; + *ref_from_raw_1 = 2; + } + state = true; + iters += 1; + } + } +} From a0fe5599c25ac1edcbe95c14ee1c3500e9dbba1c Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 15 Aug 2024 12:44:25 -0400 Subject: [PATCH 22/72] Remove pub(self) --- library/kani/src/aliasing.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 1efaa72b9d87..ca82985982a3 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -18,19 +18,19 @@ pub mod sstate { struct Access; #[allow(unused)] impl Access { - pub(self) const READ: bool = false; - pub(self) const WRITE: bool = true; + const READ: bool = false; + const WRITE: bool = true; } #[non_exhaustive] struct Permission; impl Permission { - pub(self) const UNIQUE: u8 = 0; - pub(self) const SHAREDRW: u8 = 1; - pub(self) const SHAREDRO: u8 = 2; - pub(self) const DISABLED: u8 = 3; + const UNIQUE: u8 = 0; + const SHAREDRW: u8 = 1; + const SHAREDRO: u8 = 2; + const DISABLED: u8 = 3; - pub(self) fn grants(access: bool, tag: u8) -> bool { + fn grants(access: bool, tag: u8) -> bool { tag != Self::DISABLED && (access != Access::WRITE || tag != Self::SHAREDRO) } } @@ -49,8 +49,8 @@ pub mod sstate { #[non_exhaustive] struct MonitorState; impl MonitorState { - pub(self) const UNINIT: bool = false; - pub(self) const INIT: bool = true; + const UNINIT: bool = false; + const INIT: bool = true; } use super::*; From ef833b2ab82517aa3509c84923c93ebbe9057120 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 15 Aug 2024 13:29:06 -0400 Subject: [PATCH 23/72] Add documentation for the module sstate. --- library/kani/src/aliasing.rs | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index ca82985982a3..2ca0df47b5f4 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -7,6 +7,53 @@ use crate::shadow::ShadowMem; use crate::{any, assume}; /// The stacked borrows state. +/// +/// The stacked borrows state associates every pointer value +/// (IE reference or raw pointer) with a unique tag and permission +/// in shadow memory. +/// +/// The tags correspond to the time of the creation of the pointer +/// value, and the permissions correspond to the mutability +/// of the pointer value and its status as a raw pointer or reference. +/// +/// It also associates each byte of the program's memory +/// with a stack of tags, tracking the borrows of the memory +/// containing that byte in temporal order. Every time a +/// pointer value is used, the stack is popped down to that pointer value's +/// tag, effectively marking the borrows that occur after that variable +/// as dead. If the borrows associated with the tags popped are later used, +/// the search for them at that byte fails and the stacked borrows state +/// is considered violated. +/// +/// For example: +/// ```rust +/// let mut x: i32 = 10; +/// // Stack allocate 10 and store it at x. +/// // Stack at addr_of!(x) through addr_of!(x) + 4: +/// // [(0, Permission::UNIQUE)] +/// let y = &mut x; +/// // Make the pointer object `&mut x`. Associate `&mut x` +/// // with the tag and permission `(1, Permission::UNIQUE)` +/// // by associating `addr_of!(y)` with `(1, Permission::UNIQUE)` +/// // in shadow memory. Push the tag to the borrow stacks of +/// // `addr_of!(x)` through `addr_of!(x) + 4` yielding +/// // the stacks [(0, Permission::UNIQUE), (1, Permission::UNIQUE)] +/// let z = y as *mut i32; +/// // Make the pointer object `y as *mut i32`. +/// // associate `addr_of!(z)` and push the stacks as +/// // above with the tag (2, Permission::SHAREDRW), +/// // corresponding to a raw pointer, yielding the stacks +/// // [(0, Permission::UNIQUE), (1, Permission::UNIQUE), +/// // (2, Permission::SHAREDRW)]. +/// *y = 10; +/// // Pop the stack down to the tag associated with the pointer +/// // object created at `&mut x` yielding +/// // [(0, Permission::UNIQUE), (1, Permission::UNIQUE)] +/// unsafe { *(&mut *z) = 10; } +/// // Run stack lookup on the tag associated with the pointer +/// // object created at `y as *mut i32`, ie, (2, Permission::SHAREDRW) +/// // resulting in an error. +/// ``` pub mod sstate { use super::*; /// Associate every pointer object with a tag From 553c737f41e053f967638738f2905d118efdb389 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 15 Aug 2024 13:32:37 -0400 Subject: [PATCH 24/72] Add demonic nondeterminism blurb --- library/kani/src/aliasing.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 2ca0df47b5f4..6db2060aacf0 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -54,6 +54,13 @@ use crate::{any, assume}; /// // object created at `y as *mut i32`, ie, (2, Permission::SHAREDRW) /// // resulting in an error. /// ``` +/// When demonic nondeterminism is used (currently it is always used), +/// a nondeterminism oracle is queried to select a single byte of the program's +/// memory. This way, if a single byte is ever invalid, the nondeterminism +/// oracle will select it, and allow an error to be thrown. +/// This can be used with the restriction that assertions over +/// relations between the stacks (such as, for example, equality between +/// the top two tags of two different stacks) are never needed. pub mod sstate { use super::*; /// Associate every pointer object with a tag From 5df2dbc2270ad519fbe9d2cf973ee7979713afe1 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 15 Aug 2024 15:26:33 -0400 Subject: [PATCH 25/72] provisionally add line numbers; tests failing --- .../transform/check_aliasing/body_mutator.rs | 30 ++++++++++++- .../check_aliasing/cached_body_mutator.rs | 23 +++++++++- .../check_aliasing/function_cache.rs | 21 ++++++++- .../check_aliasing/instrumentation.rs | 43 +++++++++++++------ library/kani/src/aliasing.rs | 12 +++++- tests/expected/aliasing/duplicate_write.rs | 2 - 6 files changed, 110 insertions(+), 21 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs index e3f452de48cb..e73cc5b6f533 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs @@ -1,5 +1,5 @@ -use stable_mir::mir::{BasicBlock, BasicBlockIdx, Body, Local, LocalDecl, Mutability, Operand, Place, Statement, Terminator, TerminatorKind, UnwindAction, VarDebugInfo}; -use stable_mir::ty::{Ty, Span}; +use stable_mir::mir::{BasicBlock, BasicBlockIdx, Body, Local, LocalDecl, Mutability, Operand, Place, Statement, Terminator, TerminatorKind, UnwindAction, VarDebugInfo, ConstOperand}; +use stable_mir::ty::{Ty, MirConst, Span}; /// BodyMutator combines the data of the function body /// with "ghost" basic block and local data, allowing the user @@ -116,6 +116,26 @@ impl BodyMutator { local } + /// Call the assertion function at assert_fn, returning into the unit, + /// with condition cond and message string. + pub fn assert(&mut self, assert_fn: Local, unit: Local, cond: Local, message: String, span: Span) { + let user_ty = None; + let const_ = MirConst::from_str(&message); + let message = Operand::Constant(ConstOperand { span, user_ty, const_ }); + let args = vec![Operand::Copy(Place::from(cond)), + message]; + let func = Operand::Copy(Place::from(assert_fn)); + let unwind = UnwindAction::Terminate; + let target = Some(self.next_block()); + let projection = Vec::new(); + let destination = Place { local: unit, projection }; + let kind = TerminatorKind::Call { func, args, destination, target, unwind }; + let span = self.span; + let terminator = Terminator { kind, span }; + let statements = std::mem::replace(&mut self.ghost_statements, Vec::new()); + self.ghost_blocks.push(BasicBlock { statements, terminator }); + } + /// Insert a call into the function body of the function stored at /// callee with the arguments in args. pub fn call(&mut self, callee: Local, args: Vec, local: Local) { @@ -256,6 +276,12 @@ pub struct MutatorIndex { span: Span, } +impl MutatorIndex { + pub fn span(&self) -> Span { + self.span.clone() + } +} + /// Whether or not there is remaining code #[derive(PartialEq, Eq)] pub enum MutatorIndexStatus { diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs index 3f1ef61bd078..16e80dfe98f6 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs @@ -10,6 +10,7 @@ use super::{MutatorIndex, MutatorIndexStatus, Instruction}; pub struct CachedBodyMutator { body: BodyMutator, unit: Local, + valid: Local, cache: HashMap, } @@ -18,8 +19,9 @@ impl CachedBodyMutator { pub fn from(body: Body) -> Self { let mut body = BodyMutator::from(body); let unit = body.new_local(Ty::new_tuple(&[]), Mutability::Not); + let valid = body.new_local(Ty::from_rigid_kind(stable_mir::ty::RigidTy::Bool), Mutability::Mut); let cache = HashMap::new(); - CachedBodyMutator { body, unit, cache } + CachedBodyMutator { body, unit, valid, cache } } /// Get the local at idx @@ -58,6 +60,20 @@ impl CachedBodyMutator { self.body.call(*func_local, args, local); } + pub fn assert(&mut self, assert_fn: &MirInstance, cond: Local, message: String, span: Span) { + let func_local; + { + let cache = &mut self.cache; + let body = &mut self.body; + { + func_local = cache + .entry(*assert_fn) + .or_insert_with(|| body.new_local(assert_fn.ty(), Mutability::Not)); + } + } + self.body.assert(*func_local, self.unit, cond, message, span); + } + /// Finalize the prologue, initializing all of the locals pub fn finalize_prologue(&mut self) { self.body.finalize_prologue(); @@ -104,4 +120,9 @@ impl CachedBodyMutator { pub fn unit(&self) -> Local { self.unit } + + /// Get the violated local + pub fn valid(&self) -> Local { + self.valid + } } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs index 36458f2ad3b3..a34f78778869 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs @@ -37,7 +37,7 @@ pub struct Instance { impl Instance { /// Create a new cacheable instance with the given signature and /// instance - pub fn new(signature: Signature, instance: super::MirInstance) -> Instance { + pub fn new(signature: Signature, instance: MirInstance) -> Instance { Instance { signature, instance } } } @@ -65,6 +65,25 @@ impl Cache { return Ok(&cache[i].instance); } } + let fndef = find_fn_def(*ctx, &sig.diagnostic) + .ok_or(MirError::new(format!("Not found: {}", &sig.diagnostic)))?; + let instance = MirInstance::resolve(fndef, &GenericArgs(sig.args.clone()))?; + cache.push(Instance::new(sig, instance)); + Ok(&cache[cache.len() - 1].instance) + } + + /// Register the kani assertion function + pub fn register_assert(&mut self, ctx: &TyCtxt) -> + Result<&MirInstance, MirError> { + let diagnostic = "KaniAssert".to_string(); + let args = vec![]; + let sig = Signature { diagnostic, args }; + let Cache { cache } = self; + for i in 0..cache.len() { + if sig == cache[i].signature { + return Ok(&cache[i].instance); + } + } let fndef = find_fn_def(*ctx, &sig.diagnostic) .ok_or(MirError::new(format!("Not found: {}", &sig.diagnostic)))?; let instance = super::MirInstance::resolve(fndef, &GenericArgs(sig.args.clone()))?; diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 21380ddbc3db..4b5d8106a8b5 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use rustc_middle::ty::TyCtxt; -use stable_mir::mir::{Local, Place, Rvalue, Mutability, StatementKind, Statement, BorrowKind, ProjectionElem}; +use stable_mir::mir::{BorrowKind, Local, Mutability, Place, ProjectionElem, Rvalue, Statement, StatementKind}; use stable_mir::ty::{GenericArgKind, Ty, Span, TyKind, RigidTy}; use super::{MirError, CachedBodyMutator, Cache, Signature, MutatorIndex, Instruction, MutatorIndexStatus}; @@ -77,11 +77,24 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Ok(()) } + /// Instrument with stack violated / not violated + pub fn instrument_stack_check(&mut self, idx: &MutatorIndex) -> Result<(), MirError> { + // Initialize the constants + let instance = self.cache.register( + &self.tcx, + Signature::new("KaniStackValid", &[]) + )?; + self.body.call(instance, vec![], self.body.valid()); + let msg = format!("Stacked borrows aliasing model violated at {:?}:{:?}", idx.span().get_filename(), idx.span().get_lines()); + let instance = self.cache.register_assert(&self.tcx)?; + self.body.assert(instance, self.body.valid(), msg, idx.span()); + Ok(()) + } + /// Instrument a validity assertion on the stacked borrows state /// at idx for (place: &mut T). - pub fn instrument_stack_check_ref( + pub fn instrument_stack_update_ref( &mut self, - idx: &MutatorIndex, place: Local, ty: Ty, ) -> Result<(), MirError> { @@ -92,15 +105,13 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Signature::new("KaniStackCheckRef", &[GenericArgKind::Type(ty)]), )?; self.body.call(instance, vec![*place_ref], self.body.unit()); - self.body.split(idx); Ok(()) } /// Instrument a validity assertion on the stacked borrows state /// at idx for (place: *const T). - pub fn instrument_stack_check_ptr( + pub fn instrument_stack_update_ptr( &mut self, - idx: &MutatorIndex, place: Local, ty: Ty, ) -> Result<(), MirError> { @@ -111,7 +122,6 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Signature::new("KaniStackCheckPtr", &[GenericArgKind::Type(ty)]), )?; self.body.call(instance, vec![*place_ref], self.body.unit()); - self.body.split(idx); Ok(()) } @@ -188,10 +198,9 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { ); } TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { - self.instrument_stack_check_ref(idx, from, ty)?; - self.instrument_new_mut_ref_from_raw( - idx, to, from, - )?; + self.instrument_stack_update_ref(from, ty)?; + self.instrument_stack_check(idx)?; + self.body.split(idx); } _ => {} } @@ -213,7 +222,9 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let to = to.local; // Copy to avoid borrow match self.body.local(to).ty.kind() { TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { - self.instrument_stack_check_ref(idx, from, ty)?; + self.instrument_stack_update_ref(from, ty)?; + self.instrument_stack_check(idx)?; + self.body.split(idx); self.instrument_new_mut_raw_from_ref( idx, to, from, )?; @@ -244,10 +255,14 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { println!("Self body local to is: {:?}", self.body.local(to)); match self.body.local(to).ty.kind() { TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { - self.instrument_stack_check_ref(idx, to, ty)?; + self.instrument_stack_update_ref(to, ty)?; + self.instrument_stack_check(idx)?; + self.body.split(idx); } TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { - self.instrument_stack_check_ptr(idx, to, ty)?; + self.instrument_stack_update_ptr(to, ty)?; + self.instrument_stack_check(idx)?; + self.body.split(idx); } _ => {} } diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 6db2060aacf0..22fa13344a0a 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -68,6 +68,15 @@ pub mod sstate { /// Next pointer id: the next pointer id in sequence static mut NEXT_TAG: PointerTag = 0; + /// Set to true whenever the stack has been + /// invalidated by a failed lookup. + static mut STACK_VALID: bool = true; + + #[rustc_diagnostic_item = "KaniStackValid"] + pub fn stack_valid() -> bool { + unsafe {STACK_VALID} + } + #[non_exhaustive] struct Access; #[allow(unused)] @@ -196,7 +205,7 @@ pub mod sstate { j += 1; } STACK_TOP = new_top; - crate::assert(found, "Stack violated."); + STACK_VALID = STACK_VALID && found; } } } @@ -260,6 +269,7 @@ pub mod sstate { /// pointer_to_val. #[rustc_diagnostic_item = "KaniNewMutRefFromValue"] pub fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { + crate::assert(true, "hi"); unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); diff --git a/tests/expected/aliasing/duplicate_write.rs b/tests/expected/aliasing/duplicate_write.rs index 62ecec832f21..afa68026b3e1 100644 --- a/tests/expected/aliasing/duplicate_write.rs +++ b/tests/expected/aliasing/duplicate_write.rs @@ -15,7 +15,5 @@ fn main() { ref_from_raw_1 = &mut *raw_pointer; *ref_from_raw_1 = 0; ref_from_raw_2 = &mut *raw_pointer; - *ref_from_raw_2 = 1; - *ref_from_raw_1 = 2; } } From a190e1c40b651023bb2bb8e97009bfd605b06d34 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 15 Aug 2024 15:28:44 -0400 Subject: [PATCH 26/72] Remove extraneous assertion --- library/kani/src/aliasing.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 22fa13344a0a..18d2d61aef56 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -269,7 +269,6 @@ pub mod sstate { /// pointer_to_val. #[rustc_diagnostic_item = "KaniNewMutRefFromValue"] pub fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { - crate::assert(true, "hi"); unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); From 79d40adaefb4f0c8727aeb98d963e76ca33d70d5 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 16 Aug 2024 14:32:47 -0400 Subject: [PATCH 27/72] Make the pass compile on the current compiler version --- kani-compiler/src/kani_middle/transform/mod.rs | 4 ---- kani-driver/src/call_single_file.rs | 14 +++++--------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index c20a2a9fdc2c..9771af621a62 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -19,12 +19,8 @@ use crate::kani_middle::codegen_units::CodegenUnit; use crate::kani_middle::reachability::CallGraph; use crate::kani_middle::transform::body::CheckType; -<<<<<<< HEAD use crate::kani_middle::transform::check_aliasing::AliasingPass; -use crate::kani_middle::transform::check_uninit::UninitPass; -======= use crate::kani_middle::transform::check_uninit::{DelayedUbPass, UninitPass}; ->>>>>>> kani/main use crate::kani_middle::transform::check_values::ValidValuePass; use crate::kani_middle::transform::contracts::{AnyModifiesPass, FunctionWithContractPass}; use crate::kani_middle::transform::kani_intrinsics::IntrinsicGeneratorPass; diff --git a/kani-driver/src/call_single_file.rs b/kani-driver/src/call_single_file.rs index 78a1993c9b87..2b97acf5b46a 100644 --- a/kani-driver/src/call_single_file.rs +++ b/kani-driver/src/call_single_file.rs @@ -135,21 +135,17 @@ 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.common_args.unstable_features.contains(UnstableFeature::Aliasing) { + if self.args.common_args.unstable_features.contains(UnstableFeature::UninitChecks) { // Automatically enable shadow memory, since the version of uninitialized memory checks // without non-determinism depends on it. - flags.push("--ub-check=aliasing".into()); + flags.push("-Z ghost-state".into()); + flags.push("--ub-check=uninit".into()); } - if self.args.common_args.unstable_features.contains(UnstableFeature::UninitChecks) { + if self.args.common_args.unstable_features.contains(UnstableFeature::Aliasing) { // 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()); + flags.push("--ub-check=aliasing".into()); } flags.extend(self.args.common_args.unstable_features.as_arguments().map(str::to_string)); From 4a718f73fcfc4380b63fbb9d207098c280eed35a Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Mon, 19 Aug 2024 18:30:36 -0400 Subject: [PATCH 28/72] Change test to emit line number. make .split() allow executing multiple calls. instrument more kinds of instructions. change the expected files to include line number. --- .../transform/check_aliasing/body_mutation.rs | 1 + .../transform/check_aliasing/body_mutator.rs | 26 ++++++-- .../check_aliasing/cached_body_mutator.rs | 5 +- .../check_aliasing/instrumentation.rs | 63 ++++++++++--------- .../transform/check_aliasing/mod.rs | 2 +- library/kani/src/aliasing.rs | 9 ++- .../aliasing/duplicate_write.expected | 2 +- tests/expected/aliasing/duplicate_write.rs | 2 + 8 files changed, 67 insertions(+), 43 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs index baaad0528830..18b31fc8d2da 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs @@ -1,6 +1,7 @@ use stable_mir::mir::Body; use super::MirError; use super::InstrumentationData; +use super::MutatorIndex; pub struct BodyMutationPassState<'tcx, 'cache> { instrumentation_data: InstrumentationData<'tcx, 'cache>, diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs index e73cc5b6f533..6a35d477a53c 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs @@ -17,6 +17,9 @@ pub struct BodyMutator { ghost_locals: Vec, ghost_blocks: Vec, ghost_statements: Vec, + /// index after the last ghost block interleaved with the + /// original program's control flow + processed: BasicBlockIdx, } impl BodyMutator { @@ -30,7 +33,8 @@ impl BodyMutator { span: Span, ghost_locals: Vec, ghost_blocks: Vec, - statements: Vec, + ghost_statements: Vec, + processed: BasicBlockIdx, ) -> Self { BodyMutator { blocks, @@ -41,7 +45,8 @@ impl BodyMutator { span, ghost_locals, ghost_blocks, - ghost_statements: statements, + ghost_statements, + processed } } @@ -75,6 +80,7 @@ impl BodyMutator { let spread_arg = body.spread_arg(); let debug_info = body.var_debug_info; let statements = Vec::new(); + let processed = 0; BodyMutator::new( body.blocks, locals, @@ -85,6 +91,7 @@ impl BodyMutator { ghost_locals, ghost_blocks, statements, + processed ) } @@ -157,6 +164,11 @@ impl BodyMutator { /// Finalize the prologue that initializes the variable data. pub fn finalize_prologue(&mut self) { + // mark the ghost blocks as processed + // include one extra ghost block to jump + // back to the 0th block + self.processed = self.ghost_blocks.len() + 1; + // goto the source's 0th basic block: let kind = TerminatorKind::Goto { target: self.blocks.len() - 1 }; let span = self.span; let terminator = Terminator { kind, span }; @@ -210,7 +222,12 @@ impl BodyMutator { /// Split at the given index, causing the current ghost code to be called /// and control flow to return from the ghost code to after the current index pub fn split(&mut self, index: &MutatorIndex) { - let kind = TerminatorKind::Goto { target: self.blocks.len() + self.ghost_blocks.len() - 1 }; + // Jump to the first unprocessed ghost block + let kind = TerminatorKind::Goto { target: self.blocks.len() + self.processed }; + // Mark the rest of the ghost blocks as processed + // include one extra -- the jump back into the source + // code + self.processed = self.ghost_blocks.len() + 1; let span = index.span; let term = Terminator { kind, span }; let len = self.blocks[index.bb].statements.len(); @@ -246,6 +263,7 @@ impl BodyMutator { ghost_locals, ghost_blocks, ghost_statements, + .. } => { assert!(ghost_statements.len() == 0); blocks.extend(ghost_blocks.into_iter()); @@ -269,7 +287,7 @@ impl BodyMutator { /// Mutator index with which to iterate over the function body. /// when idx = len(blocks[bb]), you are at the terminator, otherwise, /// you are at the statement idx in the basic block blocks[bb]. -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub struct MutatorIndex { bb: BasicBlockIdx, idx: usize, diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs index 16e80dfe98f6..6eb6a1da1e46 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use super::{MirInstance, BodyMutator}; -use stable_mir::mir::{Local, Mutability, LocalDecl, Statement, Body}; +use stable_mir::mir::{Body, ConstOperand, Local, LocalDecl, Mutability, Operand, Place, Statement}; use stable_mir::ty::{Ty, Span}; use super::{MutatorIndex, MutatorIndexStatus, Instruction}; @@ -20,6 +20,7 @@ impl CachedBodyMutator { let mut body = BodyMutator::from(body); let unit = body.new_local(Ty::new_tuple(&[]), Mutability::Not); let valid = body.new_local(Ty::from_rigid_kind(stable_mir::ty::RigidTy::Bool), Mutability::Mut); + body.insert_statement(Statement { kind: stable_mir::mir::StatementKind::Assign(Place::from(valid), stable_mir::mir::Rvalue::Use(Operand::Constant(ConstOperand { span: body.span(), user_ty: None, const_: stable_mir::ty::MirConst::from_bool(true) }))), span: body.span() }); let cache = HashMap::new(); CachedBodyMutator { body, unit, valid, cache } } @@ -85,7 +86,7 @@ impl CachedBodyMutator { } /// Get an index with which to iterate over the body - pub fn new_index(&mut self) -> MutatorIndex { + pub fn new_index(&self) -> MutatorIndex { self.body.new_index() } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 4b5d8106a8b5..8e6720c4d4ec 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use rustc_middle::ty::TyCtxt; -use stable_mir::mir::{BorrowKind, Local, Mutability, Place, ProjectionElem, Rvalue, Statement, StatementKind}; +use stable_mir::mir::{BorrowKind, ConstOperand, Local, Mutability, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind}; use stable_mir::ty::{GenericArgKind, Ty, Span, TyKind, RigidTy}; use super::{MirError, CachedBodyMutator, Cache, Signature, MutatorIndex, Instruction, MutatorIndexStatus}; @@ -73,20 +73,18 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Signature::new("KaniNewMutRefFromValue", &[GenericArgKind::Type(ty)]), )?; self.body.call(instance, vec![*lvalue_ref, *rvalue_ref], self.body.unit()); - self.body.split(idx); Ok(()) } /// Instrument with stack violated / not violated pub fn instrument_stack_check(&mut self, idx: &MutatorIndex) -> Result<(), MirError> { - // Initialize the constants let instance = self.cache.register( &self.tcx, Signature::new("KaniStackValid", &[]) )?; self.body.call(instance, vec![], self.body.valid()); - let msg = format!("Stacked borrows aliasing model violated at {:?}:{:?}", idx.span().get_filename(), idx.span().get_lines()); let instance = self.cache.register_assert(&self.tcx)?; + let msg = format!("Stacked borrows aliasing model violated at {:?}:{:?}", idx.span().get_filename(), idx.span().get_lines()); self.body.assert(instance, self.body.valid(), msg, idx.span()); Ok(()) } @@ -129,12 +127,11 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// created = &mut *(raw: const *T). pub fn instrument_new_mut_ref_from_raw( &mut self, - idx: &MutatorIndex, created: Local, raw: Local, + ty: Ty, ) -> Result<(), MirError> { // Initialize the constants - let ty = self.body.local(created).ty; let created_ref = self.meta_stack.get(&created).unwrap(); let reference_ref = self.meta_stack.get(&raw).unwrap(); let instance = self.cache.register( @@ -142,7 +139,6 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Signature::new("KaniNewMutRefFromRaw", &[GenericArgKind::Type(ty)]), )?; self.body.call(instance, vec![*created_ref, *reference_ref], self.body.unit()); - self.body.split(idx); Ok(()) } @@ -150,12 +146,11 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// created = (ref: &mut T) as *mut T pub fn instrument_new_mut_raw_from_ref( &mut self, - idx: &MutatorIndex, created: Local, reference: Local, + ty: Ty, ) -> Result<(), MirError> { // Initialize the constants - let ty = self.body.local(created).ty; let created_ref = self.meta_stack.get(&created).unwrap(); let reference_ref = self.meta_stack.get(&reference).unwrap(); let instance = self.cache.register( @@ -163,7 +158,6 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Signature::new("KaniNewMutRawFromRef", &[GenericArgKind::Type(ty)]), )?; self.body.call(instance, vec![*created_ref, *reference_ref], self.body.unit()); - self.body.split(idx); Ok(()) } @@ -185,21 +179,28 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { self.instrument_new_stack_reference( idx, to.local, from.local, )?; + self.body.split(idx); } [ProjectionElem::Deref] => { // Reborrow - // x : &mut T = &*(y : *mut T) + // x : &mut T = &*(y : *mut T OR &mut T) let from = from.local; // Copy to avoid borrow let to = to.local; // Copy to avoid borrow - match self.body.local(to).ty.kind() { - TyKind::RigidTy(RigidTy::Ref(_, _ty, _)) => { - eprintln!( - "Reborrow from reference not yet handled" - ); + match self.body.local(from).ty.kind() { + TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { + // Reborrow of reference + // Occurs during normal course of reference-from + // raw-pointer -- a reference will be made, then reborrowed. + self.instrument_stack_update_ref(from, ty)?; + self.instrument_stack_check(idx)?; + self.instrument_new_mut_ref_from_raw(to, from, ty)?; + self.body.split(idx); } TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { - self.instrument_stack_update_ref(from, ty)?; + // Reborrow of raw pointer + self.instrument_stack_update_ptr(from, ty)?; self.instrument_stack_check(idx)?; + self.instrument_new_mut_ref_from_raw(to, from, ty)?; self.body.split(idx); } _ => {} @@ -214,34 +215,37 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { match from.projection[..] { [] => { // x = &raw y - eprintln!("addr of not yet handled"); + panic!("Addr of not yet handled"); } [ProjectionElem::Deref] => { - // x = &raw mut *(y: &mut T) + // x = &raw mut *(y: &mut T OR *mut T) let from = from.local; // Copy to avoid borrow - let to = to.local; // Copy to avoid borrow - match self.body.local(to).ty.kind() { + let to = to.local; + match self.body.local(from).ty.kind() { TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { self.instrument_stack_update_ref(from, ty)?; self.instrument_stack_check(idx)?; - self.body.split(idx); self.instrument_new_mut_raw_from_ref( - idx, to, from, + to, from, ty )?; + self.body.split(idx); } - TyKind::RigidTy(RigidTy::RawPtr(_ty, _)) => { - eprintln!( - "Pointer to pointer casts not yet handled" - ); + _ => { + panic!("Deref case {:?} not yet handled", kind); } - _ => {} } } _ => {} } + }, + Rvalue::Use(Operand::Constant(_)) => { + // Do nothing for the constants case + }, + Rvalue::BinaryOp(_, _, _) => { + eprintln!("Binary op not yet handled"); } _ => { - eprintln!("Rvalue kind: {:?} not yet handled", rvalue); + panic!("Rvalue kind: {:?} not yet handled", rvalue); } } match to.projection[..] { @@ -252,7 +256,6 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { [ProjectionElem::Deref] => { // *x = rvalue let to = to.local; - println!("Self body local to is: {:?}", self.body.local(to)); match self.body.local(to).ty.kind() { TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { self.instrument_stack_update_ref(to, ty)?; diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 6781b59a9379..ae221d23e665 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -131,7 +131,7 @@ impl TransformPass for AliasingPass { (false, body) } else { let body = CachedBodyMutator::from(body); - let instrumentation_data = InstrumentationData::new(tcx, &mut self.cache, body); + let mut instrumentation_data = InstrumentationData::new(tcx, &mut self.cache, body); let out = BodyMutationPassState::new(instrumentation_data).finalize(); (true, out) } diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 18d2d61aef56..5a0b768c1a53 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -1,10 +1,8 @@ const STACK_DEPTH: usize = 15; type PointerTag = u8; -use crate::mem::pointer_object; -use crate::mem::pointer_offset; +use crate::mem::{pointer_object, pointer_offset}; use crate::shadow::ShadowMem; -use crate::{any, assume}; /// The stacked borrows state. /// @@ -156,7 +154,8 @@ pub mod sstate { if demonic_nondet() && STATE == MonitorState::UNINIT { STATE = MonitorState::INIT; OBJECT = pointer_object(pointer); - assume(OFFSET < std::mem::size_of::()); + OFFSET = 0; + crate::assume(OFFSET < std::mem::size_of::()); STACK_TAGS[STACK_TOP] = tag; STACK_PERMS[STACK_TOP] = Permission::UNIQUE; STACK_TOP += 1; @@ -313,5 +312,5 @@ pub mod sstate { } pub fn demonic_nondet() -> bool { - any::() + crate::any() } diff --git a/tests/expected/aliasing/duplicate_write.expected b/tests/expected/aliasing/duplicate_write.expected index 35a510028c4e..69eb5e3b9b29 100644 --- a/tests/expected/aliasing/duplicate_write.expected +++ b/tests/expected/aliasing/duplicate_write.expected @@ -1 +1 @@ -Failed Checks: Stack violated. +Failed Checks: Stacked borrows aliasing model violated at "./tests/expected/aliasing/duplicate_write.rs":LineInfo { start_line: 21, start_col: 1, end_line: 21, end_col: 2 } diff --git a/tests/expected/aliasing/duplicate_write.rs b/tests/expected/aliasing/duplicate_write.rs index afa68026b3e1..62ecec832f21 100644 --- a/tests/expected/aliasing/duplicate_write.rs +++ b/tests/expected/aliasing/duplicate_write.rs @@ -15,5 +15,7 @@ fn main() { ref_from_raw_1 = &mut *raw_pointer; *ref_from_raw_1 = 0; ref_from_raw_2 = &mut *raw_pointer; + *ref_from_raw_2 = 1; + *ref_from_raw_1 = 2; } } From cf3e19c8d0cc50ee2098fa6b922db42c9e72ff95 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Mon, 19 Aug 2024 18:49:52 -0400 Subject: [PATCH 29/72] Delete redundant tests, provide accurate line #s. --- .../check_aliasing/instrumentation.rs | 11 ++++++++ tests/expected/aliasing/control_flow.expected | 2 +- tests/expected/aliasing/control_flow.rs | 2 +- .../duplicate_write_compressed.expected | 2 +- .../aliasing/multiple_referents.expected | 1 - tests/expected/aliasing/multiple_referents.rs | 28 ------------------- 6 files changed, 14 insertions(+), 32 deletions(-) delete mode 100644 tests/expected/aliasing/multiple_referents.expected delete mode 100644 tests/expected/aliasing/multiple_referents.rs diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 8e6720c4d4ec..1c037644a089 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -241,9 +241,20 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Rvalue::Use(Operand::Constant(_)) => { // Do nothing for the constants case }, + Rvalue::Use(Operand::Copy(_)) => { + eprintln!("Copy not yet handled"); + // Do nothing for the constants case + }, + Rvalue::Use(Operand::Move(_)) => { + eprintln!("Move not yet handled"); + // Do nothing for the constants case + }, Rvalue::BinaryOp(_, _, _) => { eprintln!("Binary op not yet handled"); } + Rvalue::CheckedBinaryOp(_, _, _) => { + eprintln!("Checked binary op not yet handled"); + } _ => { panic!("Rvalue kind: {:?} not yet handled", rvalue); } diff --git a/tests/expected/aliasing/control_flow.expected b/tests/expected/aliasing/control_flow.expected index 35a510028c4e..8c3030151008 100644 --- a/tests/expected/aliasing/control_flow.expected +++ b/tests/expected/aliasing/control_flow.expected @@ -1 +1 @@ -Failed Checks: Stack violated. +Failed Checks: Stacked borrows aliasing model violated at "./tests/expected/aliasing/control_flow.rs":LineInfo { start_line: 22, start_col: 17, end_line: 22, end_col: 36 } diff --git a/tests/expected/aliasing/control_flow.rs b/tests/expected/aliasing/control_flow.rs index 60e5ba059776..4a2fe04ff555 100644 --- a/tests/expected/aliasing/control_flow.rs +++ b/tests/expected/aliasing/control_flow.rs @@ -1,12 +1,12 @@ // kani-flags: -Zghost-state -Zaliasing +#[allow(unused)] #[kani::proof] fn main() { let mut local: i32 = 10; let mut referent_1: i32 = 0; let mut referent_2: i32 = 0; let mut ref_from_raw_1: &mut i32 = &mut referent_1; - #[allow(unused)] let mut ref_from_raw_2: &mut i32 = &mut referent_2; let raw_pointer: *mut i32 = &mut local as *mut i32; let mut state = false; diff --git a/tests/expected/aliasing/duplicate_write_compressed.expected b/tests/expected/aliasing/duplicate_write_compressed.expected index 35a510028c4e..bf6effc16ffd 100644 --- a/tests/expected/aliasing/duplicate_write_compressed.expected +++ b/tests/expected/aliasing/duplicate_write_compressed.expected @@ -1 +1 @@ -Failed Checks: Stack violated. +Failed Checks: Stacked borrows aliasing model violated at "./tests/expected/aliasing/duplicate_write_compressed.rs":LineInfo { start_line: 13, start_col: 5, end_line: 13, end_col: 6 } diff --git a/tests/expected/aliasing/multiple_referents.expected b/tests/expected/aliasing/multiple_referents.expected deleted file mode 100644 index 35a510028c4e..000000000000 --- a/tests/expected/aliasing/multiple_referents.expected +++ /dev/null @@ -1 +0,0 @@ -Failed Checks: Stack violated. diff --git a/tests/expected/aliasing/multiple_referents.rs b/tests/expected/aliasing/multiple_referents.rs deleted file mode 100644 index 6f60f9a71e27..000000000000 --- a/tests/expected/aliasing/multiple_referents.rs +++ /dev/null @@ -1,28 +0,0 @@ -// kani-flags: -Zghost-state -Zaliasing - -#[kani::proof] -fn main() { - let mut local: i32 = 10; - let mut referent_1: i32 = 0; - let mut referent_2: i32 = 0; - let mut ref_from_raw_1: &mut i32 = &mut referent_1; - let mut ref_from_raw_2: &mut i32 = &mut referent_2; - let temp_ref: &mut i32 = &mut local; - let raw_pointer: *mut i32 = &mut local as *mut i32; - let mut state = false; - let mut iters = 0; - unsafe { - while iters < 2 { - if state { - ref_from_raw_1 = &mut *raw_pointer; - *ref_from_raw_1 = 0; - } else { - ref_from_raw_2 = &mut *raw_pointer; - *ref_from_raw_2 = 1; - *ref_from_raw_1 = 2; - } - state = true; - iters += 1; - } - } -} From afc5ecaa3e1f11ae79a744f9284ce442451ca8f3 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 22 Aug 2024 22:33:29 -0400 Subject: [PATCH 30/72] Improve error reporting. Use Kani's library. --- .../transform/check_aliasing/actions.rs | 215 ++++++++ .../transform/check_aliasing/body_mutation.rs | 30 -- .../transform/check_aliasing/body_mutator.rs | 315 ------------ .../check_aliasing/cached_body_mutator.rs | 129 ----- .../check_aliasing/function_cache.rs | 16 - .../check_aliasing/instrumentation.rs | 469 +++++++++--------- .../transform/check_aliasing/mod.rs | 16 +- library/kani/src/aliasing.rs | 8 + .../aliasing/duplicate_write.expected | 2 +- .../duplicate_write_compressed.expected | 2 +- 10 files changed, 466 insertions(+), 736 deletions(-) create mode 100644 kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs delete mode 100644 kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs delete mode 100644 kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs delete mode 100644 kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs new file mode 100644 index 000000000000..c790d3cb4017 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs @@ -0,0 +1,215 @@ +use stable_mir::mir::{ + BorrowKind, Local, LocalDecl, Mutability, Operand, ProjectionElem, Rvalue, + Statement, StatementKind, Place, +}; +use stable_mir::ty::{RigidTy, Ty, TyKind}; + +/// An instrumentation action to be taken +#[derive(Debug)] +pub enum Action { + StackCheck, + NewStackReference { lvalue: Local, rvalue: usize }, + StackUpdateReference { place: usize, ty: Ty }, + NewMutRefFromRaw { lvalue: usize, rvalue: usize, ty: Ty }, + StackUpdatePointer { place: usize, ty: Ty }, + NewMutRawFromRef { lvalue: usize, rvalue: usize, ty: Ty }, +} + +pub struct CollectActions<'locals> { + actions: Vec, + locals: &'locals [LocalDecl], +} + +impl<'locals> CollectActions<'locals> { + pub fn new(locals: &'locals [LocalDecl]) -> Self { + CollectActions { actions: Vec::new(), locals } + } + + pub fn finalize(self) -> Vec { + self.actions + } + + fn visit_assign_reference_dereference(&mut self, lvalue: Local, rvalue: Local) { + match self.locals[rvalue].ty.kind() { + TyKind::RigidTy(RigidTy::Ref(_, ty, _)) | TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { + // reborrow + self.actions.push(Action::NewMutRefFromRaw { + lvalue, + rvalue, + ty, + }); + } + _ => {} + } + } + + fn visit_assign_reference(&mut self, to: Local, from: Place) { + match from.projection[..] { + [] => { + // Direct reference to stack local + // x = &y; + let lvalue = to; + let rvalue = from.local.clone(); + self.actions.push(Action::NewStackReference { lvalue, rvalue }); + }, + [ProjectionElem::Deref] => { + // Reborrow + // x : &mut T = &*(y : *mut T OR &mut T) + let lvalue = to; // Copy to avoid borrow + let rvalue = from.local; // Copy to avoid borrow + self.visit_assign_reference_dereference(lvalue, rvalue); + } + _ => { /* not yet handled */ } + } + } + + fn visit_assign_pointer(&mut self, to: Local, from: Place) { + match from.projection[..] { + [] => { + // x = &raw y + panic!("Addr of not yet handled"); + } + [ProjectionElem::Deref] => { + // x = &raw mut *(y: &mut T OR *mut T) + let rvalue = from.local; // Copy to avoid borrow + let lvalue = to; + match self.locals[rvalue].ty.kind() { + TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { + self.actions.push(Action::NewMutRawFromRef { + lvalue, + rvalue, + ty, + }); + } + _ => { + panic!("Dereference of rvalue case not yet handled for raw pointers {:?}", from); + } + } + } + _ => {} + } + } + + fn visit_place(&mut self, place: &Place) { + match &place.projection[..] { + [] => { + // Direct usage -- no update needed + return; + }, + [ProjectionElem::Deref] => { + // Dereference -- instrument stack check + }, + _ => { + // Field access -- not yet handled. + return; + } + }; + match self.locals[place.local].ty.kind() { + TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { + self.actions.push(Action::StackUpdateReference { place: place.local, ty }); + self.actions.push(Action::StackCheck); + }, + TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { + self.actions.push(Action::StackUpdatePointer { place: place.local, ty }); + self.actions.push(Action::StackCheck); + }, + _ => {}, + } + } + + fn visit_rvalue_places(&mut self, rvalue: &Rvalue) { + match rvalue { + Rvalue::AddressOf(_, place) => { + self.visit_place(place); + }, + Rvalue::Ref(_, _, place) => { + self.visit_place(place); + } + // The rest are not yet handled + Rvalue::Aggregate(_, _) => {}, + Rvalue::BinaryOp(_, _, _) => {}, + Rvalue::Cast(_, _, _) => {}, + Rvalue::CheckedBinaryOp(_, _, _) => {}, + Rvalue::CopyForDeref(_) => {}, + Rvalue::Discriminant(_) => {}, + Rvalue::Len(_) => {}, + Rvalue::Repeat(_, _) => {}, + Rvalue::ShallowInitBox(_, _) => {}, + Rvalue::ThreadLocalRef(_) => {}, + Rvalue::NullaryOp(_, _) => {}, + Rvalue::UnaryOp(_, _) => {}, + Rvalue::Use(_) => {}, + } + } + + fn visit_statement_places(&mut self, stmt: &Statement) { + match &stmt.kind { + StatementKind::Assign(place, rvalue) => { + self.visit_rvalue_places(rvalue); + self.visit_place(place); + } + StatementKind::FakeRead(_, _) => {}, + StatementKind::SetDiscriminant { .. } => {}, + StatementKind::Deinit(_) => {}, + StatementKind::StorageLive(_) => {}, + StatementKind::StorageDead(_) => {}, + StatementKind::Retag(_, _) => {}, + StatementKind::PlaceMention(_) => {}, + StatementKind::AscribeUserType { .. } => {}, + StatementKind::Coverage(_) => {}, + StatementKind::Intrinsic(_) => {}, + StatementKind::ConstEvalCounter => {}, + StatementKind::Nop => {}, + } + } + + pub fn visit_statement(&mut self, stmt: &Statement) { + self.visit_statement_places(stmt); + match &stmt.kind { + StatementKind::Assign(to, rvalue) => { + match rvalue { + Rvalue::Ref(_, BorrowKind::Mut { .. }, from) => { + self.visit_assign_reference(to.local, from.clone()); + } + Rvalue::AddressOf(Mutability::Mut, from) => { + self.visit_assign_pointer(to.local, from.clone()); + } + Rvalue::Use(Operand::Constant(_)) => { + // Do nothing for the constants case + } + Rvalue::Use(Operand::Copy(_)) => { + eprintln!("Copy not yet handled"); + // Do nothing for the constants case + } + Rvalue::Use(Operand::Move(_)) => { + eprintln!("Move not yet handled"); + // Do nothing for the constants case + } + Rvalue::BinaryOp(_, _, _) => { + eprintln!("Binary op not yet handled"); + } + Rvalue::CheckedBinaryOp(_, _, _) => { + eprintln!("Checked binary op not yet handled"); + } + _ => { + panic!("Rvalue kind: {:?} not yet handled", rvalue); + } + } + } + // The following are not yet handled, however, no info is printed + // to avoid blowups: + StatementKind::Retag(_, _) => {} + StatementKind::FakeRead(_, _) => {} + StatementKind::SetDiscriminant { .. } => {} + StatementKind::Deinit(_) => {} + StatementKind::StorageLive(_) => {} + StatementKind::StorageDead(_) => {} + StatementKind::PlaceMention(_) => {} + StatementKind::AscribeUserType { .. } => {} + StatementKind::Coverage(_) => {} + StatementKind::Intrinsic(_) => {} + StatementKind::ConstEvalCounter => {} + StatementKind::Nop => {} + } + } +} diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs deleted file mode 100644 index 18b31fc8d2da..000000000000 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutation.rs +++ /dev/null @@ -1,30 +0,0 @@ -use stable_mir::mir::Body; -use super::MirError; -use super::InstrumentationData; -use super::MutatorIndex; - -pub struct BodyMutationPassState<'tcx, 'cache> { - instrumentation_data: InstrumentationData<'tcx, 'cache>, -} - -impl<'tcx, 'cache> BodyMutationPassState<'tcx, 'cache> { - pub fn new(instrumentation_data: InstrumentationData<'tcx, 'cache>) -> Self { - BodyMutationPassState { instrumentation_data } - } - - pub fn instrument_locals(&mut self) -> Result<(), MirError> { - self.instrumentation_data.instrument_locals() - } - - pub fn instrument_instructions(&mut self) -> Result<(), MirError> { - self.instrumentation_data.instrument_instructions()?; - Ok(()) - } - - pub fn finalize(mut self) -> Body { - self.instrument_locals().unwrap(); - self.instrumentation_data.finalize_prologue(); - self.instrument_instructions().unwrap(); - self.instrumentation_data.finalize() - } -} diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs deleted file mode 100644 index 6a35d477a53c..000000000000 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs +++ /dev/null @@ -1,315 +0,0 @@ -use stable_mir::mir::{BasicBlock, BasicBlockIdx, Body, Local, LocalDecl, Mutability, Operand, Place, Statement, Terminator, TerminatorKind, UnwindAction, VarDebugInfo, ConstOperand}; -use stable_mir::ty::{Ty, MirConst, Span}; - -/// BodyMutator combines the data of the function body -/// with "ghost" basic block and local data, allowing the user -/// to instrument the original body with instructions in the -/// "ghost" section while iterating over the original data from the -/// function body. -pub struct BodyMutator { - blocks: Vec, - locals: Vec, - arg_count: usize, - var_debug_info: Vec, - spread_arg: Option, - span: Span, - - ghost_locals: Vec, - ghost_blocks: Vec, - ghost_statements: Vec, - /// index after the last ghost block interleaved with the - /// original program's control flow - processed: BasicBlockIdx, -} - -impl BodyMutator { - /// Instantiate the body mutator - pub fn new( - blocks: Vec, - locals: Vec, - arg_count: usize, - var_debug_info: Vec, - spread_arg: Option, - span: Span, - ghost_locals: Vec, - ghost_blocks: Vec, - ghost_statements: Vec, - processed: BasicBlockIdx, - ) -> Self { - BodyMutator { - blocks, - locals, - arg_count, - var_debug_info, - spread_arg, - span, - ghost_locals, - ghost_blocks, - ghost_statements, - processed - } - } - - /// Generate bb0 which jumps to the "ghost" basic blocks - pub fn gen_bb0(body: &mut Body) -> BasicBlock { - let target = body.blocks.len() + 1; - let kind = TerminatorKind::Goto { target }; - let span = body.span; - let terminator = Terminator { kind, span }; - let statements = Vec::new(); - std::mem::replace(&mut body.blocks[0], BasicBlock { statements, terminator }) - } - - /// Generate a unit local variable to be - /// used as the destination of function calls - pub fn gen_unit(body: &Body) -> LocalDecl { - let ty = Ty::new_tuple(&[]); - let span = body.span; - let mutability = Mutability::Not; - LocalDecl { ty, span, mutability } - } - - /// Create this body from Mir's Body - pub fn from(mut body: Body) -> Self { - let bb0 = Self::gen_bb0(&mut body); - body.blocks.push(bb0); - let ghost_locals = vec![Self::gen_unit(&body)]; - let ghost_blocks = vec![]; - let locals = body.locals().to_vec(); - let arg_count = body.arg_locals().len(); - let spread_arg = body.spread_arg(); - let debug_info = body.var_debug_info; - let statements = Vec::new(); - let processed = 0; - BodyMutator::new( - body.blocks, - locals, - arg_count, - debug_info, - spread_arg, - body.span, - ghost_locals, - ghost_blocks, - statements, - processed - ) - } - - /// Locals in `self` that correspond to this function's arguments. - pub fn arg_locals(&self) -> &[LocalDecl] { - &self.locals[1..][..self.arg_count] - } - - // Get the inner locals - pub fn inner_locals(&self) -> &[LocalDecl] { - &self.locals[self.arg_count + 1..] - } - - /// Index into the locals - pub fn local(&self, idx: usize) -> &LocalDecl { - if idx > self.locals.len() { - &self.ghost_locals[idx - self.locals.len()] - } else { - &self.locals[idx] - } - } - - /// Create a new "ghost" local - pub fn new_local(&mut self, ty: Ty, mutability: Mutability) -> Local { - let span = self.span; - let decl = LocalDecl { ty, span, mutability }; - let local = self.locals.len() + self.ghost_locals.len(); - self.ghost_locals.push(decl); - local - } - - /// Call the assertion function at assert_fn, returning into the unit, - /// with condition cond and message string. - pub fn assert(&mut self, assert_fn: Local, unit: Local, cond: Local, message: String, span: Span) { - let user_ty = None; - let const_ = MirConst::from_str(&message); - let message = Operand::Constant(ConstOperand { span, user_ty, const_ }); - let args = vec![Operand::Copy(Place::from(cond)), - message]; - let func = Operand::Copy(Place::from(assert_fn)); - let unwind = UnwindAction::Terminate; - let target = Some(self.next_block()); - let projection = Vec::new(); - let destination = Place { local: unit, projection }; - let kind = TerminatorKind::Call { func, args, destination, target, unwind }; - let span = self.span; - let terminator = Terminator { kind, span }; - let statements = std::mem::replace(&mut self.ghost_statements, Vec::new()); - self.ghost_blocks.push(BasicBlock { statements, terminator }); - } - - /// Insert a call into the function body of the function stored at - /// callee with the arguments in args. - pub fn call(&mut self, callee: Local, args: Vec, local: Local) { - let projection = Vec::new(); - let destination = Place { local, projection }; - let args = args - .into_iter() - .map(|v| Operand::Copy(Place { local: v, projection: vec![] })) - .collect(); - let func = Operand::Copy(Place::from(callee)); - let unwind = UnwindAction::Terminate; - let target = Some(self.next_block()); - let kind = TerminatorKind::Call { func, args, destination, target, unwind }; - let span = self.span; - let terminator = Terminator { kind, span }; - let statements = std::mem::replace(&mut self.ghost_statements, Vec::new()); - self.ghost_blocks.push(BasicBlock { statements, terminator }); - } - - /// Finalize the prologue that initializes the variable data. - pub fn finalize_prologue(&mut self) { - // mark the ghost blocks as processed - // include one extra ghost block to jump - // back to the 0th block - self.processed = self.ghost_blocks.len() + 1; - // goto the source's 0th basic block: - let kind = TerminatorKind::Goto { target: self.blocks.len() - 1 }; - let span = self.span; - let terminator = Terminator { kind, span }; - self.insert_bb(terminator); - } - - /// Insert a ghost statement - pub fn insert_statement(&mut self, stmt: Statement) { - self.ghost_statements.push(stmt); - } - - /// Get an index with which to iterate over the body - pub fn new_index(&self) -> MutatorIndex { - let len = self.blocks.len(); - let bb = std::cmp::max(len, 1) - 1; - let idx = if len > 0 { std::cmp::max(self.blocks[bb].statements.len(), 1) - 1 } else { 0 }; - let span = self.span; - MutatorIndex { bb, idx, span } - } - - /// Decrement the index - pub fn decrement(&self, index: &mut MutatorIndex) -> MutatorIndexStatus { - let mut status = MutatorIndexStatus::Done; - if index.idx > 0 || index.bb > 0 { - status = MutatorIndexStatus::Remaining; - } - if index.idx > 0 { - if index.idx < self.blocks[index.bb].statements.len() { - index.span = self.blocks[index.bb].statements[index.idx].span; - } else { - index.span = self.blocks[index.bb].terminator.span; - } - index.idx -= 1; - } else if index.bb > 0 { - index.bb -= 1; - index.span = self.blocks[index.bb].terminator.span; - index.idx = self.blocks[index.bb].statements.len() - } - status - } - - /// Inspect the index yielding the current statement or terminator - pub fn inspect(&self, index: &MutatorIndex) -> Instruction { - if index.idx >= self.blocks[index.bb].statements.len() { - Instruction::Term(&self.blocks[index.bb].terminator) - } else { - Instruction::Stmt(&self.blocks[index.bb].statements[index.idx]) - } - } - - /// Split at the given index, causing the current ghost code to be called - /// and control flow to return from the ghost code to after the current index - pub fn split(&mut self, index: &MutatorIndex) { - // Jump to the first unprocessed ghost block - let kind = TerminatorKind::Goto { target: self.blocks.len() + self.processed }; - // Mark the rest of the ghost blocks as processed - // include one extra -- the jump back into the source - // code - self.processed = self.ghost_blocks.len() + 1; - let span = index.span; - let term = Terminator { kind, span }; - let len = self.blocks[index.bb].statements.len(); - if index.idx < len { - self.ghost_statements.extend(self.blocks[index.bb].statements.split_off(index.idx + 1)); - } - let term = std::mem::replace(&mut self.blocks[index.bb].terminator, term); - self.insert_bb(term); - } - - /// Get the index of the next basic block - pub fn next_block(&self) -> usize { - self.blocks.len() + self.ghost_blocks.len() + 1 - } - - /// Insert a basic block with the given terminator - pub fn insert_bb(&mut self, terminator: Terminator) { - let statements = std::mem::replace(&mut self.ghost_statements, Vec::new()); - let execute_original_body = BasicBlock { statements, terminator }; - self.ghost_blocks.push(execute_original_body); - } - - // Finalize the body mutator yielding a body - pub fn finalize(self) -> Body { - match self { - BodyMutator { - mut blocks, - mut locals, - arg_count, - var_debug_info, - spread_arg, - span, - ghost_locals, - ghost_blocks, - ghost_statements, - .. - } => { - assert!(ghost_statements.len() == 0); - blocks.extend(ghost_blocks.into_iter()); - locals.extend(ghost_locals.into_iter()); - Body::new(blocks, locals, arg_count, var_debug_info, spread_arg, span) - } - } - } - - /// Get the span - pub fn span(&self) -> Span { - self.span - } - - /// Get the locals - pub fn locals(&self) -> &[LocalDecl] { - &self.locals - } -} - -/// Mutator index with which to iterate over the function body. -/// when idx = len(blocks[bb]), you are at the terminator, otherwise, -/// you are at the statement idx in the basic block blocks[bb]. -#[derive(Debug, Copy, Clone)] -pub struct MutatorIndex { - bb: BasicBlockIdx, - idx: usize, - span: Span, -} - -impl MutatorIndex { - pub fn span(&self) -> Span { - self.span.clone() - } -} - -/// Whether or not there is remaining code -#[derive(PartialEq, Eq)] -pub enum MutatorIndexStatus { - Remaining, - Done, -} - -/// The instruction under inspection -pub enum Instruction<'a> { - Stmt(&'a Statement), - #[allow(unused)] - Term(&'a Terminator), -} diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs deleted file mode 100644 index 6eb6a1da1e46..000000000000 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/cached_body_mutator.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::collections::HashMap; -use super::{MirInstance, BodyMutator}; -use stable_mir::mir::{Body, ConstOperand, Local, LocalDecl, Mutability, Operand, Place, Statement}; -use stable_mir::ty::{Ty, Span}; -use super::{MutatorIndex, MutatorIndexStatus, Instruction}; - -/// Body mutator which wraps the BodyMutator -/// interface with a cache of the locals that -/// store function calls. -pub struct CachedBodyMutator { - body: BodyMutator, - unit: Local, - valid: Local, - cache: HashMap, -} - -impl CachedBodyMutator { - /// Create a new cached body mutator - pub fn from(body: Body) -> Self { - let mut body = BodyMutator::from(body); - let unit = body.new_local(Ty::new_tuple(&[]), Mutability::Not); - let valid = body.new_local(Ty::from_rigid_kind(stable_mir::ty::RigidTy::Bool), Mutability::Mut); - body.insert_statement(Statement { kind: stable_mir::mir::StatementKind::Assign(Place::from(valid), stable_mir::mir::Rvalue::Use(Operand::Constant(ConstOperand { span: body.span(), user_ty: None, const_: stable_mir::ty::MirConst::from_bool(true) }))), span: body.span() }); - let cache = HashMap::new(); - CachedBodyMutator { body, unit, valid, cache } - } - - /// Get the local at idx - pub fn local(&self, idx: usize) -> &LocalDecl { - self.body.local(idx) - } - - /// Get a new local - pub fn new_local(&mut self, ty: Ty, mutability: Mutability) -> Local { - self.body.new_local(ty, mutability) - } - - /// Locals in `self` that correspond to this function's arguments. - pub fn arg_locals(&self) -> &[LocalDecl] { - self.body.arg_locals() - } - - /// Locals - pub fn locals(&self) -> &[LocalDecl] { - self.body.locals() - } - - /// Insert a call to the function stored at local with the args - /// stored at args - pub fn call(&mut self, callee: &MirInstance, args: Vec, local: Local) { - let func_local; - { - let cache = &mut self.cache; - let body = &mut self.body; - { - func_local = cache - .entry(*callee) - .or_insert_with(|| body.new_local(callee.ty(), Mutability::Not)); - } - } - self.body.call(*func_local, args, local); - } - - pub fn assert(&mut self, assert_fn: &MirInstance, cond: Local, message: String, span: Span) { - let func_local; - { - let cache = &mut self.cache; - let body = &mut self.body; - { - func_local = cache - .entry(*assert_fn) - .or_insert_with(|| body.new_local(assert_fn.ty(), Mutability::Not)); - } - } - self.body.assert(*func_local, self.unit, cond, message, span); - } - - /// Finalize the prologue, initializing all of the locals - pub fn finalize_prologue(&mut self) { - self.body.finalize_prologue(); - } - - /// Insert a ghost statement - pub fn insert_statement(&mut self, stmt: Statement) { - self.body.insert_statement(stmt); - } - - /// Get an index with which to iterate over the body - pub fn new_index(&self) -> MutatorIndex { - self.body.new_index() - } - - /// Decrement the index - pub fn decrement_index(&mut self, idx: &mut MutatorIndex) -> MutatorIndexStatus { - self.body.decrement(idx) - } - - /// Split at the index causing the ghost code to be called - /// after that index - pub fn split(&mut self, idx: &MutatorIndex) { - self.body.split(idx); - } - - /// Inspect the instruction at the index - pub fn inspect(&self, idx: &MutatorIndex) -> Instruction { - self.body.inspect(idx) - } - - /// Finalize the body - pub fn finalize(self) -> Body { - self.body.finalize() - } - - /// Get the span - pub fn span(&self) -> Span { - self.body.span() - - } - - /// Get the unit local - pub fn unit(&self) -> Local { - self.unit - } - - /// Get the violated local - pub fn valid(&self) -> Local { - self.valid - } -} diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs index a34f78778869..a64ab75813ad 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs @@ -50,11 +50,6 @@ pub struct Cache { } impl Cache { - /// Instantiate the cache - pub fn new() -> Self { - Cache::default() - } - /// Register the signature the to the cache /// in the given compilation context, ctx pub fn register(&mut self, ctx: &TyCtxt, sig: Signature) -> @@ -90,15 +85,4 @@ impl Cache { cache.push(Instance::new(sig, instance)); Ok(&cache[cache.len() - 1].instance) } - - /// Fetch the signature sig from the cache - pub fn get(&self, sig: &Signature) -> Result<&MirInstance, MirError> { - let Cache { cache } = self; - for Instance { signature, instance } in cache { - if *sig == *signature { - return Ok(instance); - } - } - Err(MirError::new(format!("Not found: {:?}", sig))) - } } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 1c037644a089..d7ea9e302677 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -1,91 +1,223 @@ use std::collections::HashMap; use rustc_middle::ty::TyCtxt; -use stable_mir::mir::{BorrowKind, ConstOperand, Local, Mutability, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind}; -use stable_mir::ty::{GenericArgKind, Ty, Span, TyKind, RigidTy}; -use super::{MirError, CachedBodyMutator, Cache, Signature, MutatorIndex, Instruction, MutatorIndexStatus}; +use stable_mir::mir::{BasicBlock, Body, Local, Mutability, Operand, Place, Rvalue, Terminator, TerminatorKind, UnwindAction}; +use stable_mir::ty::{GenericArgKind, Ty, Span}; +use crate::kani_middle::transform::body::{CheckType, InsertPosition}; + +use super::{Action, Cache, CollectActions, MirError, MirInstance, Signature}; +use super::super::body::{MutableBody, SourceInstruction}; + +type Result = std::result::Result; pub struct InstrumentationData<'tcx, 'cache> { tcx: TyCtxt<'tcx>, cache: &'cache mut Cache, meta_stack: HashMap, - body: CachedBodyMutator, + local_count: usize, + unit: Local, + valid: Local, + fn_pointers: HashMap, + span: Span, + /// The minimum processed instruction + min_processed: SourceInstruction, + /// The index after which "ghost" instrumentation code may be added + ghost_index: SourceInstruction, + pub body: MutableBody, } impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { - pub fn new(tcx: TyCtxt<'tcx>, cache: &'cache mut Cache, body: CachedBodyMutator) -> Self { - let meta_stack = HashMap::new(); - InstrumentationData { tcx, cache, meta_stack, body } + /// Move bb0 to the end to start instrumentation there. + pub fn prepare_body(body: &mut Body) { + let statements = std::mem::replace(&mut body.blocks[0].statements, Vec::new()); + let terminator = Terminator { kind: TerminatorKind::Goto { target: body.blocks.len() }, + span: body.blocks[0].terminator.span }; + let terminator = std::mem::replace(&mut body.blocks[0].terminator, terminator); + let bb0 = BasicBlock { statements, terminator }; + body.blocks.push(bb0); } - /// Assign lvalue to the address of rvalue with the given span. - pub fn assign_ptr(body: &mut CachedBodyMutator, lvalue: Local, rvalue: Local, span: Span) { - let lvalue = Place::from(lvalue); - let rvalue = Rvalue::AddressOf(Mutability::Not, Place::from(rvalue)); - let kind = StatementKind::Assign(lvalue, rvalue); - body.insert_statement(Statement { kind, span }); + pub fn new(tcx: TyCtxt<'tcx>, cache: &'cache mut Cache, mut body: Body) -> Self { + Self::prepare_body(&mut body); + let span = body.span; + let meta_stack = HashMap::new(); + let local_count = body.locals().len(); + let fn_pointers = HashMap::new(); + let mut body = MutableBody::from(body); + let unit = body.new_local(Ty::new_tuple(&[]), span, Mutability::Not); + let valid = body.new_local(Ty::from_rigid_kind(stable_mir::ty::RigidTy::Bool), span, Mutability::Mut); + let bb = body.blocks().len() - 1; + let min_processed = match body.blocks()[bb].statements.len() { + 0 => SourceInstruction::Terminator { bb }, + n => SourceInstruction::Statement { idx: n - 1, bb } + }; + let ghost_index = SourceInstruction::Terminator { bb: 0 }; + InstrumentationData { tcx, cache, meta_stack, local_count, unit, valid, fn_pointers, span, min_processed, ghost_index, body } } - /// Instrument the code with a call to initialize the monitors. - pub fn instrument_initialize(&mut self) -> Result<(), MirError> { - let instance = - self.cache.register(&self.tcx, Signature::new("KaniInitializeSState", &[]))?; + /// Register the function described by the diagnostic + /// and generic arguments in "Signature". + fn register_fn(&mut self, callee: Signature) -> Result { + let cache = &mut self.cache; + let tcx = &self.tcx; + let fn_pointers = &mut self.fn_pointers; let body = &mut self.body; - body.call(instance, [].to_vec(), body.unit()); + let span = self.span.clone(); + let instance = cache.register(tcx, callee)?; + let func_local = fn_pointers.entry(*instance) + .or_insert_with(|| body.new_local(instance.ty(), span, Mutability::Not)); + Ok(*func_local) + } + + /// Call at source and insert position, using the arguments + /// in args and returning into "dest". + /// This differs from Mutable Body's call in that the + /// function name is cached. + pub fn call(&mut self, + callee: Signature, + args: Vec, + dest: Local) -> Result<()> { + let func_local = self.register_fn(callee)?; + let new_bb = self.body.blocks().len(); + let span = self.body.blocks()[self.min_processed.bb()].terminator.span; + let callee_op = Operand::Copy(Place::from(func_local)); + let args = args + .into_iter() + .map(|v| Operand::Copy(Place { local: v, projection: vec![] })) + .collect(); + let destination = Place::from(dest); + let kind = TerminatorKind::Call { + func: callee_op, + args, + destination, + target: Some(new_bb), + unwind: UnwindAction::Terminate, + }; + let terminator = Terminator { kind, span }; + let source = &mut self.ghost_index; + self.body.insert_terminator(source, InsertPosition::After, terminator); Ok(()) } + /// Instrument the code with a call to initialize the monitors. + pub fn initialize_prologue(&mut self) -> Result<()> { + self.call(Signature::new("KaniStackedBorrowsBeginPrologue", &[]), vec![], self.unit) + } + + pub fn finalize_prologue(&mut self) -> Result<()> { + self.call(Signature::new("KaniStackedBorrowsEndPrologue", &[]), vec![], self.unit) + } + + /// Instrument an assignment to a local + pub fn assign_pointer(&mut self, lvalue: Local, rvalue: Local) { + let source = &mut self.ghost_index; + let position = InsertPosition::After; + self.body.assign_to(Place::from(lvalue), Rvalue::AddressOf(Mutability::Not, Place::from(rvalue)), source, position); + } + /// For some local, say let x: T; /// instrument it with the functions that initialize the stack: /// let ptr_x: *const T = &raw const x; /// initialize_local(ptr_x); - pub fn instrument_local(&mut self, local: usize) -> Result<(), MirError> { - let ty = self.body.local(local).ty; + pub fn instrument_local(&mut self, local: Local) -> Result<()> { + let ty = self.body.locals()[local].ty; let ptr_ty = Ty::new_ptr(ty, Mutability::Not); - let span = self.body.span().clone(); + let span = self.span.clone(); let body = &mut self.body; let local_ptr = - self.meta_stack.entry(local).or_insert_with(|| body.new_local(ptr_ty, Mutability::Not)); - Self::assign_ptr(body, *local_ptr, local, span); - let instance = self.cache.register( - &self.tcx, - Signature::new("KaniInitializeLocal", - &[GenericArgKind::Type(ty)]))?; - body.call(instance, [*local_ptr].to_vec(), body.unit()); + self.meta_stack.entry(local).or_insert_with(|| body.new_local(ptr_ty, span, Mutability::Not)); + let local_ptr = *local_ptr; + self.assign_pointer(local_ptr, local); + self.call(Signature::new("KaniInitializeLocal", &[GenericArgKind::Type(ty)]), vec![local_ptr], self.unit)?; Ok(()) } - /// Instrument a stack reference of the form + /// Split at the minimum processed instruction, + /// allowing instrumentation of ghost code following + /// that source instruction. + pub fn process_instruction(&mut self) { + // If the instruction under processing is a terminator, + // special care is needed; it is impossible to instrument + // "after" a terminator with no target. + // Therefore we handle the terminators manually, + // inserting the terminator into the ghost code, + // then inserting a jump to that terminator. + // These will be called the "enter ghost," + // "execute ghost", and "execute terminator" + // terminators + match self.min_processed { + SourceInstruction::Terminator { bb } => { + let original = self.body.blocks()[bb].terminator.clone(); + let original_span = self.body.blocks()[bb].terminator.span.clone(); + let span = self.span; + let kind = TerminatorKind::Goto { target: 0 }; + let terminator = Terminator { kind, span }; + let source = &mut self.min_processed.clone(); + let enter_ghost_block = source.clone(); + let body = &mut self.body; + body.replace_terminator(source, terminator.clone()); // replace terminator so you can instrument "after" it + body.insert_terminator(source, InsertPosition::After, terminator.clone()); + let execute_terminator_block = source.clone(); + body.insert_terminator(source, InsertPosition::After, terminator.clone()); + let execute_ghost_block = source.clone(); + + // Instrument enter ghost: + let span = original_span; + let target = execute_ghost_block.bb(); + let kind = TerminatorKind::Goto { target }; + let terminator = Terminator { kind, span }; + body.replace_terminator(&enter_ghost_block, terminator); + // Instrument execute ghost: + let target = execute_terminator_block.bb(); + let kind = TerminatorKind::Goto { target }; + let terminator = Terminator { kind, span }; + body.replace_terminator(&execute_ghost_block, terminator); + // Instrument execute terminator + body.replace_terminator(&execute_terminator_block, original); + + self.ghost_index = execute_ghost_block; + }, + SourceInstruction::Statement { idx, bb } => { + // In this case it is simple, merely goto the ghost code + // immdediately. + let span = self.body.blocks()[bb].statements[idx].span; + let target = self.body.blocks().len(); + let kind = TerminatorKind::Goto { target }; + let terminator = Terminator { kind, span }; + let min_processed = &mut self.min_processed.clone(); + self.body.insert_terminator(min_processed, InsertPosition::After, terminator); + self.ghost_index = *min_processed; + } + } + } + + /// Instrument a stack reference of the fo /// lvalue = &rvalue /// with an update to the stacked borrows state, - /// at the code index idx. + /// at the code index source. pub fn instrument_new_stack_reference( &mut self, - idx: &MutatorIndex, lvalue: Local, rvalue: Local, - ) -> Result<(), MirError> { + ) -> Result<()> { // Initialize the constants - let ty = self.body.local(rvalue).ty; + let ty = self.body.locals()[rvalue].ty; let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); let rvalue_ref = self.meta_stack.get(&rvalue).unwrap(); - let instance = self.cache.register( - &self.tcx, - Signature::new("KaniNewMutRefFromValue", &[GenericArgKind::Type(ty)]), - )?; - self.body.call(instance, vec![*lvalue_ref, *rvalue_ref], self.body.unit()); + self.call(Signature::new("KaniNewMutRefFromValue", &[GenericArgKind::Type(ty)]), vec![*lvalue_ref, *rvalue_ref], self.unit)?; Ok(()) } /// Instrument with stack violated / not violated - pub fn instrument_stack_check(&mut self, idx: &MutatorIndex) -> Result<(), MirError> { - let instance = self.cache.register( - &self.tcx, - Signature::new("KaniStackValid", &[]) - )?; - self.body.call(instance, vec![], self.body.valid()); - let instance = self.cache.register_assert(&self.tcx)?; - let msg = format!("Stacked borrows aliasing model violated at {:?}:{:?}", idx.span().get_filename(), idx.span().get_lines()); - self.body.assert(instance, self.body.valid(), msg, idx.span()); + pub fn instrument_stack_check(&mut self) -> Result<()> { + let span = match self.min_processed { + SourceInstruction::Statement { idx, bb } => self.body.blocks()[bb].statements[idx].span, + SourceInstruction::Terminator { bb } => self.body.blocks()[bb].terminator.span, + }; + self.call(Signature::new("KaniStackValid", &[]), vec![], self.valid)?; + let msg = format!("Stacked borrows aliasing model violated at {:?}:{:?}", span.get_filename(), span.get_lines()); + let check_fn = self.cache.register_assert(&self.tcx)?; + let check_type = &CheckType::Assert(*check_fn); + self.body.insert_check(self.tcx, check_type, &mut self.ghost_index, InsertPosition::After, self.valid, &msg); Ok(()) } @@ -95,14 +227,10 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { &mut self, place: Local, ty: Ty, - ) -> Result<(), MirError> { + ) -> Result<()> { // Initialize the constants let place_ref = self.meta_stack.get(&place).unwrap(); - let instance = self.cache.register( - &self.tcx, - Signature::new("KaniStackCheckRef", &[GenericArgKind::Type(ty)]), - )?; - self.body.call(instance, vec![*place_ref], self.body.unit()); + self.call(Signature::new("KaniStackCheckRef", &[GenericArgKind::Type(ty)]), vec![*place_ref], self.unit)?; Ok(()) } @@ -112,14 +240,10 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { &mut self, place: Local, ty: Ty, - ) -> Result<(), MirError> { + ) -> Result<()> { // Initialize the constants let place_ref = self.meta_stack.get(&place).unwrap(); - let instance = self.cache.register( - &self.tcx, - Signature::new("KaniStackCheckPtr", &[GenericArgKind::Type(ty)]), - )?; - self.body.call(instance, vec![*place_ref], self.body.unit()); + self.call(Signature::new("KaniStackCheckPtr", &[GenericArgKind::Type(ty)]), vec![*place_ref], self.unit)?; Ok(()) } @@ -130,15 +254,11 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { created: Local, raw: Local, ty: Ty, - ) -> Result<(), MirError> { + ) -> Result<()> { // Initialize the constants let created_ref = self.meta_stack.get(&created).unwrap(); let reference_ref = self.meta_stack.get(&raw).unwrap(); - let instance = self.cache.register( - &self.tcx, - Signature::new("KaniNewMutRefFromRaw", &[GenericArgKind::Type(ty)]), - )?; - self.body.call(instance, vec![*created_ref, *reference_ref], self.body.unit()); + self.call(Signature::new("KaniNewMutRefFromRaw", &[GenericArgKind::Type(ty)]), vec![*created_ref, *reference_ref], self.unit)?; Ok(()) } @@ -149,193 +269,72 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { created: Local, reference: Local, ty: Ty, - ) -> Result<(), MirError> { + ) -> Result<()> { // Initialize the constants let created_ref = self.meta_stack.get(&created).unwrap(); let reference_ref = self.meta_stack.get(&reference).unwrap(); - let instance = self.cache.register( - &self.tcx, - Signature::new("KaniNewMutRawFromRef", &[GenericArgKind::Type(ty)]), - )?; - self.body.call(instance, vec![*created_ref, *reference_ref], self.body.unit()); + self.call(Signature::new("KaniNewMutRawFromRef", &[GenericArgKind::Type(ty)]), vec![*created_ref, *reference_ref], self.unit)?; Ok(()) } - /// Instrument at the code index idx with the appropriate updates - /// to the stacked borrows state and with assertions for the validity - /// of that state. - pub fn instrument_index(&mut self, idx: &MutatorIndex) -> Result<(), MirError> { - match self.body.inspect(idx) { - Instruction::Stmt(Statement { kind, .. }) => { - match kind { - StatementKind::Assign(to, rvalue) => { - let to = to.clone(); - match rvalue { - Rvalue::Ref(_, BorrowKind::Mut { .. }, from) => { - match from.projection[..] { - [] => { - // Direct reference to stack local - // x = &y - self.instrument_new_stack_reference( - idx, to.local, from.local, - )?; - self.body.split(idx); - } - [ProjectionElem::Deref] => { - // Reborrow - // x : &mut T = &*(y : *mut T OR &mut T) - let from = from.local; // Copy to avoid borrow - let to = to.local; // Copy to avoid borrow - match self.body.local(from).ty.kind() { - TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { - // Reborrow of reference - // Occurs during normal course of reference-from - // raw-pointer -- a reference will be made, then reborrowed. - self.instrument_stack_update_ref(from, ty)?; - self.instrument_stack_check(idx)?; - self.instrument_new_mut_ref_from_raw(to, from, ty)?; - self.body.split(idx); - } - TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { - // Reborrow of raw pointer - self.instrument_stack_update_ptr(from, ty)?; - self.instrument_stack_check(idx)?; - self.instrument_new_mut_ref_from_raw(to, from, ty)?; - self.body.split(idx); - } - _ => {} - } - } - _ => { - eprintln!("Field projections not yet handled"); - } - } - } - Rvalue::AddressOf(Mutability::Mut, from) => { - match from.projection[..] { - [] => { - // x = &raw y - panic!("Addr of not yet handled"); - } - [ProjectionElem::Deref] => { - // x = &raw mut *(y: &mut T OR *mut T) - let from = from.local; // Copy to avoid borrow - let to = to.local; - match self.body.local(from).ty.kind() { - TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { - self.instrument_stack_update_ref(from, ty)?; - self.instrument_stack_check(idx)?; - self.instrument_new_mut_raw_from_ref( - to, from, ty - )?; - self.body.split(idx); - } - _ => { - panic!("Deref case {:?} not yet handled", kind); - } - } - } - _ => {} - } - }, - Rvalue::Use(Operand::Constant(_)) => { - // Do nothing for the constants case - }, - Rvalue::Use(Operand::Copy(_)) => { - eprintln!("Copy not yet handled"); - // Do nothing for the constants case - }, - Rvalue::Use(Operand::Move(_)) => { - eprintln!("Move not yet handled"); - // Do nothing for the constants case - }, - Rvalue::BinaryOp(_, _, _) => { - eprintln!("Binary op not yet handled"); - } - Rvalue::CheckedBinaryOp(_, _, _) => { - eprintln!("Checked binary op not yet handled"); - } - _ => { - panic!("Rvalue kind: {:?} not yet handled", rvalue); - } - } - match to.projection[..] { - [] => { - // Assignment directly to local - Ok(()) - } - [ProjectionElem::Deref] => { - // *x = rvalue - let to = to.local; - match self.body.local(to).ty.kind() { - TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { - self.instrument_stack_update_ref(to, ty)?; - self.instrument_stack_check(idx)?; - self.body.split(idx); - } - TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { - self.instrument_stack_update_ptr(to, ty)?; - self.instrument_stack_check(idx)?; - self.body.split(idx); - } - _ => {} - } - Ok(()) - } - _ => { - eprintln!("Field assignment not yet handled"); - Ok(()) - } - } - } - // The following are not yet handled, however, no info is printed - // to avoid blowups: - StatementKind::Retag(_, _) => Ok(()), - StatementKind::FakeRead(_, _) => Ok(()), - StatementKind::SetDiscriminant { .. } => Ok(()), - StatementKind::Deinit(_) => Ok(()), - StatementKind::StorageLive(_) => Ok(()), - StatementKind::StorageDead(_) => Ok(()), - StatementKind::PlaceMention(_) => Ok(()), - StatementKind::AscribeUserType { .. } => Ok(()), - StatementKind::Coverage(_) => Ok(()), - StatementKind::Intrinsic(_) => Ok(()), - StatementKind::ConstEvalCounter => Ok(()), - StatementKind::Nop => Ok(()), - } - } - Instruction::Term(_) => Ok(()), - } - } - /// Instrument each of the locals collected into values with /// initialization data. - pub fn instrument_locals(&mut self) -> Result<(), MirError> { - self.instrument_initialize()?; - for local in (self.body.arg_locals().len() + 1)..self.body.locals().len() { + pub fn instrument_locals(&mut self) -> Result<()> { + self.initialize_prologue()?; + self.call(Signature::new("KaniInitializeSState", &[]), vec![], self.unit)?; + for local in (self.body.arg_count() + 1)..self.local_count { self.instrument_local(local)? } + self.finalize_prologue()?; Ok(()) } - /// Instrument all of the instructions and terminators in the function body - /// with appropriate updates to the stacked borrows state - /// and with validity assertions on the stacked borrows state. - pub fn instrument_instructions(&mut self) -> Result<(), MirError> { - let mut index = self.body.new_index(); - let mut status = MutatorIndexStatus::Remaining; - while status == MutatorIndexStatus::Remaining { - self.instrument_index(&index)?; - status = self.body.decrement_index(&mut index); + pub fn instruction_actions(&self) -> Vec { + let mut visitor = CollectActions::new(self.body.locals()); + match self.min_processed { + SourceInstruction::Terminator { .. } => { /* not yet handled */ }, + SourceInstruction::Statement { idx, bb } => { + visitor.visit_statement(&self.body.blocks()[bb].statements[idx]); + } } - Ok(()) + visitor.finalize() } - pub fn finalize_prologue(&mut self) { - self.body.finalize_prologue(); + fn instrument_action(&mut self, action: Action) -> Result<()> { + match action { + Action::StackCheck => self.instrument_stack_check(), + Action::NewStackReference { lvalue, rvalue } => self.instrument_new_stack_reference(lvalue, rvalue), + Action::StackUpdateReference { place, ty } => self.instrument_stack_update_ref(place, ty), + Action::NewMutRefFromRaw { lvalue, rvalue, ty } => self.instrument_new_mut_ref_from_raw(lvalue, rvalue, ty), + Action::StackUpdatePointer { place, ty } => self.instrument_stack_update_ptr(place, ty), + Action::NewMutRawFromRef { lvalue, rvalue, ty } => self.instrument_new_mut_raw_from_ref(lvalue, rvalue, ty), + } } - pub fn finalize(self) -> stable_mir::mir::Body { - self.body.finalize() + /// Instrument all of the instructions and terminators in the function body + /// with appropriate updates to the stacked borrows state + /// and with validity assertions on the stacked borrows state. + pub fn instrument_instructions(&mut self) -> Result<()> { + loop { + let actions = self.instruction_actions(); + if actions.len() > 0 { + eprintln!("Instrumenting actions:"); + self.process_instruction(); + } + for action in actions { + eprintln!("Action is: {:?}", action); + self.instrument_action(action)?; + } + self.min_processed = match self.min_processed { + SourceInstruction::Statement { idx: 0, bb: 0 } => { break; }, + SourceInstruction::Statement { idx: 0, bb } => SourceInstruction::Terminator { bb: bb - 1 }, + SourceInstruction::Statement { idx, bb } => SourceInstruction::Statement { idx: idx - 1, bb }, + SourceInstruction::Terminator { bb } if self.body.blocks()[bb].statements.len() > 0 => + SourceInstruction::Statement { idx: self.body.blocks()[bb].statements.len() - 1, bb }, + SourceInstruction::Terminator { bb } if bb > 0 => SourceInstruction::Terminator { bb: bb - 1 }, + SourceInstruction::Terminator { .. } => { break; } + } + } + Ok(()) } } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index ae221d23e665..8e9a75192621 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -10,14 +10,10 @@ pub use stable_mir::Error as MirError; pub use stable_mir::mir::mono::Instance as MirInstance; +mod actions; +use actions::*; mod function_cache; use function_cache::*; -mod cached_body_mutator; -use cached_body_mutator::*; -mod body_mutator; -use body_mutator::*; -mod body_mutation; -use body_mutation::*; mod instrumentation; use instrumentation::*; @@ -130,10 +126,12 @@ impl TransformPass for AliasingPass { { (false, body) } else { - let body = CachedBodyMutator::from(body); + // let body = CachedBodyMutator::from(body); let mut instrumentation_data = InstrumentationData::new(tcx, &mut self.cache, body); - let out = BodyMutationPassState::new(instrumentation_data).finalize(); - (true, out) + // let out = BodyMutationPassState::new(instrumentation_data).finalize(); + instrumentation_data.instrument_locals().unwrap(); + instrumentation_data.instrument_instructions().unwrap(); + (true, instrumentation_data.body.into()) } } } diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 5a0b768c1a53..270585a923d8 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -311,6 +311,14 @@ pub mod sstate { } } +/// Signposts the sections of the instrumentation. +/// Serves no other purpose. +#[rustc_diagnostic_item = "KaniStackedBorrowsBeginPrologue"] +pub fn begin_prologue() { } + +#[rustc_diagnostic_item = "KaniStackedBorrowsEndPrologue"] +pub fn end_prologue() { } + pub fn demonic_nondet() -> bool { crate::any() } diff --git a/tests/expected/aliasing/duplicate_write.expected b/tests/expected/aliasing/duplicate_write.expected index 69eb5e3b9b29..c5757551eff3 100644 --- a/tests/expected/aliasing/duplicate_write.expected +++ b/tests/expected/aliasing/duplicate_write.expected @@ -1 +1 @@ -Failed Checks: Stacked borrows aliasing model violated at "./tests/expected/aliasing/duplicate_write.rs":LineInfo { start_line: 21, start_col: 1, end_line: 21, end_col: 2 } +Failed Checks: Stacked borrows aliasing model violated at "./tests/expected/aliasing/duplicate_write.rs":LineInfo { start_line: 19, start_col: 9, end_line: 19, end_col: 28 } diff --git a/tests/expected/aliasing/duplicate_write_compressed.expected b/tests/expected/aliasing/duplicate_write_compressed.expected index bf6effc16ffd..af4acf5a49fd 100644 --- a/tests/expected/aliasing/duplicate_write_compressed.expected +++ b/tests/expected/aliasing/duplicate_write_compressed.expected @@ -1 +1 @@ -Failed Checks: Stacked borrows aliasing model violated at "./tests/expected/aliasing/duplicate_write_compressed.rs":LineInfo { start_line: 13, start_col: 5, end_line: 13, end_col: 6 } +Failed Checks: Stacked borrows aliasing model violated at "./tests/expected/aliasing/duplicate_write_compressed.rs":LineInfo { start_line: 12, start_col: 9, end_line: 12, end_col: 28 } From f0efc1e1907efd8c6f2cc47d2103892f2042078e Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 22 Aug 2024 22:48:35 -0400 Subject: [PATCH 31/72] Document the actions module. --- .../transform/check_aliasing/actions.rs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs index c790d3cb4017..1e7103d3835b 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs @@ -4,7 +4,7 @@ use stable_mir::mir::{ }; use stable_mir::ty::{RigidTy, Ty, TyKind}; -/// An instrumentation action to be taken +/// Update action to the stacked borrows state #[derive(Debug)] pub enum Action { StackCheck, @@ -15,20 +15,27 @@ pub enum Action { NewMutRawFromRef { lvalue: usize, rvalue: usize, ty: Ty }, } +/// The actions of a statement pub struct CollectActions<'locals> { actions: Vec, + /// The locals, required to ensure that the references + /// and pointers are picked appropriately. locals: &'locals [LocalDecl], } impl<'locals> CollectActions<'locals> { + /// Initialize the struct using the given locals pub fn new(locals: &'locals [LocalDecl]) -> Self { CollectActions { actions: Vec::new(), locals } } + /// Finalize the code actions to be taken pub fn finalize(self) -> Vec { self.actions } + /// Collect the actions for assigning the lvalue + /// to the dereferenced rvalue fn visit_assign_reference_dereference(&mut self, lvalue: Local, rvalue: Local) { match self.locals[rvalue].ty.kind() { TyKind::RigidTy(RigidTy::Ref(_, ty, _)) | TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { @@ -43,6 +50,8 @@ impl<'locals> CollectActions<'locals> { } } + /// Collect the actions for assigning the reference in + /// "place" to the local at "to". fn visit_assign_reference(&mut self, to: Local, from: Place) { match from.projection[..] { [] => { @@ -63,6 +72,8 @@ impl<'locals> CollectActions<'locals> { } } + /// Collect the actions for assigning the data at + /// from to the local at to. fn visit_assign_pointer(&mut self, to: Local, from: Place) { match from.projection[..] { [] => { @@ -90,6 +101,9 @@ impl<'locals> CollectActions<'locals> { } } + /// Collect the actions for a Place, + /// incurring a stack check in the case of a + /// dereference of a pointer or reference fn visit_place(&mut self, place: &Place) { match &place.projection[..] { [] => { @@ -117,6 +131,7 @@ impl<'locals> CollectActions<'locals> { } } + /// Collect the actions for the places of an Rvalue fn visit_rvalue_places(&mut self, rvalue: &Rvalue) { match rvalue { Rvalue::AddressOf(_, place) => { @@ -142,6 +157,7 @@ impl<'locals> CollectActions<'locals> { } } + /// Collect the actions for the places of a statement fn visit_statement_places(&mut self, stmt: &Statement) { match &stmt.kind { StatementKind::Assign(place, rvalue) => { @@ -163,6 +179,10 @@ impl<'locals> CollectActions<'locals> { } } + /// Collect the actions for the places of the statement, + /// then find assignments of pointer values to lvalues + /// and collect updates to the stacked borrows state + /// accordingly pub fn visit_statement(&mut self, stmt: &Statement) { self.visit_statement_places(stmt); match &stmt.kind { From 84ed7017c9ba5905970f13876c4ea532c901a61b Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 22 Aug 2024 23:09:07 -0400 Subject: [PATCH 32/72] Add doc comments for each of the instrumentation data fields. --- .../check_aliasing/instrumentation.rs | 48 +++++++++++++------ .../transform/check_aliasing/mod.rs | 2 +- library/kani/src/aliasing.rs | 8 ---- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index d7ea9e302677..d00cc68d2efc 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -10,24 +10,42 @@ use super::super::body::{MutableBody, SourceInstruction}; type Result = std::result::Result; pub struct InstrumentationData<'tcx, 'cache> { + /// Compilation context, used to fetch resolved generic functions tcx: TyCtxt<'tcx>, + /// Cache of resolved generic functions, + /// potentially populated by previous passes cache: &'cache mut Cache, + /// Map associating each local with a local storing + /// its address on the stack, which is used to associate + /// the metadata. meta_stack: HashMap, + /// The count of the number of locals in the original + /// body local_count: usize, + /// A local storing the unit value unit: Local, + /// A local storing whether the stack is still in a valid + /// state valid: Local, + /// A map associating resolved generic functions with + /// locals in the body that can be used to call them fn_pointers: HashMap, + /// The span of the body span: Span, - /// The minimum processed instruction + /// The minimum processed instruction. + /// All instructions before this one belong to the original + /// source code, and have not yet been analyzed. min_processed: SourceInstruction, - /// The index after which "ghost" instrumentation code may be added + /// The index after which "ghost" instrumentation code + /// may be added. ghost_index: SourceInstruction, - pub body: MutableBody, + /// The body being instrumented + body: MutableBody, } impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// Move bb0 to the end to start instrumentation there. - pub fn prepare_body(body: &mut Body) { + fn prepare_body(body: &mut Body) { let statements = std::mem::replace(&mut body.blocks[0].statements, Vec::new()); let terminator = Terminator { kind: TerminatorKind::Goto { target: body.blocks.len() }, span: body.blocks[0].terminator.span }; @@ -36,6 +54,9 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { body.blocks.push(bb0); } + /// Using a (potentially) pre-populated cache of resolved generic + /// functions, and the StableMir body "body", initialize the instrumentation + /// pass data. pub fn new(tcx: TyCtxt<'tcx>, cache: &'cache mut Cache, mut body: Body) -> Self { Self::prepare_body(&mut body); let span = body.span; @@ -98,15 +119,6 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Ok(()) } - /// Instrument the code with a call to initialize the monitors. - pub fn initialize_prologue(&mut self) -> Result<()> { - self.call(Signature::new("KaniStackedBorrowsBeginPrologue", &[]), vec![], self.unit) - } - - pub fn finalize_prologue(&mut self) -> Result<()> { - self.call(Signature::new("KaniStackedBorrowsEndPrologue", &[]), vec![], self.unit) - } - /// Instrument an assignment to a local pub fn assign_pointer(&mut self, lvalue: Local, rvalue: Local) { let source = &mut self.ghost_index; @@ -280,15 +292,14 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// Instrument each of the locals collected into values with /// initialization data. pub fn instrument_locals(&mut self) -> Result<()> { - self.initialize_prologue()?; self.call(Signature::new("KaniInitializeSState", &[]), vec![], self.unit)?; for local in (self.body.arg_count() + 1)..self.local_count { self.instrument_local(local)? } - self.finalize_prologue()?; Ok(()) } + /// Fetch the actions to be instrumented at the current instruction. pub fn instruction_actions(&self) -> Vec { let mut visitor = CollectActions::new(self.body.locals()); match self.min_processed { @@ -300,6 +311,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { visitor.finalize() } + /// Instrument the action given in "action" with the appropriate + /// update to the stacked borrows state. fn instrument_action(&mut self, action: Action) -> Result<()> { match action { Action::StackCheck => self.instrument_stack_check(), @@ -337,4 +350,9 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { } Ok(()) } + + /// Finalize the instrumentation of the body + pub fn finalize(self) -> MutableBody { + self.body + } } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 8e9a75192621..7e4e00fcdb32 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -131,7 +131,7 @@ impl TransformPass for AliasingPass { // let out = BodyMutationPassState::new(instrumentation_data).finalize(); instrumentation_data.instrument_locals().unwrap(); instrumentation_data.instrument_instructions().unwrap(); - (true, instrumentation_data.body.into()) + (true, instrumentation_data.finalize().into()) } } } diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 270585a923d8..5a0b768c1a53 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -311,14 +311,6 @@ pub mod sstate { } } -/// Signposts the sections of the instrumentation. -/// Serves no other purpose. -#[rustc_diagnostic_item = "KaniStackedBorrowsBeginPrologue"] -pub fn begin_prologue() { } - -#[rustc_diagnostic_item = "KaniStackedBorrowsEndPrologue"] -pub fn end_prologue() { } - pub fn demonic_nondet() -> bool { crate::any() } From 18775911060d9d75881255bdb971ef9358ad5948 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 23 Aug 2024 14:59:35 -0400 Subject: [PATCH 33/72] -visibility, -raw MIR manipulation, -blacklist, +doc Remove "public" visibility from the instrumented functions; they will be found by diagnostic instead. Remove the last bit of raw mir body manipulation, replacing this with a method from stable mir. Remove the function blacklist, replacing this with a whitelist of functions found from the proof harness. Add doc comments where appropriate. --- .../check_aliasing/instrumentation.rs | 33 ++-- .../transform/check_aliasing/mod.rs | 146 ++++++++++-------- .../src/kani_middle/transform/mod.rs | 4 +- library/kani/src/aliasing.rs | 133 ++++++++-------- library/kani/src/lib.rs | 2 +- 5 files changed, 167 insertions(+), 151 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index d00cc68d2efc..a409e6e97b13 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use rustc_middle::ty::TyCtxt; -use stable_mir::mir::{BasicBlock, Body, Local, Mutability, Operand, Place, Rvalue, Terminator, TerminatorKind, UnwindAction}; +use stable_mir::mir::{Body, Local, Mutability, Operand, Place, Rvalue, Terminator, TerminatorKind, UnwindAction}; use stable_mir::ty::{GenericArgKind, Ty, Span}; use crate::kani_middle::transform::body::{CheckType, InsertPosition}; @@ -45,25 +45,35 @@ pub struct InstrumentationData<'tcx, 'cache> { impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// Move bb0 to the end to start instrumentation there. - fn prepare_body(body: &mut Body) { - let statements = std::mem::replace(&mut body.blocks[0].statements, Vec::new()); - let terminator = Terminator { kind: TerminatorKind::Goto { target: body.blocks.len() }, - span: body.blocks[0].terminator.span }; - let terminator = std::mem::replace(&mut body.blocks[0].terminator, terminator); - let bb0 = BasicBlock { statements, terminator }; - body.blocks.push(bb0); + fn prepare_body(body: Body) -> MutableBody { + let mut body = MutableBody::from(body); + let span; + let mut source; + match body.blocks()[0].statements.len() { + 0 => { + source = SourceInstruction::Terminator { bb: 0 }; + span = body.blocks()[0].terminator.span; + }, + _ => { + source = SourceInstruction::Statement { idx: 0, bb: 0 }; + span = body.blocks()[0].terminator.span; + } + } + let kind = TerminatorKind::Goto { target: body.blocks().len() }; + let terminator = Terminator { kind, span }; + body.insert_terminator(&mut source, InsertPosition::Before, terminator); + body } /// Using a (potentially) pre-populated cache of resolved generic /// functions, and the StableMir body "body", initialize the instrumentation /// pass data. - pub fn new(tcx: TyCtxt<'tcx>, cache: &'cache mut Cache, mut body: Body) -> Self { - Self::prepare_body(&mut body); + pub fn new(tcx: TyCtxt<'tcx>, cache: &'cache mut Cache, body: Body) -> Self { let span = body.span; + let mut body = Self::prepare_body(body); let meta_stack = HashMap::new(); let local_count = body.locals().len(); let fn_pointers = HashMap::new(); - let mut body = MutableBody::from(body); let unit = body.new_local(Ty::new_tuple(&[]), span, Mutability::Not); let valid = body.new_local(Ty::from_rigid_kind(stable_mir::ty::RigidTy::Bool), span, Mutability::Mut); let bb = body.blocks().len() - 1; @@ -292,7 +302,6 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// Instrument each of the locals collected into values with /// initialization data. pub fn instrument_locals(&mut self) -> Result<()> { - self.call(Signature::new("KaniInitializeSState", &[]), vec![], self.unit)?; for local in (self.body.arg_count() + 1)..self.local_count { self.instrument_local(local)? } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 7e4e00fcdb32..80bc170db50b 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -4,11 +4,12 @@ //! Implement a pass that instruments code with assertions //! that will fail when the aliasing model is violated. +use stable_mir::mir::mono::MonoItem; +use stable_mir::CrateDef; // Reimport components of mir that conflict with // parts of the sub-pass's API. -pub use stable_mir::Error as MirError; pub use stable_mir::mir::mono::Instance as MirInstance; - +pub use stable_mir::Error as MirError; mod actions; use actions::*; @@ -18,13 +19,17 @@ mod instrumentation; use instrumentation::*; use crate::args::ExtraChecks; -use crate::kani_middle::transform::{TransformPass, TransformationType}; +use crate::kani_middle::reachability::{collect_reachable_items}; +use crate::kani_middle::transform::{TransformPass, TransformationResult, TransformationType}; use crate::kani_queries::QueryDb; -use stable_mir::mir::Body; use rustc_middle::ty::TyCtxt; +use stable_mir::mir::Body; +use std::collections::HashSet; use std::fmt::Debug; use tracing::trace; +use super::GlobalPass; + /// Instrument the code with checks for aliasing model /// violations. /// Cache functions in-between applications of the pass. @@ -51,58 +56,12 @@ use tracing::trace; /// /// Finally, a new body is made from the code + the instrumented /// code. -#[derive(Debug, Default)] -pub struct AliasingPass { - cache: Cache, -} - -impl AliasingPass { - pub fn new() -> AliasingPass { - Default::default() - } +#[derive(Debug)] +struct AliasingPass<'cache> { + cache: &'cache mut Cache, } -/// Functions containing any of the following in their -/// prefix or in their name will be ignored. -/// This allows skipping instrumenting functions that -/// are called by the instrumentation functions. -const ALIASING_BLACKLIST: &'static [&'static str] = &[ - "kani", // Skip kani functions - "std::mem::size_of", // skip size_of:: - "core::num", // Skip numerical ops (like .wrapping_add) - "std::ptr", // Skip pointer manipulation functions - "KaniInitializeSState", - "KaniInitializeLocal", - "KaniStackCheckPtr", - "KaniStackCheckRef", - "KaniNewMutRefFromValue", - "KaniNewMutRawFromRef", - "KaniNewMutRefFromRaw", - "std::array", - "std::ops", - "core::panicking", - "std::rt", - "std::panic", - "core::panic", - "std::fmt", - "std::iter", - "core::ub_checks", - "std::cmp", - "core::slice", - "std::mem", - // This blacklist needs expansion. -]; - -// Currently, the above list of functions is too -// coarse-grained; because all kani functions -// are skipped, many std modules are skipped, -// and kani functions are skipped, this pass -// cannot be used to verify functions -// in those modules, despite the fact that -// only some of those functions in those modules -// are called by the instrumented code. - -impl TransformPass for AliasingPass { +impl<'cache> TransformPass for AliasingPass<'cache> { fn transformation_type() -> TransformationType where Self: Sized, @@ -110,6 +69,39 @@ impl TransformPass for AliasingPass { TransformationType::Instrumentation } + fn is_enabled(&self, _query_db: &QueryDb) -> bool + where + Self: Sized, + { + true + } + + fn transform(&mut self, tcx: TyCtxt, body: Body, instance: MirInstance) -> (bool, Body) { + trace!(function=?instance.name(), "transform: aliasing pass"); + // let body = CachedBodyMutator::from(body); + let mut instrumentation_data = InstrumentationData::new(tcx, &mut self.cache, body); + // let out = BodyMutationPassState::new(instrumentation_data).finalize(); + instrumentation_data.instrument_locals().unwrap(); + instrumentation_data.instrument_instructions().unwrap(); + (true, instrumentation_data.finalize().into()) + } +} + +/// The global aliasing pass keeps a cache of resolved generic functions, +/// and ensures that only the functions that are called +/// from the proof harness itself are instrumented. +#[derive(Debug, Default)] +pub struct GlobalAliasingPass { + cache: Cache, +} + +impl GlobalAliasingPass { + pub fn new() -> Self { + Default::default() + } +} + +impl GlobalPass for GlobalAliasingPass { fn is_enabled(&self, query_db: &QueryDb) -> bool where Self: Sized, @@ -118,20 +110,38 @@ impl TransformPass for AliasingPass { args.ub_check.contains(&ExtraChecks::Aliasing) } - fn transform(&mut self, tcx: TyCtxt, body: Body, instance: MirInstance) -> (bool, Body) { - trace!(function=?instance.name(), "transform: aliasing pass"); - if ALIASING_BLACKLIST - .iter() - .fold(false, |blacklisted, member| blacklisted || instance.name().contains(member)) - { - (false, body) - } else { - // let body = CachedBodyMutator::from(body); - let mut instrumentation_data = InstrumentationData::new(tcx, &mut self.cache, body); - // let out = BodyMutationPassState::new(instrumentation_data).finalize(); - instrumentation_data.instrument_locals().unwrap(); - instrumentation_data.instrument_instructions().unwrap(); - (true, instrumentation_data.finalize().into()) + fn transform( + &mut self, + tcx: TyCtxt, + _call_graph: &crate::kani_middle::reachability::CallGraph, + _starting_items: &[stable_mir::mir::mono::MonoItem], + instances: Vec, + transformer: &mut super::BodyTransformation, + ) { + let mut found = HashSet::new(); + // Collect + for instance in &instances { + if instance.def.all_attrs().into_iter().fold(false, |is_proof, attr| is_proof || attr.as_str().contains("kanitool::proof")) { + let (items, _) = collect_reachable_items(tcx, transformer, &[MonoItem::Fn(*instance)]); + for item in items { + if let MonoItem::Fn(instance) = item { + found.insert(instance); + } + } + } + } + eprintln!("Found is: {:?}", found); + // Instrument + for instance in &instances { + if found.contains(instance) { + found.remove(instance); + let mut pass = AliasingPass { cache: &mut self.cache }; + let (_, body) = + pass.transform(tcx, transformer.body(tcx, *instance), *instance); + transformer + .cache + .insert(*instance, TransformationResult::Modified(body)); + } } } } diff --git a/kani-compiler/src/kani_middle/transform/mod.rs b/kani-compiler/src/kani_middle/transform/mod.rs index 9771af621a62..ef9023649775 100644 --- a/kani-compiler/src/kani_middle/transform/mod.rs +++ b/kani-compiler/src/kani_middle/transform/mod.rs @@ -19,13 +19,13 @@ 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_aliasing::AliasingPass; use crate::kani_middle::transform::check_uninit::{DelayedUbPass, UninitPass}; use crate::kani_middle::transform::check_values::ValidValuePass; use crate::kani_middle::transform::contracts::{AnyModifiesPass, FunctionWithContractPass}; use crate::kani_middle::transform::kani_intrinsics::IntrinsicGeneratorPass; use crate::kani_middle::transform::stubs::{ExternFnStubPass, FnStubPass}; use crate::kani_queries::QueryDb; +use check_aliasing::GlobalAliasingPass; use dump_mir_pass::DumpMirPass; use rustc_middle::ty::TyCtxt; use stable_mir::mir::mono::{Instance, MonoItem}; @@ -90,7 +90,6 @@ impl BodyTransformation { }, ); // Check aliasing - transformer.add_pass(queries, AliasingPass::new()); transformer.add_pass( queries, IntrinsicGeneratorPass { @@ -200,6 +199,7 @@ impl GlobalPasses { pub fn new(queries: &QueryDb, tcx: TyCtxt) -> Self { let mut global_passes = GlobalPasses { global_passes: vec![] }; global_passes.add_global_pass(queries, DelayedUbPass::new(CheckType::new_assert(tcx))); + global_passes.add_global_pass(queries, GlobalAliasingPass::new()); global_passes.add_global_pass(queries, DumpMirPass::new(tcx)); global_passes } diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 5a0b768c1a53..8f16221acf47 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -1,6 +1,3 @@ -const STACK_DEPTH: usize = 15; -type PointerTag = u8; - use crate::mem::{pointer_object, pointer_offset}; use crate::shadow::ShadowMem; @@ -59,90 +56,95 @@ use crate::shadow::ShadowMem; /// This can be used with the restriction that assertions over /// relations between the stacks (such as, for example, equality between /// the top two tags of two different stacks) are never needed. -pub mod sstate { +#[allow(unused)] // All functions hidden; they are queried by diagnostic +mod sstate { + const STACK_DEPTH: usize = 15; + type PointerTag = u8; + use super::*; /// Associate every pointer object with a tag static mut TAGS: ShadowMem = ShadowMem::new(0); /// Next pointer id: the next pointer id in sequence - static mut NEXT_TAG: PointerTag = 0; + const INITIAL_TAG: PointerTag = 0; + static mut NEXT_TAG: PointerTag = INITIAL_TAG; /// Set to true whenever the stack has been /// invalidated by a failed lookup. static mut STACK_VALID: bool = true; #[rustc_diagnostic_item = "KaniStackValid"] - pub fn stack_valid() -> bool { + fn stack_valid() -> bool { unsafe {STACK_VALID} } - #[non_exhaustive] + /// Type of access. + /// To ensure that 1 bit, instead of + /// larger, representations are used in cbmc, + /// this is encoded using associated constants. struct Access; - #[allow(unused)] impl Access { const READ: bool = false; const WRITE: bool = true; } + type AccessBit = bool; - #[non_exhaustive] + /// Type of permission. + /// To ensure that 8 bit, instead of larger, + /// repreesentations are used in cbmc, this + /// is encoded using associated constants. struct Permission; impl Permission { + /// Unique corresponds to the original allocation + /// and to &mut. For each byte, this permission can + /// only be aquired once at any given time in the program, + /// therefore it is called "unique." const UNIQUE: u8 = 0; + /// SharedRW corresponds to a mutable pointer. const SHAREDRW: u8 = 1; + /// SharedRO corresponds to a const pointer const SHAREDRO: u8 = 2; + /// Disabled corresponds to disabling writable pointers + /// during 2-phase borrows (not yet implemented) const DISABLED: u8 = 3; + } - fn grants(access: bool, tag: u8) -> bool { + type PermissionByte = u8; + + impl Permission { + /// Returns whether the access bit is granted by the permission + /// byte + fn grants(access: AccessBit, tag: PermissionByte) -> bool { tag != Self::DISABLED && (access != Access::WRITE || tag != Self::SHAREDRO) } } /// Associate every pointer object with a permission - static mut PERMS: ShadowMem = ShadowMem::new(0); + static mut PERMS: ShadowMem = ShadowMem::new(Permission::UNIQUE); + /// State of the borrows stack monitor for a byte pub(super) mod monitors { - static mut STATE: bool = false; - static mut OBJECT: usize = 0; - static mut OFFSET: usize = 0; - static mut STACK_TAGS: [u8; STACK_DEPTH] = [0; STACK_DEPTH]; - static mut STACK_PERMS: [u8; STACK_DEPTH] = [0; STACK_DEPTH]; - static mut STACK_TOP: usize = 0; - - #[non_exhaustive] struct MonitorState; impl MonitorState { - const UNINIT: bool = false; - const INIT: bool = true; + const ON: bool = true; + const OFF: bool = false; } - use super::*; + /// Whether the monitor is on. Initially, the monitor is + /// "off", and it will remain so until an allocation is found + /// to track. + static mut STATE: bool = MonitorState::OFF; + /// The object being monitored + static mut OBJECT: usize = 0; + /// The offset being monitored + static mut OFFSET: usize = 0; + /// The tags of the pointer objects borrowing the byte + static mut STACK_TAGS: [PointerTag; STACK_DEPTH] = [INITIAL_TAG; STACK_DEPTH]; + /// The permissions of the pointer objects borrowing the byte + static mut STACK_PERMS: [PermissionByte; STACK_DEPTH] = [Permission::UNIQUE; STACK_DEPTH]; + /// The "top" of the stack + static mut STACK_TOP: usize = 0; - /// Monitors: - /// If there are K bytes in the address space, - /// every stacked borrows instrumentation has - /// between 0 and K monitors. - /// These monitors track a single byte of the program, - /// associating it with a stack of pointer values - /// (represented by tags). - /// Whenever a pointer borrows an object containing - /// the byte, its tag is pushed to the stack; - /// when a read or write is performed through this pointer, - /// writes from pointers above its location on the stack - /// are disabled. - /// This function prepares N monitors, - /// writes them to global heap memory, then - /// stores them in pointers. - /// An N+1th monitor is allocated as a "garbage" - /// area to be used when no monitor is picked. - pub fn prepare_monitors() { - unsafe { - OBJECT = 0usize; - OFFSET = 0usize; - STATE = MonitorState::UNINIT; - STACK_TAGS = [NEXT_TAG; STACK_DEPTH]; - STACK_PERMS = [Permission::UNIQUE; STACK_DEPTH]; - STACK_TOP = 0usize; - } - } + use super::*; /// Initialize local when track local is true, picking a monitor, /// and setting its object and offset to within pointer. @@ -151,8 +153,8 @@ pub mod sstate { // for location:location+size_of(U). // Offset has already been picked earlier. unsafe { - if demonic_nondet() && STATE == MonitorState::UNINIT { - STATE = MonitorState::INIT; + if demonic_nondet() && STATE == MonitorState::OFF { + STATE = MonitorState::ON; OBJECT = pointer_object(pointer); OFFSET = 0; crate::assume(OFFSET < std::mem::size_of::()); @@ -170,7 +172,7 @@ pub mod sstate { // Offset has already been picked earlier. unsafe { use self::*; - if STATE == MonitorState::INIT + if STATE == MonitorState::ON && OBJECT == pointer_object(pointer) && OFFSET == pointer_offset(pointer) { @@ -184,7 +186,7 @@ pub mod sstate { pub(super) fn stack_check(tag: u8, access: bool, address: *const U) { unsafe { use self::*; - if STATE == MonitorState::INIT + if STATE == MonitorState::ON && OFFSET == pointer_offset(address) && OBJECT == pointer_object(address) { @@ -210,13 +212,8 @@ pub mod sstate { } } - #[rustc_diagnostic_item = "KaniInitializeSState"] - pub fn initialize() { - self::monitors::prepare_monitors(); - } - /// Push the permissions at the given location - pub fn push(tag: u8, perm: u8, address: *const U) { + fn push(tag: u8, perm: u8, address: *const U) { self::monitors::push(tag, perm, address) } @@ -231,7 +228,7 @@ pub mod sstate { /// of a query to a demonic nondeterminism oracle (when this feature is used) /// and a reference to the stack location. #[rustc_diagnostic_item = "KaniInitializeLocal"] - pub fn initialize_local(pointer: *const U) { + fn initialize_local(pointer: *const U) { unsafe { let tag = NEXT_TAG; TAGS.set(pointer, tag); @@ -242,7 +239,7 @@ pub mod sstate { } #[rustc_diagnostic_item = "KaniStackCheckPtr"] - pub fn stack_check_ptr(pointer_value: *const *mut U) { + fn stack_check_ptr(pointer_value: *const *mut U) { unsafe { let tag = TAGS.get(pointer_value); let perm = PERMS.get(pointer_value); @@ -258,7 +255,7 @@ pub mod sstate { } #[rustc_diagnostic_item = "KaniStackCheckRef"] - pub fn stack_check_ref(pointer_value: *const &mut U) { + fn stack_check_ref(pointer_value: *const &mut U) { stack_check_ptr(pointer_value as *const *mut U); } @@ -267,7 +264,7 @@ pub mod sstate { /// with a new tag, and pushing the new tag to the created reference, stored at /// pointer_to_val. #[rustc_diagnostic_item = "KaniNewMutRefFromValue"] - pub fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { + fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); @@ -281,7 +278,7 @@ pub mod sstate { /// tag, running a stack check on the tag associated with the reference, accessed by /// pointer_to_ref, and pushing the tag to the original location. #[rustc_diagnostic_item = "KaniNewMutRawFromRef"] - pub fn new_mut_raw_from_ref( + fn new_mut_raw_from_ref( pointer_to_created: *const *mut T, pointer_to_ref: *const &mut T, ) { @@ -298,7 +295,7 @@ pub mod sstate { /// tag, running a stack check on the tag associated with the reference, accessed by /// pointer_to_ref, and pushing the tag to the original location. #[rustc_diagnostic_item = "KaniNewMutRefFromRaw"] - pub fn new_mut_ref_from_raw( + fn new_mut_ref_from_raw( pointer_to_created: *const &mut T, pointer_to_ref: *const *mut T, ) { @@ -309,8 +306,8 @@ pub mod sstate { NEXT_TAG += 1; } } -} -pub fn demonic_nondet() -> bool { - crate::any() + fn demonic_nondet() -> bool { + crate::any() + } } diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index 5cdd3853dc9a..cee6bde0eea5 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -23,7 +23,7 @@ // Allow us to use `kani::` to access crate features. extern crate self as kani; -pub mod aliasing; +mod aliasing; pub mod arbitrary; #[cfg(feature = "concrete_playback")] mod concrete_playback; From 4fd5842087840329d4d9fcc344f0f636ac3e09f4 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 23 Aug 2024 15:22:23 -0400 Subject: [PATCH 34/72] Add module level documentation + copyrights --- .../transform/check_aliasing/actions.rs | 7 + .../check_aliasing/function_cache.rs | 4 + .../check_aliasing/instrumentation.rs | 9 + .../transform/check_aliasing/mod.rs | 2 +- library/kani/src/aliasing.rs | 538 +++++++++--------- 5 files changed, 291 insertions(+), 269 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs index 1e7103d3835b..3e735307b47d 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs @@ -1,3 +1,10 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +//! This module contains stacked borrows "actions," +//! or updates to the stacked borrows state, as well as +//! methods that collect the actions that need to be applied from the +//! statements of the code. + use stable_mir::mir::{ BorrowKind, Local, LocalDecl, Mutability, Operand, ProjectionElem, Rvalue, Statement, StatementKind, Place, diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs index a64ab75813ad..e9e589053c61 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs @@ -1,3 +1,7 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +//! This module contains a cache of resolved generic functions + use super::{MirInstance, MirError}; use rustc_middle::ty::TyCtxt; use stable_mir::ty::{GenericArgKind as GenericArg, GenericArgs}; diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index a409e6e97b13..9ace574c9378 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -1,3 +1,12 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +//! This module contains methods handling the data needed +//! for the instrumentation of a single function body with updates from the +//! stacked borrows calculus. These data ensure that each of the original +//! locals is instrumented, that each of the original instructions +//! are instrumented, and that no code that is added by the instrumentation +//! pass itself is instrumented or analyzed. + use std::collections::HashMap; use rustc_middle::ty::TyCtxt; use stable_mir::mir::{Body, Local, Mutability, Operand, Place, Rvalue, Terminator, TerminatorKind, UnwindAction}; diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 80bc170db50b..869417454f0c 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -19,7 +19,7 @@ mod instrumentation; use instrumentation::*; use crate::args::ExtraChecks; -use crate::kani_middle::reachability::{collect_reachable_items}; +use crate::kani_middle::reachability::collect_reachable_items; use crate::kani_middle::transform::{TransformPass, TransformationResult, TransformationType}; use crate::kani_queries::QueryDb; use rustc_middle::ty::TyCtxt; diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 8f16221acf47..7dd780202b09 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -1,313 +1,315 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +#![allow(unused)] // All functions hidden; some may be queried by diagnostic +//! The stacked borrows state. +//! +//! The stacked borrows state associates every pointer value +//! (IE reference or raw pointer) with a unique tag and permission +//! in shadow memory. +//! +//! The tags correspond to the time of the creation of the pointer +//! value, and the permissions correspond to the mutability +//! of the pointer value and its status as a raw pointer or reference. +//! +//! It also associates each byte of the program's memory +//! with a stack of tags, tracking the borrows of the memory +//! containing that byte in temporal order. Every time a +//! pointer value is used, the stack is popped down to that pointer value's +//! tag, effectively marking the borrows that occur after that variable +//! as dead. If the borrows associated with the tags popped are later used, +//! the search for them at that byte fails and the stacked borrows state +//! is considered violated. +//! +//! For example: +//! ```rust +//! let mut x: i32 = 10; +//! // Stack allocate 10 and store it at x. +//! // Stack at addr_of!(x) through addr_of!(x) + 4: +//! // [(0, Permission::UNIQUE)] +//! let y = &mut x; +//! // Make the pointer object `&mut x`. Associate `&mut x` +//! // with the tag and permission `(1, Permission::UNIQUE)` +//! // by associating `addr_of!(y)` with `(1, Permission::UNIQUE)` +//! // in shadow memory. Push the tag to the borrow stacks of +//! // `addr_of!(x)` through `addr_of!(x) + 4` yielding +//! // the stacks [(0, Permission::UNIQUE), (1, Permission::UNIQUE)] +//! let z = y as *mut i32; +//! // Make the pointer object `y as *mut i32`. +//! // associate `addr_of!(z)` and push the stacks as +//! // above with the tag (2, Permission::SHAREDRW), +//! // corresponding to a raw pointer, yielding the stacks +//! // [(0, Permission::UNIQUE), (1, Permission::UNIQUE), +//! // (2, Permission::SHAREDRW)]. +//! *y = 10; +//! // Pop the stack down to the tag associated with the pointer +//! // object created at `&mut x` yielding +//! // [(0, Permission::UNIQUE), (1, Permission::UNIQUE)] +//! unsafe { *(&mut *z) = 10; } +//! // Run stack lookup on the tag associated with the pointer +//! // object created at `y as *mut i32`, ie, (2, Permission::SHAREDRW) +//! // resulting in an error. +//! ``` +//! When demonic nondeterminism is used (currently it is always used), +//! a nondeterminism oracle is queried to select a single byte of the program's +//! memory. This way, if a single byte is ever invalid, the nondeterminism +//! oracle will select it, and allow an error to be thrown. +//! This can be used with the restriction that assertions over +//! relations between the stacks (such as, for example, equality between +//! the top two tags of two different stacks) are never needed. + use crate::mem::{pointer_object, pointer_offset}; use crate::shadow::ShadowMem; -/// The stacked borrows state. -/// -/// The stacked borrows state associates every pointer value -/// (IE reference or raw pointer) with a unique tag and permission -/// in shadow memory. -/// -/// The tags correspond to the time of the creation of the pointer -/// value, and the permissions correspond to the mutability -/// of the pointer value and its status as a raw pointer or reference. -/// -/// It also associates each byte of the program's memory -/// with a stack of tags, tracking the borrows of the memory -/// containing that byte in temporal order. Every time a -/// pointer value is used, the stack is popped down to that pointer value's -/// tag, effectively marking the borrows that occur after that variable -/// as dead. If the borrows associated with the tags popped are later used, -/// the search for them at that byte fails and the stacked borrows state -/// is considered violated. -/// -/// For example: -/// ```rust -/// let mut x: i32 = 10; -/// // Stack allocate 10 and store it at x. -/// // Stack at addr_of!(x) through addr_of!(x) + 4: -/// // [(0, Permission::UNIQUE)] -/// let y = &mut x; -/// // Make the pointer object `&mut x`. Associate `&mut x` -/// // with the tag and permission `(1, Permission::UNIQUE)` -/// // by associating `addr_of!(y)` with `(1, Permission::UNIQUE)` -/// // in shadow memory. Push the tag to the borrow stacks of -/// // `addr_of!(x)` through `addr_of!(x) + 4` yielding -/// // the stacks [(0, Permission::UNIQUE), (1, Permission::UNIQUE)] -/// let z = y as *mut i32; -/// // Make the pointer object `y as *mut i32`. -/// // associate `addr_of!(z)` and push the stacks as -/// // above with the tag (2, Permission::SHAREDRW), -/// // corresponding to a raw pointer, yielding the stacks -/// // [(0, Permission::UNIQUE), (1, Permission::UNIQUE), -/// // (2, Permission::SHAREDRW)]. -/// *y = 10; -/// // Pop the stack down to the tag associated with the pointer -/// // object created at `&mut x` yielding -/// // [(0, Permission::UNIQUE), (1, Permission::UNIQUE)] -/// unsafe { *(&mut *z) = 10; } -/// // Run stack lookup on the tag associated with the pointer -/// // object created at `y as *mut i32`, ie, (2, Permission::SHAREDRW) -/// // resulting in an error. -/// ``` -/// When demonic nondeterminism is used (currently it is always used), -/// a nondeterminism oracle is queried to select a single byte of the program's -/// memory. This way, if a single byte is ever invalid, the nondeterminism -/// oracle will select it, and allow an error to be thrown. -/// This can be used with the restriction that assertions over -/// relations between the stacks (such as, for example, equality between -/// the top two tags of two different stacks) are never needed. -#[allow(unused)] // All functions hidden; they are queried by diagnostic -mod sstate { - const STACK_DEPTH: usize = 15; - type PointerTag = u8; +#[allow(unused)] +const STACK_DEPTH: usize = 15; +type PointerTag = u8; - use super::*; - /// Associate every pointer object with a tag - static mut TAGS: ShadowMem = ShadowMem::new(0); - /// Next pointer id: the next pointer id in sequence - const INITIAL_TAG: PointerTag = 0; - static mut NEXT_TAG: PointerTag = INITIAL_TAG; +use super::*; +/// Associate every pointer object with a tag +static mut TAGS: ShadowMem = ShadowMem::new(0); +/// Next pointer id: the next pointer id in sequence +const INITIAL_TAG: PointerTag = 0; +static mut NEXT_TAG: PointerTag = INITIAL_TAG; - /// Set to true whenever the stack has been - /// invalidated by a failed lookup. - static mut STACK_VALID: bool = true; +/// Set to true whenever the stack has been +/// invalidated by a failed lookup. +static mut STACK_VALID: bool = true; - #[rustc_diagnostic_item = "KaniStackValid"] - fn stack_valid() -> bool { - unsafe {STACK_VALID} - } +#[rustc_diagnostic_item = "KaniStackValid"] +fn stack_valid() -> bool { + unsafe {STACK_VALID} +} - /// Type of access. - /// To ensure that 1 bit, instead of - /// larger, representations are used in cbmc, - /// this is encoded using associated constants. - struct Access; - impl Access { - const READ: bool = false; - const WRITE: bool = true; - } - type AccessBit = bool; +/// Type of access. +/// To ensure that 1 bit, instead of +/// larger, representations are used in cbmc, +/// this is encoded using associated constants. +struct Access; +impl Access { + const READ: bool = false; + const WRITE: bool = true; +} +type AccessBit = bool; - /// Type of permission. - /// To ensure that 8 bit, instead of larger, - /// repreesentations are used in cbmc, this - /// is encoded using associated constants. - struct Permission; - impl Permission { - /// Unique corresponds to the original allocation - /// and to &mut. For each byte, this permission can - /// only be aquired once at any given time in the program, - /// therefore it is called "unique." - const UNIQUE: u8 = 0; - /// SharedRW corresponds to a mutable pointer. - const SHAREDRW: u8 = 1; - /// SharedRO corresponds to a const pointer - const SHAREDRO: u8 = 2; - /// Disabled corresponds to disabling writable pointers - /// during 2-phase borrows (not yet implemented) - const DISABLED: u8 = 3; - } +/// Type of permission. +/// To ensure that 8 bit, instead of larger, +/// repreesentations are used in cbmc, this +/// is encoded using associated constants. +struct Permission; +impl Permission { + /// Unique corresponds to the original allocation + /// and to &mut. For each byte, this permission can + /// only be aquired once at any given time in the program, + /// therefore it is called "unique." + const UNIQUE: u8 = 0; + /// SharedRW corresponds to a mutable pointer. + const SHAREDRW: u8 = 1; + /// SharedRO corresponds to a const pointer + const SHAREDRO: u8 = 2; + /// Disabled corresponds to disabling writable pointers + /// during 2-phase borrows (not yet implemented) + const DISABLED: u8 = 3; +} - type PermissionByte = u8; +type PermissionByte = u8; - impl Permission { - /// Returns whether the access bit is granted by the permission - /// byte - fn grants(access: AccessBit, tag: PermissionByte) -> bool { - tag != Self::DISABLED && (access != Access::WRITE || tag != Self::SHAREDRO) - } +impl Permission { + /// Returns whether the access bit is granted by the permission + /// byte + fn grants(access: AccessBit, tag: PermissionByte) -> bool { + tag != Self::DISABLED && (access != Access::WRITE || tag != Self::SHAREDRO) } +} - /// Associate every pointer object with a permission - static mut PERMS: ShadowMem = ShadowMem::new(Permission::UNIQUE); +/// Associate every pointer object with a permission +static mut PERMS: ShadowMem = ShadowMem::new(Permission::UNIQUE); - /// State of the borrows stack monitor for a byte - pub(super) mod monitors { - struct MonitorState; - impl MonitorState { - const ON: bool = true; - const OFF: bool = false; - } +/// State of the borrows stack monitor for a byte +pub(super) mod monitors { + struct MonitorState; + impl MonitorState { + const ON: bool = true; + const OFF: bool = false; + } - /// Whether the monitor is on. Initially, the monitor is - /// "off", and it will remain so until an allocation is found - /// to track. - static mut STATE: bool = MonitorState::OFF; - /// The object being monitored - static mut OBJECT: usize = 0; - /// The offset being monitored - static mut OFFSET: usize = 0; - /// The tags of the pointer objects borrowing the byte - static mut STACK_TAGS: [PointerTag; STACK_DEPTH] = [INITIAL_TAG; STACK_DEPTH]; - /// The permissions of the pointer objects borrowing the byte - static mut STACK_PERMS: [PermissionByte; STACK_DEPTH] = [Permission::UNIQUE; STACK_DEPTH]; - /// The "top" of the stack - static mut STACK_TOP: usize = 0; + /// Whether the monitor is on. Initially, the monitor is + /// "off", and it will remain so until an allocation is found + /// to track. + static mut STATE: bool = MonitorState::OFF; + /// The object being monitored + static mut OBJECT: usize = 0; + /// The offset being monitored + static mut OFFSET: usize = 0; + /// The tags of the pointer objects borrowing the byte + static mut STACK_TAGS: [PointerTag; STACK_DEPTH] = [INITIAL_TAG; STACK_DEPTH]; + /// The permissions of the pointer objects borrowing the byte + static mut STACK_PERMS: [PermissionByte; STACK_DEPTH] = [Permission::UNIQUE; STACK_DEPTH]; + /// The "top" of the stack + static mut STACK_TOP: usize = 0; - use super::*; + use super::*; - /// Initialize local when track local is true, picking a monitor, - /// and setting its object and offset to within pointer. - pub(super) unsafe fn track_local(tag: u8, pointer: *const U) { - // Decide whether to initialize the stacks - // for location:location+size_of(U). - // Offset has already been picked earlier. - unsafe { - if demonic_nondet() && STATE == MonitorState::OFF { - STATE = MonitorState::ON; - OBJECT = pointer_object(pointer); - OFFSET = 0; - crate::assume(OFFSET < std::mem::size_of::()); - STACK_TAGS[STACK_TOP] = tag; - STACK_PERMS[STACK_TOP] = Permission::UNIQUE; - STACK_TOP += 1; - } + /// Initialize local when track local is true, picking a monitor, + /// and setting its object and offset to within pointer. + pub(super) unsafe fn track_local(tag: u8, pointer: *const U) { + // Decide whether to initialize the stacks + // for location:location+size_of(U). + // Offset has already been picked earlier. + unsafe { + if demonic_nondet() && STATE == MonitorState::OFF { + STATE = MonitorState::ON; + OBJECT = pointer_object(pointer); + OFFSET = 0; + crate::assume(OFFSET < std::mem::size_of::()); + STACK_TAGS[STACK_TOP] = tag; + STACK_PERMS[STACK_TOP] = Permission::UNIQUE; + STACK_TOP += 1; } } + } - /// Push a tag with a permission perm at pointer - pub(super) fn push(tag: u8, perm: u8, pointer: *const U) { - // Decide whether to initialize the stacks - // for location:location+size_of(U). - // Offset has already been picked earlier. - unsafe { - use self::*; - if STATE == MonitorState::ON - && OBJECT == pointer_object(pointer) - && OFFSET == pointer_offset(pointer) - { - STACK_TAGS[STACK_TOP] = tag; - STACK_PERMS[STACK_TOP] = perm; - STACK_TOP += 1; - } + /// Push a tag with a permission perm at pointer + pub(super) fn push(tag: u8, perm: u8, pointer: *const U) { + // Decide whether to initialize the stacks + // for location:location+size_of(U). + // Offset has already been picked earlier. + unsafe { + use self::*; + if STATE == MonitorState::ON + && OBJECT == pointer_object(pointer) + && OFFSET == pointer_offset(pointer) + { + STACK_TAGS[STACK_TOP] = tag; + STACK_PERMS[STACK_TOP] = perm; + STACK_TOP += 1; } } + } - pub(super) fn stack_check(tag: u8, access: bool, address: *const U) { - unsafe { - use self::*; - if STATE == MonitorState::ON - && OFFSET == pointer_offset(address) - && OBJECT == pointer_object(address) - { - let mut found = false; - let mut j = 0; - let mut new_top = 0; - assert!(STACK_TOP < STACK_DEPTH); - while j < STACK_DEPTH { - if j < STACK_TOP { - let id = STACK_TAGS[j]; - let kind = STACK_PERMS[j]; - if Permission::grants(access, kind) && id == tag { - new_top = j + 1; - found = true; - } + pub(super) fn stack_check(tag: u8, access: bool, address: *const U) { + unsafe { + use self::*; + if STATE == MonitorState::ON + && OFFSET == pointer_offset(address) + && OBJECT == pointer_object(address) + { + let mut found = false; + let mut j = 0; + let mut new_top = 0; + assert!(STACK_TOP < STACK_DEPTH); + while j < STACK_DEPTH { + if j < STACK_TOP { + let id = STACK_TAGS[j]; + let kind = STACK_PERMS[j]; + if Permission::grants(access, kind) && id == tag { + new_top = j + 1; + found = true; } - j += 1; } - STACK_TOP = new_top; - STACK_VALID = STACK_VALID && found; + j += 1; } + STACK_TOP = new_top; + STACK_VALID = STACK_VALID && found; } } } +} - /// Push the permissions at the given location - fn push(tag: u8, perm: u8, address: *const U) { - self::monitors::push(tag, perm, address) - } +/// Push the permissions at the given location +fn push(tag: u8, perm: u8, address: *const U) { + self::monitors::push(tag, perm, address) +} - /// Initialize the local stored at reference if initialized is set to false, - /// and track it using a monitor when using demonic nondeterminism. - /// - /// Every function call in the source program stack-allocates - /// the local variables that it uses; references to these - /// variables are only valid after these variables are initialized (first written). - /// Therefore this function can be used by supplying an initialized flag - /// set to true after the first write, a track flag set to the value - /// of a query to a demonic nondeterminism oracle (when this feature is used) - /// and a reference to the stack location. - #[rustc_diagnostic_item = "KaniInitializeLocal"] - fn initialize_local(pointer: *const U) { - unsafe { - let tag = NEXT_TAG; - TAGS.set(pointer, tag); - PERMS.set(pointer, Permission::UNIQUE); - NEXT_TAG += 1; - monitors::track_local(tag, pointer); - } +/// Initialize the local stored at reference if initialized is set to false, +/// and track it using a monitor when using demonic nondeterminism. +/// +/// Every function call in the source program stack-allocates +/// the local variables that it uses; references to these +/// variables are only valid after these variables are initialized (first written). +/// Therefore this function can be used by supplying an initialized flag +/// set to true after the first write, a track flag set to the value +/// of a query to a demonic nondeterminism oracle (when this feature is used) +/// and a reference to the stack location. +#[rustc_diagnostic_item = "KaniInitializeLocal"] +fn initialize_local(pointer: *const U) { + unsafe { + let tag = NEXT_TAG; + TAGS.set(pointer, tag); + PERMS.set(pointer, Permission::UNIQUE); + NEXT_TAG += 1; + monitors::track_local(tag, pointer); } +} - #[rustc_diagnostic_item = "KaniStackCheckPtr"] - fn stack_check_ptr(pointer_value: *const *mut U) { - unsafe { - let tag = TAGS.get(pointer_value); - let perm = PERMS.get(pointer_value); - let pointer = *pointer_value; - for i in 0..std::mem::size_of::() { - for access in [false, true] { - if Permission::grants(access, perm) { - self::monitors::stack_check(tag, access, pointer.byte_add(i)); - } +#[rustc_diagnostic_item = "KaniStackCheckPtr"] +fn stack_check_ptr(pointer_value: *const *mut U) { + unsafe { + let tag = TAGS.get(pointer_value); + let perm = PERMS.get(pointer_value); + let pointer = *pointer_value; + for i in 0..std::mem::size_of::() { + for access in [false, true] { + if Permission::grants(access, perm) { + self::monitors::stack_check(tag, access, pointer.byte_add(i)); } } } } +} - #[rustc_diagnostic_item = "KaniStackCheckRef"] - fn stack_check_ref(pointer_value: *const &mut U) { - stack_check_ptr(pointer_value as *const *mut U); - } +#[rustc_diagnostic_item = "KaniStackCheckRef"] +fn stack_check_ref(pointer_value: *const &mut U) { + stack_check_ptr(pointer_value as *const *mut U); +} - /// Update the stacked borrows state for the case created: &mut T = &mut (referent:T) - /// by associating the location of the created value, stored at pointer_to_created, - /// with a new tag, and pushing the new tag to the created reference, stored at - /// pointer_to_val. - #[rustc_diagnostic_item = "KaniNewMutRefFromValue"] - fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { - unsafe { - // Then associate the lvalue and push it - TAGS.set(pointer_to_created, NEXT_TAG); - push(NEXT_TAG, Permission::SHAREDRW, pointer_to_val); - NEXT_TAG += 1; - } +/// Update the stacked borrows state for the case created: &mut T = &mut (referent:T) +/// by associating the location of the created value, stored at pointer_to_created, +/// with a new tag, and pushing the new tag to the created reference, stored at +/// pointer_to_val. +#[rustc_diagnostic_item = "KaniNewMutRefFromValue"] +fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { + unsafe { + // Then associate the lvalue and push it + TAGS.set(pointer_to_created, NEXT_TAG); + push(NEXT_TAG, Permission::SHAREDRW, pointer_to_val); + NEXT_TAG += 1; } +} - /// Update the stacked borrows state for the case created = (reference: &mut T) as *mut T, - /// associating the location of the created value, stored at pointer_to_created, with a new - /// tag, running a stack check on the tag associated with the reference, accessed by - /// pointer_to_ref, and pushing the tag to the original location. - #[rustc_diagnostic_item = "KaniNewMutRawFromRef"] - fn new_mut_raw_from_ref( - pointer_to_created: *const *mut T, - pointer_to_ref: *const &mut T, - ) { - unsafe { - // Then associate the lvalue and push it - TAGS.set(pointer_to_created, NEXT_TAG); - push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); - NEXT_TAG += 1; - } +/// Update the stacked borrows state for the case created = (reference: &mut T) as *mut T, +/// associating the location of the created value, stored at pointer_to_created, with a new +/// tag, running a stack check on the tag associated with the reference, accessed by +/// pointer_to_ref, and pushing the tag to the original location. +#[rustc_diagnostic_item = "KaniNewMutRawFromRef"] +fn new_mut_raw_from_ref( + pointer_to_created: *const *mut T, + pointer_to_ref: *const &mut T, +) { + unsafe { + // Then associate the lvalue and push it + TAGS.set(pointer_to_created, NEXT_TAG); + push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); + NEXT_TAG += 1; } +} - /// Update the stacked borrows state for the case created = (reference: &mut T) as *mut T, - /// associating the location of the created value, stored at pointer_to_created, with a new - /// tag, running a stack check on the tag associated with the reference, accessed by - /// pointer_to_ref, and pushing the tag to the original location. - #[rustc_diagnostic_item = "KaniNewMutRefFromRaw"] - fn new_mut_ref_from_raw( - pointer_to_created: *const &mut T, - pointer_to_ref: *const *mut T, - ) { - unsafe { - // Then associate the lvalue and push it - TAGS.set(pointer_to_created, NEXT_TAG); - push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); - NEXT_TAG += 1; - } +/// Update the stacked borrows state for the case created = (reference: &mut T) as *mut T, +/// associating the location of the created value, stored at pointer_to_created, with a new +/// tag, running a stack check on the tag associated with the reference, accessed by +/// pointer_to_ref, and pushing the tag to the original location. +#[rustc_diagnostic_item = "KaniNewMutRefFromRaw"] +fn new_mut_ref_from_raw( + pointer_to_created: *const &mut T, + pointer_to_ref: *const *mut T, +) { + unsafe { + // Then associate the lvalue and push it + TAGS.set(pointer_to_created, NEXT_TAG); + push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); + NEXT_TAG += 1; } +} - fn demonic_nondet() -> bool { - crate::any() - } +fn demonic_nondet() -> bool { + crate::any() } From a742547f88bf17c52823fa04a51de730cc8ed3af Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 23 Aug 2024 15:44:32 -0400 Subject: [PATCH 35/72] Add message to assertion --- library/kani/src/aliasing.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 7dd780202b09..240d9d6d1447 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -60,8 +60,6 @@ use crate::mem::{pointer_object, pointer_offset}; use crate::shadow::ShadowMem; -#[allow(unused)] -const STACK_DEPTH: usize = 15; type PointerTag = u8; use super::*; @@ -132,6 +130,9 @@ pub(super) mod monitors { const OFF: bool = false; } + #[allow(unused)] + const STACK_DEPTH: usize = 15; + /// Whether the monitor is on. Initially, the monitor is /// "off", and it will remain so until an allocation is found /// to track. @@ -196,7 +197,7 @@ pub(super) mod monitors { let mut found = false; let mut j = 0; let mut new_top = 0; - assert!(STACK_TOP < STACK_DEPTH); + crate::assert(STACK_TOP < STACK_DEPTH, "Max # of nested borrows (15) exceeded"); while j < STACK_DEPTH { if j < STACK_TOP { let id = STACK_TAGS[j]; From 69fd13682f6d78078593fb110eaa9bdc26a4211d Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 23 Aug 2024 15:52:16 -0400 Subject: [PATCH 36/72] Apply formatter --- .../transform/check_aliasing/actions.rs | 85 ++++----- .../check_aliasing/function_cache.rs | 10 +- .../check_aliasing/instrumentation.rs | 176 ++++++++++++------ .../transform/check_aliasing/mod.rs | 17 +- library/kani/src/aliasing.rs | 12 +- 5 files changed, 180 insertions(+), 120 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs index 3e735307b47d..dc629bd1e0b9 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs @@ -6,8 +6,8 @@ //! statements of the code. use stable_mir::mir::{ - BorrowKind, Local, LocalDecl, Mutability, Operand, ProjectionElem, Rvalue, - Statement, StatementKind, Place, + BorrowKind, Local, LocalDecl, Mutability, Operand, Place, ProjectionElem, Rvalue, Statement, + StatementKind, }; use stable_mir::ty::{RigidTy, Ty, TyKind}; @@ -47,11 +47,7 @@ impl<'locals> CollectActions<'locals> { match self.locals[rvalue].ty.kind() { TyKind::RigidTy(RigidTy::Ref(_, ty, _)) | TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { // reborrow - self.actions.push(Action::NewMutRefFromRaw { - lvalue, - rvalue, - ty, - }); + self.actions.push(Action::NewMutRefFromRaw { lvalue, rvalue, ty }); } _ => {} } @@ -67,7 +63,7 @@ impl<'locals> CollectActions<'locals> { let lvalue = to; let rvalue = from.local.clone(); self.actions.push(Action::NewStackReference { lvalue, rvalue }); - }, + } [ProjectionElem::Deref] => { // Reborrow // x : &mut T = &*(y : *mut T OR &mut T) @@ -93,14 +89,13 @@ impl<'locals> CollectActions<'locals> { let lvalue = to; match self.locals[rvalue].ty.kind() { TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { - self.actions.push(Action::NewMutRawFromRef { - lvalue, - rvalue, - ty, - }); + self.actions.push(Action::NewMutRawFromRef { lvalue, rvalue, ty }); } _ => { - panic!("Dereference of rvalue case not yet handled for raw pointers {:?}", from); + panic!( + "Dereference of rvalue case not yet handled for raw pointers {:?}", + from + ); } } } @@ -116,10 +111,10 @@ impl<'locals> CollectActions<'locals> { [] => { // Direct usage -- no update needed return; - }, + } [ProjectionElem::Deref] => { // Dereference -- instrument stack check - }, + } _ => { // Field access -- not yet handled. return; @@ -129,12 +124,12 @@ impl<'locals> CollectActions<'locals> { TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { self.actions.push(Action::StackUpdateReference { place: place.local, ty }); self.actions.push(Action::StackCheck); - }, + } TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { self.actions.push(Action::StackUpdatePointer { place: place.local, ty }); self.actions.push(Action::StackCheck); - }, - _ => {}, + } + _ => {} } } @@ -143,24 +138,24 @@ impl<'locals> CollectActions<'locals> { match rvalue { Rvalue::AddressOf(_, place) => { self.visit_place(place); - }, + } Rvalue::Ref(_, _, place) => { self.visit_place(place); } // The rest are not yet handled - Rvalue::Aggregate(_, _) => {}, - Rvalue::BinaryOp(_, _, _) => {}, - Rvalue::Cast(_, _, _) => {}, - Rvalue::CheckedBinaryOp(_, _, _) => {}, - Rvalue::CopyForDeref(_) => {}, - Rvalue::Discriminant(_) => {}, - Rvalue::Len(_) => {}, - Rvalue::Repeat(_, _) => {}, - Rvalue::ShallowInitBox(_, _) => {}, - Rvalue::ThreadLocalRef(_) => {}, - Rvalue::NullaryOp(_, _) => {}, - Rvalue::UnaryOp(_, _) => {}, - Rvalue::Use(_) => {}, + Rvalue::Aggregate(_, _) => {} + Rvalue::BinaryOp(_, _, _) => {} + Rvalue::Cast(_, _, _) => {} + Rvalue::CheckedBinaryOp(_, _, _) => {} + Rvalue::CopyForDeref(_) => {} + Rvalue::Discriminant(_) => {} + Rvalue::Len(_) => {} + Rvalue::Repeat(_, _) => {} + Rvalue::ShallowInitBox(_, _) => {} + Rvalue::ThreadLocalRef(_) => {} + Rvalue::NullaryOp(_, _) => {} + Rvalue::UnaryOp(_, _) => {} + Rvalue::Use(_) => {} } } @@ -171,18 +166,18 @@ impl<'locals> CollectActions<'locals> { self.visit_rvalue_places(rvalue); self.visit_place(place); } - StatementKind::FakeRead(_, _) => {}, - StatementKind::SetDiscriminant { .. } => {}, - StatementKind::Deinit(_) => {}, - StatementKind::StorageLive(_) => {}, - StatementKind::StorageDead(_) => {}, - StatementKind::Retag(_, _) => {}, - StatementKind::PlaceMention(_) => {}, - StatementKind::AscribeUserType { .. } => {}, - StatementKind::Coverage(_) => {}, - StatementKind::Intrinsic(_) => {}, - StatementKind::ConstEvalCounter => {}, - StatementKind::Nop => {}, + StatementKind::FakeRead(_, _) => {} + StatementKind::SetDiscriminant { .. } => {} + StatementKind::Deinit(_) => {} + StatementKind::StorageLive(_) => {} + StatementKind::StorageDead(_) => {} + StatementKind::Retag(_, _) => {} + StatementKind::PlaceMention(_) => {} + StatementKind::AscribeUserType { .. } => {} + StatementKind::Coverage(_) => {} + StatementKind::Intrinsic(_) => {} + StatementKind::ConstEvalCounter => {} + StatementKind::Nop => {} } } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs index e9e589053c61..7bbbabf68fd1 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs @@ -2,10 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! This module contains a cache of resolved generic functions -use super::{MirInstance, MirError}; +use super::{MirError, MirInstance}; +use crate::kani_middle::find_fn_def; use rustc_middle::ty::TyCtxt; use stable_mir::ty::{GenericArgKind as GenericArg, GenericArgs}; -use crate::kani_middle::find_fn_def; /// FunctionSignature encapsulates the data /// for rust functions with generic arguments @@ -56,8 +56,7 @@ pub struct Cache { impl Cache { /// Register the signature the to the cache /// in the given compilation context, ctx - pub fn register(&mut self, ctx: &TyCtxt, sig: Signature) -> - Result<&MirInstance, MirError> { + pub fn register(&mut self, ctx: &TyCtxt, sig: Signature) -> Result<&MirInstance, MirError> { let Cache { cache } = self; for i in 0..cache.len() { if sig == cache[i].signature { @@ -72,8 +71,7 @@ impl Cache { } /// Register the kani assertion function - pub fn register_assert(&mut self, ctx: &TyCtxt) -> - Result<&MirInstance, MirError> { + pub fn register_assert(&mut self, ctx: &TyCtxt) -> Result<&MirInstance, MirError> { let diagnostic = "KaniAssert".to_string(); let args = vec![]; let sig = Signature { diagnostic, args }; diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 9ace574c9378..65b2a5dc6e04 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -7,14 +7,16 @@ //! are instrumented, and that no code that is added by the instrumentation //! pass itself is instrumented or analyzed. -use std::collections::HashMap; -use rustc_middle::ty::TyCtxt; -use stable_mir::mir::{Body, Local, Mutability, Operand, Place, Rvalue, Terminator, TerminatorKind, UnwindAction}; -use stable_mir::ty::{GenericArgKind, Ty, Span}; use crate::kani_middle::transform::body::{CheckType, InsertPosition}; +use rustc_middle::ty::TyCtxt; +use stable_mir::mir::{ + Body, Local, Mutability, Operand, Place, Rvalue, Terminator, TerminatorKind, UnwindAction, +}; +use stable_mir::ty::{GenericArgKind, Span, Ty}; +use std::collections::HashMap; -use super::{Action, Cache, CollectActions, MirError, MirInstance, Signature}; use super::super::body::{MutableBody, SourceInstruction}; +use super::{Action, Cache, CollectActions, MirError, MirInstance, Signature}; type Result = std::result::Result; @@ -62,7 +64,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { 0 => { source = SourceInstruction::Terminator { bb: 0 }; span = body.blocks()[0].terminator.span; - }, + } _ => { source = SourceInstruction::Statement { idx: 0, bb: 0 }; span = body.blocks()[0].terminator.span; @@ -84,14 +86,30 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let local_count = body.locals().len(); let fn_pointers = HashMap::new(); let unit = body.new_local(Ty::new_tuple(&[]), span, Mutability::Not); - let valid = body.new_local(Ty::from_rigid_kind(stable_mir::ty::RigidTy::Bool), span, Mutability::Mut); + let valid = body.new_local( + Ty::from_rigid_kind(stable_mir::ty::RigidTy::Bool), + span, + Mutability::Mut, + ); let bb = body.blocks().len() - 1; let min_processed = match body.blocks()[bb].statements.len() { 0 => SourceInstruction::Terminator { bb }, - n => SourceInstruction::Statement { idx: n - 1, bb } + n => SourceInstruction::Statement { idx: n - 1, bb }, }; let ghost_index = SourceInstruction::Terminator { bb: 0 }; - InstrumentationData { tcx, cache, meta_stack, local_count, unit, valid, fn_pointers, span, min_processed, ghost_index, body } + InstrumentationData { + tcx, + cache, + meta_stack, + local_count, + unit, + valid, + fn_pointers, + span, + min_processed, + ghost_index, + body, + } } /// Register the function described by the diagnostic @@ -103,7 +121,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let body = &mut self.body; let span = self.span.clone(); let instance = cache.register(tcx, callee)?; - let func_local = fn_pointers.entry(*instance) + let func_local = fn_pointers + .entry(*instance) .or_insert_with(|| body.new_local(instance.ty(), span, Mutability::Not)); Ok(*func_local) } @@ -112,10 +131,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// in args and returning into "dest". /// This differs from Mutable Body's call in that the /// function name is cached. - pub fn call(&mut self, - callee: Signature, - args: Vec, - dest: Local) -> Result<()> { + pub fn call(&mut self, callee: Signature, args: Vec, dest: Local) -> Result<()> { let func_local = self.register_fn(callee)?; let new_bb = self.body.blocks().len(); let span = self.body.blocks()[self.min_processed.bb()].terminator.span; @@ -142,7 +158,12 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { pub fn assign_pointer(&mut self, lvalue: Local, rvalue: Local) { let source = &mut self.ghost_index; let position = InsertPosition::After; - self.body.assign_to(Place::from(lvalue), Rvalue::AddressOf(Mutability::Not, Place::from(rvalue)), source, position); + self.body.assign_to( + Place::from(lvalue), + Rvalue::AddressOf(Mutability::Not, Place::from(rvalue)), + source, + position, + ); } /// For some local, say let x: T; @@ -154,11 +175,17 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let ptr_ty = Ty::new_ptr(ty, Mutability::Not); let span = self.span.clone(); let body = &mut self.body; - let local_ptr = - self.meta_stack.entry(local).or_insert_with(|| body.new_local(ptr_ty, span, Mutability::Not)); + let local_ptr = self + .meta_stack + .entry(local) + .or_insert_with(|| body.new_local(ptr_ty, span, Mutability::Not)); let local_ptr = *local_ptr; self.assign_pointer(local_ptr, local); - self.call(Signature::new("KaniInitializeLocal", &[GenericArgKind::Type(ty)]), vec![local_ptr], self.unit)?; + self.call( + Signature::new("KaniInitializeLocal", &[GenericArgKind::Type(ty)]), + vec![local_ptr], + self.unit, + )?; Ok(()) } @@ -206,7 +233,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { body.replace_terminator(&execute_terminator_block, original); self.ghost_index = execute_ghost_block; - }, + } SourceInstruction::Statement { idx, bb } => { // In this case it is simple, merely goto the ghost code // immdediately. @@ -225,16 +252,16 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// lvalue = &rvalue /// with an update to the stacked borrows state, /// at the code index source. - pub fn instrument_new_stack_reference( - &mut self, - lvalue: Local, - rvalue: Local, - ) -> Result<()> { + pub fn instrument_new_stack_reference(&mut self, lvalue: Local, rvalue: Local) -> Result<()> { // Initialize the constants let ty = self.body.locals()[rvalue].ty; let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); let rvalue_ref = self.meta_stack.get(&rvalue).unwrap(); - self.call(Signature::new("KaniNewMutRefFromValue", &[GenericArgKind::Type(ty)]), vec![*lvalue_ref, *rvalue_ref], self.unit)?; + self.call( + Signature::new("KaniNewMutRefFromValue", &[GenericArgKind::Type(ty)]), + vec![*lvalue_ref, *rvalue_ref], + self.unit, + )?; Ok(()) } @@ -245,36 +272,47 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { SourceInstruction::Terminator { bb } => self.body.blocks()[bb].terminator.span, }; self.call(Signature::new("KaniStackValid", &[]), vec![], self.valid)?; - let msg = format!("Stacked borrows aliasing model violated at {:?}:{:?}", span.get_filename(), span.get_lines()); + let msg = format!( + "Stacked borrows aliasing model violated at {:?}:{:?}", + span.get_filename(), + span.get_lines() + ); let check_fn = self.cache.register_assert(&self.tcx)?; let check_type = &CheckType::Assert(*check_fn); - self.body.insert_check(self.tcx, check_type, &mut self.ghost_index, InsertPosition::After, self.valid, &msg); + self.body.insert_check( + self.tcx, + check_type, + &mut self.ghost_index, + InsertPosition::After, + self.valid, + &msg, + ); Ok(()) } /// Instrument a validity assertion on the stacked borrows state /// at idx for (place: &mut T). - pub fn instrument_stack_update_ref( - &mut self, - place: Local, - ty: Ty, - ) -> Result<()> { + pub fn instrument_stack_update_ref(&mut self, place: Local, ty: Ty) -> Result<()> { // Initialize the constants let place_ref = self.meta_stack.get(&place).unwrap(); - self.call(Signature::new("KaniStackCheckRef", &[GenericArgKind::Type(ty)]), vec![*place_ref], self.unit)?; + self.call( + Signature::new("KaniStackCheckRef", &[GenericArgKind::Type(ty)]), + vec![*place_ref], + self.unit, + )?; Ok(()) } /// Instrument a validity assertion on the stacked borrows state /// at idx for (place: *const T). - pub fn instrument_stack_update_ptr( - &mut self, - place: Local, - ty: Ty, - ) -> Result<()> { + pub fn instrument_stack_update_ptr(&mut self, place: Local, ty: Ty) -> Result<()> { // Initialize the constants let place_ref = self.meta_stack.get(&place).unwrap(); - self.call(Signature::new("KaniStackCheckPtr", &[GenericArgKind::Type(ty)]), vec![*place_ref], self.unit)?; + self.call( + Signature::new("KaniStackCheckPtr", &[GenericArgKind::Type(ty)]), + vec![*place_ref], + self.unit, + )?; Ok(()) } @@ -289,7 +327,11 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { // Initialize the constants let created_ref = self.meta_stack.get(&created).unwrap(); let reference_ref = self.meta_stack.get(&raw).unwrap(); - self.call(Signature::new("KaniNewMutRefFromRaw", &[GenericArgKind::Type(ty)]), vec![*created_ref, *reference_ref], self.unit)?; + self.call( + Signature::new("KaniNewMutRefFromRaw", &[GenericArgKind::Type(ty)]), + vec![*created_ref, *reference_ref], + self.unit, + )?; Ok(()) } @@ -304,7 +346,11 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { // Initialize the constants let created_ref = self.meta_stack.get(&created).unwrap(); let reference_ref = self.meta_stack.get(&reference).unwrap(); - self.call(Signature::new("KaniNewMutRawFromRef", &[GenericArgKind::Type(ty)]), vec![*created_ref, *reference_ref], self.unit)?; + self.call( + Signature::new("KaniNewMutRawFromRef", &[GenericArgKind::Type(ty)]), + vec![*created_ref, *reference_ref], + self.unit, + )?; Ok(()) } @@ -321,7 +367,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { pub fn instruction_actions(&self) -> Vec { let mut visitor = CollectActions::new(self.body.locals()); match self.min_processed { - SourceInstruction::Terminator { .. } => { /* not yet handled */ }, + SourceInstruction::Terminator { .. } => { /* not yet handled */ } SourceInstruction::Statement { idx, bb } => { visitor.visit_statement(&self.body.blocks()[bb].statements[idx]); } @@ -334,11 +380,19 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { fn instrument_action(&mut self, action: Action) -> Result<()> { match action { Action::StackCheck => self.instrument_stack_check(), - Action::NewStackReference { lvalue, rvalue } => self.instrument_new_stack_reference(lvalue, rvalue), - Action::StackUpdateReference { place, ty } => self.instrument_stack_update_ref(place, ty), - Action::NewMutRefFromRaw { lvalue, rvalue, ty } => self.instrument_new_mut_ref_from_raw(lvalue, rvalue, ty), + Action::NewStackReference { lvalue, rvalue } => { + self.instrument_new_stack_reference(lvalue, rvalue) + } + Action::StackUpdateReference { place, ty } => { + self.instrument_stack_update_ref(place, ty) + } + Action::NewMutRefFromRaw { lvalue, rvalue, ty } => { + self.instrument_new_mut_ref_from_raw(lvalue, rvalue, ty) + } Action::StackUpdatePointer { place, ty } => self.instrument_stack_update_ptr(place, ty), - Action::NewMutRawFromRef { lvalue, rvalue, ty } => self.instrument_new_mut_raw_from_ref(lvalue, rvalue, ty), + Action::NewMutRawFromRef { lvalue, rvalue, ty } => { + self.instrument_new_mut_raw_from_ref(lvalue, rvalue, ty) + } } } @@ -357,13 +411,29 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { self.instrument_action(action)?; } self.min_processed = match self.min_processed { - SourceInstruction::Statement { idx: 0, bb: 0 } => { break; }, - SourceInstruction::Statement { idx: 0, bb } => SourceInstruction::Terminator { bb: bb - 1 }, - SourceInstruction::Statement { idx, bb } => SourceInstruction::Statement { idx: idx - 1, bb }, - SourceInstruction::Terminator { bb } if self.body.blocks()[bb].statements.len() > 0 => - SourceInstruction::Statement { idx: self.body.blocks()[bb].statements.len() - 1, bb }, - SourceInstruction::Terminator { bb } if bb > 0 => SourceInstruction::Terminator { bb: bb - 1 }, - SourceInstruction::Terminator { .. } => { break; } + SourceInstruction::Statement { idx: 0, bb: 0 } => { + break; + } + SourceInstruction::Statement { idx: 0, bb } => { + SourceInstruction::Terminator { bb: bb - 1 } + } + SourceInstruction::Statement { idx, bb } => { + SourceInstruction::Statement { idx: idx - 1, bb } + } + SourceInstruction::Terminator { bb } + if self.body.blocks()[bb].statements.len() > 0 => + { + SourceInstruction::Statement { + idx: self.body.blocks()[bb].statements.len() - 1, + bb, + } + } + SourceInstruction::Terminator { bb } if bb > 0 => { + SourceInstruction::Terminator { bb: bb - 1 } + } + SourceInstruction::Terminator { .. } => { + break; + } } } Ok(()) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 869417454f0c..0f30c1ff51f3 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -121,8 +121,14 @@ impl GlobalPass for GlobalAliasingPass { let mut found = HashSet::new(); // Collect for instance in &instances { - if instance.def.all_attrs().into_iter().fold(false, |is_proof, attr| is_proof || attr.as_str().contains("kanitool::proof")) { - let (items, _) = collect_reachable_items(tcx, transformer, &[MonoItem::Fn(*instance)]); + if instance + .def + .all_attrs() + .into_iter() + .fold(false, |is_proof, attr| is_proof || attr.as_str().contains("kanitool::proof")) + { + let (items, _) = + collect_reachable_items(tcx, transformer, &[MonoItem::Fn(*instance)]); for item in items { if let MonoItem::Fn(instance) = item { found.insert(instance); @@ -136,11 +142,8 @@ impl GlobalPass for GlobalAliasingPass { if found.contains(instance) { found.remove(instance); let mut pass = AliasingPass { cache: &mut self.cache }; - let (_, body) = - pass.transform(tcx, transformer.body(tcx, *instance), *instance); - transformer - .cache - .insert(*instance, TransformationResult::Modified(body)); + let (_, body) = pass.transform(tcx, transformer.body(tcx, *instance), *instance); + transformer.cache.insert(*instance, TransformationResult::Modified(body)); } } } diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 240d9d6d1447..3243f67634d2 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -75,7 +75,7 @@ static mut STACK_VALID: bool = true; #[rustc_diagnostic_item = "KaniStackValid"] fn stack_valid() -> bool { - unsafe {STACK_VALID} + unsafe { STACK_VALID } } /// Type of access. @@ -282,10 +282,7 @@ fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: /// tag, running a stack check on the tag associated with the reference, accessed by /// pointer_to_ref, and pushing the tag to the original location. #[rustc_diagnostic_item = "KaniNewMutRawFromRef"] -fn new_mut_raw_from_ref( - pointer_to_created: *const *mut T, - pointer_to_ref: *const &mut T, -) { +fn new_mut_raw_from_ref(pointer_to_created: *const *mut T, pointer_to_ref: *const &mut T) { unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); @@ -299,10 +296,7 @@ fn new_mut_raw_from_ref( /// tag, running a stack check on the tag associated with the reference, accessed by /// pointer_to_ref, and pushing the tag to the original location. #[rustc_diagnostic_item = "KaniNewMutRefFromRaw"] -fn new_mut_ref_from_raw( - pointer_to_created: *const &mut T, - pointer_to_ref: *const *mut T, -) { +fn new_mut_ref_from_raw(pointer_to_created: *const &mut T, pointer_to_ref: *const *mut T) { unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); From 6b76832551b66fa103a7a9e0ce4093a126777a41 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 23 Aug 2024 16:48:02 -0400 Subject: [PATCH 37/72] Remove extraneous arg --- kani-compiler/src/args.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/kani-compiler/src/args.rs b/kani-compiler/src/args.rs index 48028ed898b8..ea80c2cf8926 100644 --- a/kani-compiler/src/args.rs +++ b/kani-compiler/src/args.rs @@ -82,9 +82,6 @@ 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, /// Check for violations of pointer aliasing model Aliasing, /// Check for using uninitialized memory. From 3376a93157c469cc258038fe8e89f78c16082922 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Sun, 25 Aug 2024 21:12:51 -0400 Subject: [PATCH 38/72] Begin implementing artem's comments --- kani-compiler/src/kani_middle/reachability.rs | 12 +++++++ .../transform/check_aliasing/actions.rs | 4 +-- .../check_aliasing/function_cache.rs | 15 +-------- .../check_aliasing/instrumentation.rs | 10 +----- .../transform/check_aliasing/mod.rs | 32 +++++++++---------- .../aliasing/duplicate_write.expected | 3 +- 6 files changed, 33 insertions(+), 43 deletions(-) diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index d2c9d50515c4..e380fe076fe5 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -645,6 +645,18 @@ impl CallGraph { } Ok(()) } + + /// Get all items adjacent to the current item in the + /// call graph. + pub fn adjacencies(&self, node: MonoItem) -> Vec<&MonoItem> { + match self.edges.get(&Node(node)) { + None => vec![], + Some(list) => + list.iter() + .map(|collected| { &collected.0.item }) + .collect() + } + } } impl Display for Node { diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs index dc629bd1e0b9..6f8943b809a6 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs @@ -71,7 +71,7 @@ impl<'locals> CollectActions<'locals> { let rvalue = from.local; // Copy to avoid borrow self.visit_assign_reference_dereference(lvalue, rvalue); } - _ => { /* not yet handled */ } + _ => { eprintln!("not yet handled: assignment to reference {:?}", from) } } } @@ -120,7 +120,7 @@ impl<'locals> CollectActions<'locals> { return; } }; - match self.locals[place.local].ty.kind() { + match place.ty(self.locals).unwrap().kind() { TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { self.actions.push(Action::StackUpdateReference { place: place.local, ty }); self.actions.push(Action::StackCheck); diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs index 7bbbabf68fd1..507dc0216e15 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs @@ -72,19 +72,6 @@ impl Cache { /// Register the kani assertion function pub fn register_assert(&mut self, ctx: &TyCtxt) -> Result<&MirInstance, MirError> { - let diagnostic = "KaniAssert".to_string(); - let args = vec![]; - let sig = Signature { diagnostic, args }; - let Cache { cache } = self; - for i in 0..cache.len() { - if sig == cache[i].signature { - return Ok(&cache[i].instance); - } - } - let fndef = find_fn_def(*ctx, &sig.diagnostic) - .ok_or(MirError::new(format!("Not found: {}", &sig.diagnostic)))?; - let instance = super::MirInstance::resolve(fndef, &GenericArgs(sig.args.clone()))?; - cache.push(Instance::new(sig, instance)); - Ok(&cache[cache.len() - 1].instance) + self.register(ctx, Signature::new("KaniAssert", &[])) } } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 65b2a5dc6e04..6c121c97bbad 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -267,16 +267,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// Instrument with stack violated / not violated pub fn instrument_stack_check(&mut self) -> Result<()> { - let span = match self.min_processed { - SourceInstruction::Statement { idx, bb } => self.body.blocks()[bb].statements[idx].span, - SourceInstruction::Terminator { bb } => self.body.blocks()[bb].terminator.span, - }; self.call(Signature::new("KaniStackValid", &[]), vec![], self.valid)?; - let msg = format!( - "Stacked borrows aliasing model violated at {:?}:{:?}", - span.get_filename(), - span.get_lines() - ); + let msg = "Stacked borrows aliasing model violated."; let check_fn = self.cache.register_assert(&self.tcx)?; let check_type = &CheckType::Assert(*check_fn); self.body.insert_check( diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 0f30c1ff51f3..d05f55dcc055 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -19,12 +19,11 @@ mod instrumentation; use instrumentation::*; use crate::args::ExtraChecks; -use crate::kani_middle::reachability::collect_reachable_items; use crate::kani_middle::transform::{TransformPass, TransformationResult, TransformationType}; use crate::kani_queries::QueryDb; use rustc_middle::ty::TyCtxt; use stable_mir::mir::Body; -use std::collections::HashSet; +use std::collections::{HashSet, VecDeque}; use std::fmt::Debug; use tracing::trace; @@ -113,12 +112,13 @@ impl GlobalPass for GlobalAliasingPass { fn transform( &mut self, tcx: TyCtxt, - _call_graph: &crate::kani_middle::reachability::CallGraph, + call_graph: &crate::kani_middle::reachability::CallGraph, _starting_items: &[stable_mir::mir::mono::MonoItem], instances: Vec, transformer: &mut super::BodyTransformation, ) { let mut found = HashSet::new(); + let mut queue = VecDeque::new(); // Collect for instance in &instances { if instance @@ -127,23 +127,21 @@ impl GlobalPass for GlobalAliasingPass { .into_iter() .fold(false, |is_proof, attr| is_proof || attr.as_str().contains("kanitool::proof")) { - let (items, _) = - collect_reachable_items(tcx, transformer, &[MonoItem::Fn(*instance)]); - for item in items { - if let MonoItem::Fn(instance) = item { - found.insert(instance); - } + if found.insert(instance) { + queue.push_back(instance) } } } - eprintln!("Found is: {:?}", found); - // Instrument - for instance in &instances { - if found.contains(instance) { - found.remove(instance); - let mut pass = AliasingPass { cache: &mut self.cache }; - let (_, body) = pass.transform(tcx, transformer.body(tcx, *instance), *instance); - transformer.cache.insert(*instance, TransformationResult::Modified(body)); + while let Some(instance) = queue.pop_front() { + let mut pass = AliasingPass { cache: &mut self.cache }; + let (_, body) = pass.transform(tcx, transformer.body(tcx, *instance), *instance); + transformer.cache.insert(*instance, TransformationResult::Modified(body)); + for node in call_graph.adjacencies(MonoItem::Fn(*instance).clone()) { + if let MonoItem::Fn(adjacent) = node { + if found.insert(adjacent) { + queue.push_back(adjacent); + } + } } } } diff --git a/tests/expected/aliasing/duplicate_write.expected b/tests/expected/aliasing/duplicate_write.expected index c5757551eff3..d117ac5c1d45 100644 --- a/tests/expected/aliasing/duplicate_write.expected +++ b/tests/expected/aliasing/duplicate_write.expected @@ -1 +1,2 @@ -Failed Checks: Stacked borrows aliasing model violated at "./tests/expected/aliasing/duplicate_write.rs":LineInfo { start_line: 19, start_col: 9, end_line: 19, end_col: 28 } +Failed Checks: Stacked borrows aliasing model violated. + File: "./tests/expected/aliasing/duplicate_write.rs", line 19, in main From dc332a7ecb08d6cea81b2afec983d11151735314 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Sun, 25 Aug 2024 21:26:07 -0400 Subject: [PATCH 39/72] Fix code actions --- .../transform/check_aliasing/actions.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs index 6f8943b809a6..87cba01aeebc 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs @@ -120,16 +120,15 @@ impl<'locals> CollectActions<'locals> { return; } }; - match place.ty(self.locals).unwrap().kind() { - TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { - self.actions.push(Action::StackUpdateReference { place: place.local, ty }); - self.actions.push(Action::StackCheck); - } - TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { - self.actions.push(Action::StackUpdatePointer { place: place.local, ty }); - self.actions.push(Action::StackCheck); - } - _ => {} + if self.locals[place.local].ty.kind().is_ref() { + let ty = place.ty(self.locals).unwrap(); + self.actions.push(Action::StackUpdateReference { place: place.local, ty }); + self.actions.push(Action::StackCheck); + } + if self.locals[place.local].ty.kind().is_raw_ptr() { + let ty = place.ty(self.locals).unwrap(); + self.actions.push(Action::StackUpdatePointer { place: place.local, ty }); + self.actions.push(Action::StackCheck); } } From fb388f3e6430f2b6a42bf1a1feec228a1afd0759 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Sun, 25 Aug 2024 21:43:26 -0400 Subject: [PATCH 40/72] Redo expected files with new line info --- tests/expected/aliasing/control_flow.expected | 5 ++++- tests/expected/aliasing/duplicate_write_compressed.expected | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/expected/aliasing/control_flow.expected b/tests/expected/aliasing/control_flow.expected index 8c3030151008..d48405d178d1 100644 --- a/tests/expected/aliasing/control_flow.expected +++ b/tests/expected/aliasing/control_flow.expected @@ -1 +1,4 @@ -Failed Checks: Stacked borrows aliasing model violated at "./tests/expected/aliasing/control_flow.rs":LineInfo { start_line: 22, start_col: 17, end_line: 22, end_col: 36 } +Failed Checks: Stacked borrows aliasing model violated. + File: "./tests/expected/aliasing/control_flow.rs", line 21, in main +Failed Checks: Stacked borrows aliasing model violated. + File: "./tests/expected/aliasing/control_flow.rs", line 22, in main diff --git a/tests/expected/aliasing/duplicate_write_compressed.expected b/tests/expected/aliasing/duplicate_write_compressed.expected index af4acf5a49fd..7181cb4c2385 100644 --- a/tests/expected/aliasing/duplicate_write_compressed.expected +++ b/tests/expected/aliasing/duplicate_write_compressed.expected @@ -1 +1,2 @@ -Failed Checks: Stacked borrows aliasing model violated at "./tests/expected/aliasing/duplicate_write_compressed.rs":LineInfo { start_line: 12, start_col: 9, end_line: 12, end_col: 28 } +Failed Checks: Stacked borrows aliasing model violated. + File: "./tests/expected/aliasing/duplicate_write_compressed.rs", line 12, in main From 0c17fddc53c78e07d54756d0e3aa7cb158da8299 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Sun, 25 Aug 2024 21:53:37 -0400 Subject: [PATCH 41/72] Notify the user of all unhandled cases --- .../transform/check_aliasing/actions.rs | 42 ++----------------- 1 file changed, 3 insertions(+), 39 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs index 87cba01aeebc..727407adc56b 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs @@ -142,19 +142,7 @@ impl<'locals> CollectActions<'locals> { self.visit_place(place); } // The rest are not yet handled - Rvalue::Aggregate(_, _) => {} - Rvalue::BinaryOp(_, _, _) => {} - Rvalue::Cast(_, _, _) => {} - Rvalue::CheckedBinaryOp(_, _, _) => {} - Rvalue::CopyForDeref(_) => {} - Rvalue::Discriminant(_) => {} - Rvalue::Len(_) => {} - Rvalue::Repeat(_, _) => {} - Rvalue::ShallowInitBox(_, _) => {} - Rvalue::ThreadLocalRef(_) => {} - Rvalue::NullaryOp(_, _) => {} - Rvalue::UnaryOp(_, _) => {} - Rvalue::Use(_) => {} + _ => { eprintln!("Not yet handled: {:?}", rvalue); } } } @@ -165,18 +153,7 @@ impl<'locals> CollectActions<'locals> { self.visit_rvalue_places(rvalue); self.visit_place(place); } - StatementKind::FakeRead(_, _) => {} - StatementKind::SetDiscriminant { .. } => {} - StatementKind::Deinit(_) => {} - StatementKind::StorageLive(_) => {} - StatementKind::StorageDead(_) => {} - StatementKind::Retag(_, _) => {} - StatementKind::PlaceMention(_) => {} - StatementKind::AscribeUserType { .. } => {} - StatementKind::Coverage(_) => {} - StatementKind::Intrinsic(_) => {} - StatementKind::ConstEvalCounter => {} - StatementKind::Nop => {} + _ => { eprintln!("Not yet handled: {:?}", stmt); } } } @@ -217,20 +194,7 @@ impl<'locals> CollectActions<'locals> { } } } - // The following are not yet handled, however, no info is printed - // to avoid blowups: - StatementKind::Retag(_, _) => {} - StatementKind::FakeRead(_, _) => {} - StatementKind::SetDiscriminant { .. } => {} - StatementKind::Deinit(_) => {} - StatementKind::StorageLive(_) => {} - StatementKind::StorageDead(_) => {} - StatementKind::PlaceMention(_) => {} - StatementKind::AscribeUserType { .. } => {} - StatementKind::Coverage(_) => {} - StatementKind::Intrinsic(_) => {} - StatementKind::ConstEvalCounter => {} - StatementKind::Nop => {} + _=> { eprintln!("Not yet handled, {:?}", stmt); } } } } From 3cffb186bff024f34e796d30d36521d11b47bd68 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Sun, 25 Aug 2024 21:58:15 -0400 Subject: [PATCH 42/72] Notify the user that term. does not change sb state --- .../src/kani_middle/transform/check_aliasing/instrumentation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 6c121c97bbad..37fe00e17cac 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -359,7 +359,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { pub fn instruction_actions(&self) -> Vec { let mut visitor = CollectActions::new(self.body.locals()); match self.min_processed { - SourceInstruction::Terminator { .. } => { /* not yet handled */ } + SourceInstruction::Terminator { .. } => { eprintln!("Terminators (calls, gotos) do not yet update the stacked borrows state. ") } SourceInstruction::Statement { idx, bb } => { visitor.visit_statement(&self.body.blocks()[bb].statements[idx]); } From fa201bc9e9c3ac0dc99ba0aa3f8eb052a32b5ca0 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Sun, 25 Aug 2024 23:34:04 -0400 Subject: [PATCH 43/72] Combine object & offset to reduce looping --- library/kani/src/aliasing.rs | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 3243f67634d2..2c005927fa2d 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -57,6 +57,8 @@ //! relations between the stacks (such as, for example, equality between //! the top two tags of two different stacks) are never needed. +use monitors::MONITORED; + use crate::mem::{pointer_object, pointer_offset}; use crate::shadow::ShadowMem; @@ -137,10 +139,8 @@ pub(super) mod monitors { /// "off", and it will remain so until an allocation is found /// to track. static mut STATE: bool = MonitorState::OFF; - /// The object being monitored - static mut OBJECT: usize = 0; - /// The offset being monitored - static mut OFFSET: usize = 0; + /// Object + offset being monitored + pub static mut MONITORED: *const u8 = std::ptr::null(); /// The tags of the pointer objects borrowing the byte static mut STACK_TAGS: [PointerTag; STACK_DEPTH] = [INITIAL_TAG; STACK_DEPTH]; /// The permissions of the pointer objects borrowing the byte @@ -159,9 +159,11 @@ pub(super) mod monitors { unsafe { if demonic_nondet() && STATE == MonitorState::OFF { STATE = MonitorState::ON; - OBJECT = pointer_object(pointer); - OFFSET = 0; - crate::assume(OFFSET < std::mem::size_of::()); + MONITORED = (pointer as *const u8) + .offset(kani::any_where(|offset| *offset < std::mem::size_of::().try_into().unwrap_unchecked())); + let offset = kani::any::(); + crate::assume(offset < std::mem::size_of::()); + STACK_TAGS[STACK_TOP] = tag; STACK_PERMS[STACK_TOP] = Permission::UNIQUE; STACK_TOP += 1; @@ -176,9 +178,7 @@ pub(super) mod monitors { // Offset has already been picked earlier. unsafe { use self::*; - if STATE == MonitorState::ON - && OBJECT == pointer_object(pointer) - && OFFSET == pointer_offset(pointer) + if STATE == MonitorState::ON && MONITORED == (pointer as *const u8) { STACK_TAGS[STACK_TOP] = tag; STACK_PERMS[STACK_TOP] = perm; @@ -190,9 +190,7 @@ pub(super) mod monitors { pub(super) fn stack_check(tag: u8, access: bool, address: *const U) { unsafe { use self::*; - if STATE == MonitorState::ON - && OFFSET == pointer_offset(address) - && OBJECT == pointer_object(address) + if STATE == MonitorState::ON && MONITORED == (address as *const u8) { let mut found = false; let mut j = 0; @@ -248,13 +246,11 @@ fn stack_check_ptr(pointer_value: *const *mut U) { let tag = TAGS.get(pointer_value); let perm = PERMS.get(pointer_value); let pointer = *pointer_value; - for i in 0..std::mem::size_of::() { - for access in [false, true] { - if Permission::grants(access, perm) { - self::monitors::stack_check(tag, access, pointer.byte_add(i)); - } + if pointer_object(pointer_value) == pointer_object(MONITORED) && + pointer_offset(MONITORED) < std::mem::size_of::() { + self::monitors::stack_check(tag, Access::READ, MONITORED); + self::monitors::stack_check(tag, Access::WRITE, MONITORED); } - } } } From 25958c627ec4f2f97f90f44f2f02723b36a5fa6f Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Tue, 27 Aug 2024 13:29:35 -0400 Subject: [PATCH 44/72] Fix all monitor code to use nondet offset --- library/kani/src/aliasing.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 2c005927fa2d..2f6037e409f7 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -57,6 +57,8 @@ //! relations between the stacks (such as, for example, equality between //! the top two tags of two different stacks) are never needed. +use std::ptr::read; + use monitors::MONITORED; use crate::mem::{pointer_object, pointer_offset}; @@ -155,15 +157,12 @@ pub(super) mod monitors { pub(super) unsafe fn track_local(tag: u8, pointer: *const U) { // Decide whether to initialize the stacks // for location:location+size_of(U). - // Offset has already been picked earlier. unsafe { if demonic_nondet() && STATE == MonitorState::OFF { STATE = MonitorState::ON; - MONITORED = (pointer as *const u8) - .offset(kani::any_where(|offset| *offset < std::mem::size_of::().try_into().unwrap_unchecked())); - let offset = kani::any::(); + let offset: usize = kani::any(); crate::assume(offset < std::mem::size_of::()); - + MONITORED = pointer.byte_add(offset) as *const u8; STACK_TAGS[STACK_TOP] = tag; STACK_PERMS[STACK_TOP] = Permission::UNIQUE; STACK_TOP += 1; @@ -178,7 +177,9 @@ pub(super) mod monitors { // Offset has already been picked earlier. unsafe { use self::*; - if STATE == MonitorState::ON && MONITORED == (pointer as *const u8) + if STATE == MonitorState::ON && + pointer_object(MONITORED) == pointer_object(pointer) && + pointer_offset(MONITORED) <= std::mem::size_of::() { STACK_TAGS[STACK_TOP] = tag; STACK_PERMS[STACK_TOP] = perm; @@ -187,10 +188,10 @@ pub(super) mod monitors { } } - pub(super) fn stack_check(tag: u8, access: bool, address: *const U) { + pub(super) fn stack_check(tag: u8, access: bool) { unsafe { use self::*; - if STATE == MonitorState::ON && MONITORED == (address as *const u8) + if STATE == MonitorState::ON { let mut found = false; let mut j = 0; @@ -246,10 +247,13 @@ fn stack_check_ptr(pointer_value: *const *mut U) { let tag = TAGS.get(pointer_value); let perm = PERMS.get(pointer_value); let pointer = *pointer_value; - if pointer_object(pointer_value) == pointer_object(MONITORED) && + if pointer_object(pointer) == pointer_object(MONITORED) && pointer_offset(MONITORED) < std::mem::size_of::() { - self::monitors::stack_check(tag, Access::READ, MONITORED); - self::monitors::stack_check(tag, Access::WRITE, MONITORED); + if Permission::grants(Access::READ, perm) { + self::monitors::stack_check(tag, Access::READ); + } else if Permission::grants(Access::WRITE, perm){ + self::monitors::stack_check(tag, Access::WRITE); + } } } } @@ -268,6 +272,7 @@ fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); + PERMS.set(pointer_to_created, Permission::SHAREDRW); push(NEXT_TAG, Permission::SHAREDRW, pointer_to_val); NEXT_TAG += 1; } From a86399deef323cd33552d59cd600f960b5e2c9c1 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Tue, 27 Aug 2024 13:40:04 -0400 Subject: [PATCH 45/72] Run formatter --- kani-compiler/src/kani_middle/reachability.rs | 5 +--- .../transform/check_aliasing/actions.rs | 16 +++++++++---- .../check_aliasing/instrumentation.rs | 6 ++++- library/kani/src/aliasing.rs | 24 +++++++++---------- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index e380fe076fe5..9062c1f1b82e 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -651,10 +651,7 @@ impl CallGraph { pub fn adjacencies(&self, node: MonoItem) -> Vec<&MonoItem> { match self.edges.get(&Node(node)) { None => vec![], - Some(list) => - list.iter() - .map(|collected| { &collected.0.item }) - .collect() + Some(list) => list.iter().map(|collected| &collected.0.item).collect(), } } } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs index 727407adc56b..f8514813e4f8 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs @@ -71,7 +71,9 @@ impl<'locals> CollectActions<'locals> { let rvalue = from.local; // Copy to avoid borrow self.visit_assign_reference_dereference(lvalue, rvalue); } - _ => { eprintln!("not yet handled: assignment to reference {:?}", from) } + _ => { + eprintln!("not yet handled: assignment to reference {:?}", from) + } } } @@ -142,7 +144,9 @@ impl<'locals> CollectActions<'locals> { self.visit_place(place); } // The rest are not yet handled - _ => { eprintln!("Not yet handled: {:?}", rvalue); } + _ => { + eprintln!("Not yet handled: {:?}", rvalue); + } } } @@ -153,7 +157,9 @@ impl<'locals> CollectActions<'locals> { self.visit_rvalue_places(rvalue); self.visit_place(place); } - _ => { eprintln!("Not yet handled: {:?}", stmt); } + _ => { + eprintln!("Not yet handled: {:?}", stmt); + } } } @@ -194,7 +200,9 @@ impl<'locals> CollectActions<'locals> { } } } - _=> { eprintln!("Not yet handled, {:?}", stmt); } + _ => { + eprintln!("Not yet handled, {:?}", stmt); + } } } } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 37fe00e17cac..6cac39c43ae2 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -359,7 +359,11 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { pub fn instruction_actions(&self) -> Vec { let mut visitor = CollectActions::new(self.body.locals()); match self.min_processed { - SourceInstruction::Terminator { .. } => { eprintln!("Terminators (calls, gotos) do not yet update the stacked borrows state. ") } + SourceInstruction::Terminator { .. } => { + eprintln!( + "Terminators (calls, gotos) do not yet update the stacked borrows state. " + ) + } SourceInstruction::Statement { idx, bb } => { visitor.visit_statement(&self.body.blocks()[bb].statements[idx]); } diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 2f6037e409f7..54d16939bf41 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -177,9 +177,9 @@ pub(super) mod monitors { // Offset has already been picked earlier. unsafe { use self::*; - if STATE == MonitorState::ON && - pointer_object(MONITORED) == pointer_object(pointer) && - pointer_offset(MONITORED) <= std::mem::size_of::() + if STATE == MonitorState::ON + && pointer_object(MONITORED) == pointer_object(pointer) + && pointer_offset(MONITORED) <= std::mem::size_of::() { STACK_TAGS[STACK_TOP] = tag; STACK_PERMS[STACK_TOP] = perm; @@ -191,8 +191,7 @@ pub(super) mod monitors { pub(super) fn stack_check(tag: u8, access: bool) { unsafe { use self::*; - if STATE == MonitorState::ON - { + if STATE == MonitorState::ON { let mut found = false; let mut j = 0; let mut new_top = 0; @@ -247,14 +246,15 @@ fn stack_check_ptr(pointer_value: *const *mut U) { let tag = TAGS.get(pointer_value); let perm = PERMS.get(pointer_value); let pointer = *pointer_value; - if pointer_object(pointer) == pointer_object(MONITORED) && - pointer_offset(MONITORED) < std::mem::size_of::() { - if Permission::grants(Access::READ, perm) { - self::monitors::stack_check(tag, Access::READ); - } else if Permission::grants(Access::WRITE, perm){ - self::monitors::stack_check(tag, Access::WRITE); - } + if pointer_object(pointer) == pointer_object(MONITORED) + && pointer_offset(MONITORED) < std::mem::size_of::() + { + if Permission::grants(Access::READ, perm) { + self::monitors::stack_check(tag, Access::READ); + } else if Permission::grants(Access::WRITE, perm) { + self::monitors::stack_check(tag, Access::WRITE); } + } } } From c236c2077a73df4580f9fb4068a20ce95f99c6e1 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Tue, 27 Aug 2024 13:59:42 -0400 Subject: [PATCH 46/72] Add copyright info --- tests/expected/aliasing/boxed.expected.fixme | 2 -- tests/expected/aliasing/control_flow.rs | 2 ++ tests/expected/aliasing/duplicate_write.rs | 2 ++ .../aliasing/duplicate_write_compressed.rs | 2 ++ .../aliasing/write_read_write.expected.fixme | 2 -- tests/expected/aliasing/write_read_write.rs.fixme | 14 -------------- 6 files changed, 6 insertions(+), 18 deletions(-) delete mode 100644 tests/expected/aliasing/boxed.expected.fixme delete mode 100644 tests/expected/aliasing/write_read_write.expected.fixme delete mode 100644 tests/expected/aliasing/write_read_write.rs.fixme diff --git a/tests/expected/aliasing/boxed.expected.fixme b/tests/expected/aliasing/boxed.expected.fixme deleted file mode 100644 index 40b69fbc424a..000000000000 --- a/tests/expected/aliasing/boxed.expected.fixme +++ /dev/null @@ -1,2 +0,0 @@ -FAILURE\ -assertion failed: Stack violated diff --git a/tests/expected/aliasing/control_flow.rs b/tests/expected/aliasing/control_flow.rs index 4a2fe04ff555..3b18b0f27cda 100644 --- a/tests/expected/aliasing/control_flow.rs +++ b/tests/expected/aliasing/control_flow.rs @@ -1,3 +1,5 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zghost-state -Zaliasing #[allow(unused)] diff --git a/tests/expected/aliasing/duplicate_write.rs b/tests/expected/aliasing/duplicate_write.rs index 62ecec832f21..bf3a6d11b307 100644 --- a/tests/expected/aliasing/duplicate_write.rs +++ b/tests/expected/aliasing/duplicate_write.rs @@ -1,3 +1,5 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zghost-state -Zaliasing #[kani::proof] diff --git a/tests/expected/aliasing/duplicate_write_compressed.rs b/tests/expected/aliasing/duplicate_write_compressed.rs index 1703b4799ee5..466d9f219ade 100644 --- a/tests/expected/aliasing/duplicate_write_compressed.rs +++ b/tests/expected/aliasing/duplicate_write_compressed.rs @@ -1,3 +1,5 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zghost-state -Zaliasing #[kani::proof] diff --git a/tests/expected/aliasing/write_read_write.expected.fixme b/tests/expected/aliasing/write_read_write.expected.fixme deleted file mode 100644 index 40b69fbc424a..000000000000 --- a/tests/expected/aliasing/write_read_write.expected.fixme +++ /dev/null @@ -1,2 +0,0 @@ -FAILURE\ -assertion failed: Stack violated diff --git a/tests/expected/aliasing/write_read_write.rs.fixme b/tests/expected/aliasing/write_read_write.rs.fixme deleted file mode 100644 index dea1ab64c755..000000000000 --- a/tests/expected/aliasing/write_read_write.rs.fixme +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT -// kani-flags: -Zaliasing - -#[kani::proof] -fn main() { - let mut x = 10; - let ref_x = &mut x; - let raw_1 = ref_x as *mut i32; - let raw_2 = ref_x as *const i32; - let _write = unsafe { *raw_1 = 100 }; - let _read = unsafe { *raw_2 }; - let _write = unsafe { *raw_1 = 110 }; -} From cda8657ee6820b242faf64ad0b4f2ae9d8886daf Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Tue, 27 Aug 2024 14:16:04 -0400 Subject: [PATCH 47/72] Update for clippy --- .../kani_middle/transform/check_aliasing/actions.rs | 2 +- .../transform/check_aliasing/function_cache.rs | 6 +++--- .../transform/check_aliasing/instrumentation.rs | 12 ++++++------ .../src/kani_middle/transform/check_aliasing/mod.rs | 7 +++---- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs index f8514813e4f8..7b87259d7720 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs @@ -61,7 +61,7 @@ impl<'locals> CollectActions<'locals> { // Direct reference to stack local // x = &y; let lvalue = to; - let rvalue = from.local.clone(); + let rvalue = from.local; self.actions.push(Action::NewStackReference { lvalue, rvalue }); } [ProjectionElem::Deref] => { diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs index 507dc0216e15..621572a1e28b 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs @@ -58,9 +58,9 @@ impl Cache { /// in the given compilation context, ctx pub fn register(&mut self, ctx: &TyCtxt, sig: Signature) -> Result<&MirInstance, MirError> { let Cache { cache } = self; - for i in 0..cache.len() { - if sig == cache[i].signature { - return Ok(&cache[i].instance); + for item in &cache { + if sig == item.signature { + return Ok(item.instance); } } let fndef = find_fn_def(*ctx, &sig.diagnostic) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 6cac39c43ae2..297412e985ec 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -119,7 +119,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let tcx = &self.tcx; let fn_pointers = &mut self.fn_pointers; let body = &mut self.body; - let span = self.span.clone(); + let span = *self.span; let instance = cache.register(tcx, callee)?; let func_local = fn_pointers .entry(*instance) @@ -210,13 +210,13 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let kind = TerminatorKind::Goto { target: 0 }; let terminator = Terminator { kind, span }; let source = &mut self.min_processed.clone(); - let enter_ghost_block = source.clone(); + let enter_ghost_block = *source; let body = &mut self.body; body.replace_terminator(source, terminator.clone()); // replace terminator so you can instrument "after" it body.insert_terminator(source, InsertPosition::After, terminator.clone()); - let execute_terminator_block = source.clone(); + let execute_terminator_block = *source; body.insert_terminator(source, InsertPosition::After, terminator.clone()); - let execute_ghost_block = source.clone(); + let execute_ghost_block = *source; // Instrument enter ghost: let span = original_span; @@ -398,7 +398,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { pub fn instrument_instructions(&mut self) -> Result<()> { loop { let actions = self.instruction_actions(); - if actions.len() > 0 { + if !actions.is_empty() { eprintln!("Instrumenting actions:"); self.process_instruction(); } @@ -417,7 +417,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { SourceInstruction::Statement { idx: idx - 1, bb } } SourceInstruction::Terminator { bb } - if self.body.blocks()[bb].statements.len() > 0 => + if !self.body.blocks()[bb].statements.is_empty() => { SourceInstruction::Statement { idx: self.body.blocks()[bb].statements.len() - 1, diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index d05f55dcc055..84fbba7740f1 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -125,11 +125,10 @@ impl GlobalPass for GlobalAliasingPass { .def .all_attrs() .into_iter() - .fold(false, |is_proof, attr| is_proof || attr.as_str().contains("kanitool::proof")) + .all(|attr| attr.as_str().contains("kanitool::proof")) && + found.insert(instance) { - if found.insert(instance) { - queue.push_back(instance) - } + queue.push_back(instance) } } while let Some(instance) = queue.pop_front() { From db2c5702086caf00f710653d417553bb9b30647c Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Tue, 27 Aug 2024 15:43:45 -0400 Subject: [PATCH 48/72] Fix lint on range --- .../check_aliasing/function_cache.rs | 32 ++++++++++++------- .../check_aliasing/instrumentation.rs | 2 +- .../transform/check_aliasing/mod.rs | 4 +-- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs index 621572a1e28b..5766b564a120 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs @@ -53,21 +53,31 @@ pub struct Cache { cache: Vec, } +fn try_get_or_insert(vec: &mut Vec, p: P, f: F) -> Result<&mut T, E> +where F: FnOnce() -> Result, P: Fn(&T) -> bool, T: PartialEq { + if let Some(i) = vec.iter().position(p) { + Ok(&mut vec[i]) + } else { + vec.push(f()?); + Ok(vec.last_mut().unwrap()) + } +} + impl Cache { + /// Register the signature the to the cache /// in the given compilation context, ctx - pub fn register(&mut self, ctx: &TyCtxt, sig: Signature) -> Result<&MirInstance, MirError> { + pub fn register(&mut self, ctx: &TyCtxt, signature: Signature) -> Result<&MirInstance, MirError> { + let test_sig = signature.clone(); let Cache { cache } = self; - for item in &cache { - if sig == item.signature { - return Ok(item.instance); - } - } - let fndef = find_fn_def(*ctx, &sig.diagnostic) - .ok_or(MirError::new(format!("Not found: {}", &sig.diagnostic)))?; - let instance = MirInstance::resolve(fndef, &GenericArgs(sig.args.clone()))?; - cache.push(Instance::new(sig, instance)); - Ok(&cache[cache.len() - 1].instance) + try_get_or_insert(cache, |item| item.signature == test_sig, || + { + let fndef = find_fn_def(*ctx, &signature.diagnostic) + .ok_or(MirError::new(format!("Not found: {}", &signature.diagnostic)))?; + let instance = MirInstance::resolve(fndef, &GenericArgs(signature.args.clone()))?; + Ok(Instance::new(signature, instance)) + }).map(|entry| &entry.instance) + } /// Register the kani assertion function diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 297412e985ec..db63e63d5ca4 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -119,7 +119,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let tcx = &self.tcx; let fn_pointers = &mut self.fn_pointers; let body = &mut self.body; - let span = *self.span; + let span = self.span; let instance = cache.register(tcx, callee)?; let func_local = fn_pointers .entry(*instance) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 84fbba7740f1..84184cc7a747 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -125,8 +125,8 @@ impl GlobalPass for GlobalAliasingPass { .def .all_attrs() .into_iter() - .all(|attr| attr.as_str().contains("kanitool::proof")) && - found.insert(instance) + .all(|attr| attr.as_str().contains("kanitool::proof")) + && found.insert(instance) { queue.push_back(instance) } From 9953526fb1fb649fa75d58dde40a966730bffaff Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Tue, 27 Aug 2024 15:46:20 -0400 Subject: [PATCH 49/72] Run kani fmt --- .../check_aliasing/function_cache.rs | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs index 5766b564a120..493779137035 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs @@ -54,7 +54,11 @@ pub struct Cache { } fn try_get_or_insert(vec: &mut Vec, p: P, f: F) -> Result<&mut T, E> -where F: FnOnce() -> Result, P: Fn(&T) -> bool, T: PartialEq { +where + F: FnOnce() -> Result, + P: Fn(&T) -> bool, + T: PartialEq, +{ if let Some(i) = vec.iter().position(p) { Ok(&mut vec[i]) } else { @@ -64,20 +68,26 @@ where F: FnOnce() -> Result, P: Fn(&T) -> bool, T: PartialEq { } impl Cache { - /// Register the signature the to the cache /// in the given compilation context, ctx - pub fn register(&mut self, ctx: &TyCtxt, signature: Signature) -> Result<&MirInstance, MirError> { + pub fn register( + &mut self, + ctx: &TyCtxt, + signature: Signature, + ) -> Result<&MirInstance, MirError> { let test_sig = signature.clone(); let Cache { cache } = self; - try_get_or_insert(cache, |item| item.signature == test_sig, || - { - let fndef = find_fn_def(*ctx, &signature.diagnostic) - .ok_or(MirError::new(format!("Not found: {}", &signature.diagnostic)))?; - let instance = MirInstance::resolve(fndef, &GenericArgs(signature.args.clone()))?; - Ok(Instance::new(signature, instance)) - }).map(|entry| &entry.instance) - + try_get_or_insert( + cache, + |item| item.signature == test_sig, + || { + let fndef = find_fn_def(*ctx, &signature.diagnostic) + .ok_or(MirError::new(format!("Not found: {}", &signature.diagnostic)))?; + let instance = MirInstance::resolve(fndef, &GenericArgs(signature.args.clone()))?; + Ok(Instance::new(signature, instance)) + }, + ) + .map(|entry| &entry.instance) } /// Register the kani assertion function From f0183d29d710decd76a4749de3d477624fa9d8fb Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Tue, 27 Aug 2024 15:50:31 -0400 Subject: [PATCH 50/72] More clippy lints --- .../kani_middle/transform/check_aliasing/instrumentation.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index db63e63d5ca4..2684e1159d02 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -173,7 +173,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { pub fn instrument_local(&mut self, local: Local) -> Result<()> { let ty = self.body.locals()[local].ty; let ptr_ty = Ty::new_ptr(ty, Mutability::Not); - let span = self.span.clone(); + let span = self.span; let body = &mut self.body; let local_ptr = self .meta_stack @@ -205,7 +205,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { match self.min_processed { SourceInstruction::Terminator { bb } => { let original = self.body.blocks()[bb].terminator.clone(); - let original_span = self.body.blocks()[bb].terminator.span.clone(); + let original_span = self.body.blocks()[bb].terminator.span; let span = self.span; let kind = TerminatorKind::Goto { target: 0 }; let terminator = Terminator { kind, span }; From cab1067347f34dd2f1bc6f2fdd30d65b36b97534 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Tue, 27 Aug 2024 16:20:48 -0400 Subject: [PATCH 51/72] Update line numbers --- tests/expected/aliasing/control_flow.expected | 4 ++-- tests/expected/aliasing/duplicate_write.expected | 2 +- tests/expected/aliasing/duplicate_write_compressed.expected | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/expected/aliasing/control_flow.expected b/tests/expected/aliasing/control_flow.expected index d48405d178d1..42969176f422 100644 --- a/tests/expected/aliasing/control_flow.expected +++ b/tests/expected/aliasing/control_flow.expected @@ -1,4 +1,4 @@ Failed Checks: Stacked borrows aliasing model violated. - File: "./tests/expected/aliasing/control_flow.rs", line 21, in main + File: "./tests/expected/aliasing/control_flow.rs", line 23, in main Failed Checks: Stacked borrows aliasing model violated. - File: "./tests/expected/aliasing/control_flow.rs", line 22, in main + File: "./tests/expected/aliasing/control_flow.rs", line 24, in main diff --git a/tests/expected/aliasing/duplicate_write.expected b/tests/expected/aliasing/duplicate_write.expected index d117ac5c1d45..2a5160581626 100644 --- a/tests/expected/aliasing/duplicate_write.expected +++ b/tests/expected/aliasing/duplicate_write.expected @@ -1,2 +1,2 @@ Failed Checks: Stacked borrows aliasing model violated. - File: "./tests/expected/aliasing/duplicate_write.rs", line 19, in main + File: "./tests/expected/aliasing/duplicate_write.rs", line 21, in main diff --git a/tests/expected/aliasing/duplicate_write_compressed.expected b/tests/expected/aliasing/duplicate_write_compressed.expected index 7181cb4c2385..584132639cae 100644 --- a/tests/expected/aliasing/duplicate_write_compressed.expected +++ b/tests/expected/aliasing/duplicate_write_compressed.expected @@ -1,2 +1,2 @@ Failed Checks: Stacked borrows aliasing model violated. - File: "./tests/expected/aliasing/duplicate_write_compressed.rs", line 12, in main + File: "./tests/expected/aliasing/duplicate_write_compressed.rs", line 14, in main From 0e99f41abe5a2c43cdb429498bdc3a89b14d5bca Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Wed, 28 Aug 2024 02:41:22 -0400 Subject: [PATCH 52/72] Begin implementing the backwards pass artem suggested --- .../src/kani_middle/transform/body.rs | 84 ++++- .../transform/check_aliasing/actions.rs | 208 ----------- .../check_aliasing/instrumentation.rs | 349 +++++++----------- .../transform/check_aliasing/mod.rs | 17 +- 4 files changed, 202 insertions(+), 456 deletions(-) delete mode 100644 kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index fa4e5eb1ad97..8608421dce87 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -165,6 +165,46 @@ impl MutableBody { self.insert_stmt(stmt, source, position); } + /// Add a new assert to the basic block indicated by the given index, using + /// "local" as the checking function. + /// + /// The new assertion 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_check_with_local( + &mut self, + tcx: TyCtxt, + local: Local, + source: &mut SourceInstruction, + position: InsertPosition, + value: Local, + msg: &str, + ) { + assert_eq!( + self.locals[value].ty, + Ty::bool_ty(), + "Expected boolean value as the assert input" + ); + assert!(self.locals[local].ty.kind().is_fn(), "Expected a function type as assert"); + let new_bb = self.blocks.len(); + let span = source.span(&self.blocks); + let assert_op = Operand::Copy(Place::from(local)); + 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.insert_terminator(source, position, terminator); + } + /// 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 @@ -189,24 +229,12 @@ impl MutableBody { let span = source.span(&self.blocks); match check_type { CheckType::Assert(assert_fn) => { - let assert_op = Operand::Copy(Place::from(self.new_local( + let local = 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.insert_terminator(source, position, terminator); + ); + self.insert_check_with_local(tcx, local, source, position, value, msg); } CheckType::Panic | CheckType::NoCore => { tcx.sess @@ -220,15 +248,32 @@ impl MutableBody { } } + /// Add a new call to the basic block indicated by the given index. + /// This has the same semantics as insert_call_to_local, the only + /// difference being that a new local is created for the given function + /// instance. + pub fn insert_call( + &mut self, + callee: &Instance, + source: &mut SourceInstruction, + position: InsertPosition, + args: Vec, + destination: Place, + ) { + let span = source.span(&self.blocks); + let local = self.new_local(callee.ty(), span, Mutability::Not); + self.insert_call_to_local(local, source, position, args, destination); + } + /// 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. 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( + pub fn insert_call_to_local( &mut self, - callee: &Instance, + callee: Local, source: &mut SourceInstruction, position: InsertPosition, args: Vec, @@ -236,8 +281,7 @@ impl MutableBody { ) { 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 callee_op = Operand::Copy(Place::from(callee)); let kind = TerminatorKind::Call { func: callee_op, args, @@ -484,7 +528,7 @@ impl CheckType { } /// We store the index of an instruction to avoid borrow checker issues and unnecessary copies. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum SourceInstruction { Statement { idx: usize, bb: BasicBlockIdx }, Terminator { bb: BasicBlockIdx }, diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs deleted file mode 100644 index 7b87259d7720..000000000000 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright Kani Contributors -// SPDX-License-Identifier: Apache-2.0 OR MIT -//! This module contains stacked borrows "actions," -//! or updates to the stacked borrows state, as well as -//! methods that collect the actions that need to be applied from the -//! statements of the code. - -use stable_mir::mir::{ - BorrowKind, Local, LocalDecl, Mutability, Operand, Place, ProjectionElem, Rvalue, Statement, - StatementKind, -}; -use stable_mir::ty::{RigidTy, Ty, TyKind}; - -/// Update action to the stacked borrows state -#[derive(Debug)] -pub enum Action { - StackCheck, - NewStackReference { lvalue: Local, rvalue: usize }, - StackUpdateReference { place: usize, ty: Ty }, - NewMutRefFromRaw { lvalue: usize, rvalue: usize, ty: Ty }, - StackUpdatePointer { place: usize, ty: Ty }, - NewMutRawFromRef { lvalue: usize, rvalue: usize, ty: Ty }, -} - -/// The actions of a statement -pub struct CollectActions<'locals> { - actions: Vec, - /// The locals, required to ensure that the references - /// and pointers are picked appropriately. - locals: &'locals [LocalDecl], -} - -impl<'locals> CollectActions<'locals> { - /// Initialize the struct using the given locals - pub fn new(locals: &'locals [LocalDecl]) -> Self { - CollectActions { actions: Vec::new(), locals } - } - - /// Finalize the code actions to be taken - pub fn finalize(self) -> Vec { - self.actions - } - - /// Collect the actions for assigning the lvalue - /// to the dereferenced rvalue - fn visit_assign_reference_dereference(&mut self, lvalue: Local, rvalue: Local) { - match self.locals[rvalue].ty.kind() { - TyKind::RigidTy(RigidTy::Ref(_, ty, _)) | TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { - // reborrow - self.actions.push(Action::NewMutRefFromRaw { lvalue, rvalue, ty }); - } - _ => {} - } - } - - /// Collect the actions for assigning the reference in - /// "place" to the local at "to". - fn visit_assign_reference(&mut self, to: Local, from: Place) { - match from.projection[..] { - [] => { - // Direct reference to stack local - // x = &y; - let lvalue = to; - let rvalue = from.local; - self.actions.push(Action::NewStackReference { lvalue, rvalue }); - } - [ProjectionElem::Deref] => { - // Reborrow - // x : &mut T = &*(y : *mut T OR &mut T) - let lvalue = to; // Copy to avoid borrow - let rvalue = from.local; // Copy to avoid borrow - self.visit_assign_reference_dereference(lvalue, rvalue); - } - _ => { - eprintln!("not yet handled: assignment to reference {:?}", from) - } - } - } - - /// Collect the actions for assigning the data at - /// from to the local at to. - fn visit_assign_pointer(&mut self, to: Local, from: Place) { - match from.projection[..] { - [] => { - // x = &raw y - panic!("Addr of not yet handled"); - } - [ProjectionElem::Deref] => { - // x = &raw mut *(y: &mut T OR *mut T) - let rvalue = from.local; // Copy to avoid borrow - let lvalue = to; - match self.locals[rvalue].ty.kind() { - TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { - self.actions.push(Action::NewMutRawFromRef { lvalue, rvalue, ty }); - } - _ => { - panic!( - "Dereference of rvalue case not yet handled for raw pointers {:?}", - from - ); - } - } - } - _ => {} - } - } - - /// Collect the actions for a Place, - /// incurring a stack check in the case of a - /// dereference of a pointer or reference - fn visit_place(&mut self, place: &Place) { - match &place.projection[..] { - [] => { - // Direct usage -- no update needed - return; - } - [ProjectionElem::Deref] => { - // Dereference -- instrument stack check - } - _ => { - // Field access -- not yet handled. - return; - } - }; - if self.locals[place.local].ty.kind().is_ref() { - let ty = place.ty(self.locals).unwrap(); - self.actions.push(Action::StackUpdateReference { place: place.local, ty }); - self.actions.push(Action::StackCheck); - } - if self.locals[place.local].ty.kind().is_raw_ptr() { - let ty = place.ty(self.locals).unwrap(); - self.actions.push(Action::StackUpdatePointer { place: place.local, ty }); - self.actions.push(Action::StackCheck); - } - } - - /// Collect the actions for the places of an Rvalue - fn visit_rvalue_places(&mut self, rvalue: &Rvalue) { - match rvalue { - Rvalue::AddressOf(_, place) => { - self.visit_place(place); - } - Rvalue::Ref(_, _, place) => { - self.visit_place(place); - } - // The rest are not yet handled - _ => { - eprintln!("Not yet handled: {:?}", rvalue); - } - } - } - - /// Collect the actions for the places of a statement - fn visit_statement_places(&mut self, stmt: &Statement) { - match &stmt.kind { - StatementKind::Assign(place, rvalue) => { - self.visit_rvalue_places(rvalue); - self.visit_place(place); - } - _ => { - eprintln!("Not yet handled: {:?}", stmt); - } - } - } - - /// Collect the actions for the places of the statement, - /// then find assignments of pointer values to lvalues - /// and collect updates to the stacked borrows state - /// accordingly - pub fn visit_statement(&mut self, stmt: &Statement) { - self.visit_statement_places(stmt); - match &stmt.kind { - StatementKind::Assign(to, rvalue) => { - match rvalue { - Rvalue::Ref(_, BorrowKind::Mut { .. }, from) => { - self.visit_assign_reference(to.local, from.clone()); - } - Rvalue::AddressOf(Mutability::Mut, from) => { - self.visit_assign_pointer(to.local, from.clone()); - } - Rvalue::Use(Operand::Constant(_)) => { - // Do nothing for the constants case - } - Rvalue::Use(Operand::Copy(_)) => { - eprintln!("Copy not yet handled"); - // Do nothing for the constants case - } - Rvalue::Use(Operand::Move(_)) => { - eprintln!("Move not yet handled"); - // Do nothing for the constants case - } - Rvalue::BinaryOp(_, _, _) => { - eprintln!("Binary op not yet handled"); - } - Rvalue::CheckedBinaryOp(_, _, _) => { - eprintln!("Checked binary op not yet handled"); - } - _ => { - panic!("Rvalue kind: {:?} not yet handled", rvalue); - } - } - } - _ => { - eprintln!("Not yet handled, {:?}", stmt); - } - } - } -} diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 2684e1159d02..2d8f331f1eed 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -7,10 +7,11 @@ //! are instrumented, and that no code that is added by the instrumentation //! pass itself is instrumented or analyzed. -use crate::kani_middle::transform::body::{CheckType, InsertPosition}; +use crate::kani_middle::transform::body::InsertPosition; use rustc_middle::ty::TyCtxt; use stable_mir::mir::{ - Body, Local, Mutability, Operand, Place, Rvalue, Terminator, TerminatorKind, UnwindAction, + BasicBlock, Body, Local, MirVisitor, Mutability, Operand, Place, Rvalue, Statement, + StatementKind, Terminator, TerminatorKind, UnwindAction, }; use stable_mir::ty::{GenericArgKind, Span, Ty}; use std::collections::HashMap; @@ -43,45 +44,22 @@ pub struct InstrumentationData<'tcx, 'cache> { fn_pointers: HashMap, /// The span of the body span: Span, - /// The minimum processed instruction. - /// All instructions before this one belong to the original - /// source code, and have not yet been analyzed. - min_processed: SourceInstruction, - /// The index after which "ghost" instrumentation code - /// may be added. - ghost_index: SourceInstruction, /// The body being instrumented body: MutableBody, + /// The code actions for the instruction + actions: Option)>>, } impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { - /// Move bb0 to the end to start instrumentation there. - fn prepare_body(body: Body) -> MutableBody { - let mut body = MutableBody::from(body); - let span; - let mut source; - match body.blocks()[0].statements.len() { - 0 => { - source = SourceInstruction::Terminator { bb: 0 }; - span = body.blocks()[0].terminator.span; - } - _ => { - source = SourceInstruction::Statement { idx: 0, bb: 0 }; - span = body.blocks()[0].terminator.span; - } - } - let kind = TerminatorKind::Goto { target: body.blocks().len() }; - let terminator = Terminator { kind, span }; - body.insert_terminator(&mut source, InsertPosition::Before, terminator); - body - } - /// Using a (potentially) pre-populated cache of resolved generic /// functions, and the StableMir body "body", initialize the instrumentation /// pass data. pub fn new(tcx: TyCtxt<'tcx>, cache: &'cache mut Cache, body: Body) -> Self { + let mut visitor = CollectActions::new(body.locals()); + visitor.visit_body(&body); + let actions = Some(visitor.finalize()); let span = body.span; - let mut body = Self::prepare_body(body); + let mut body = MutableBody::from(body); let meta_stack = HashMap::new(); let local_count = body.locals().len(); let fn_pointers = HashMap::new(); @@ -91,12 +69,6 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { span, Mutability::Mut, ); - let bb = body.blocks().len() - 1; - let min_processed = match body.blocks()[bb].statements.len() { - 0 => SourceInstruction::Terminator { bb }, - n => SourceInstruction::Statement { idx: n - 1, bb }, - }; - let ghost_index = SourceInstruction::Terminator { bb: 0 }; InstrumentationData { tcx, cache, @@ -106,24 +78,20 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { valid, fn_pointers, span, - min_processed, - ghost_index, body, + actions, } } /// Register the function described by the diagnostic /// and generic arguments in "Signature". fn register_fn(&mut self, callee: Signature) -> Result { - let cache = &mut self.cache; - let tcx = &self.tcx; - let fn_pointers = &mut self.fn_pointers; - let body = &mut self.body; let span = self.span; - let instance = cache.register(tcx, callee)?; - let func_local = fn_pointers + let instance = self.cache.register(&self.tcx, callee)?; + let func_local = self + .fn_pointers .entry(*instance) - .or_insert_with(|| body.new_local(instance.ty(), span, Mutability::Not)); + .or_insert_with(|| self.body.new_local(instance.ty(), span, Mutability::Not)); Ok(*func_local) } @@ -131,39 +99,55 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// in args and returning into "dest". /// This differs from Mutable Body's call in that the /// function name is cached. - pub fn call(&mut self, callee: Signature, args: Vec, dest: Local) -> Result<()> { - let func_local = self.register_fn(callee)?; - let new_bb = self.body.blocks().len(); - let span = self.body.blocks()[self.min_processed.bb()].terminator.span; - let callee_op = Operand::Copy(Place::from(func_local)); - let args = args - .into_iter() - .map(|v| Operand::Copy(Place { local: v, projection: vec![] })) - .collect(); - let destination = Place::from(dest); - let kind = TerminatorKind::Call { - func: callee_op, - args, - destination, - target: Some(new_bb), - unwind: UnwindAction::Terminate, - }; - let terminator = Terminator { kind, span }; - let source = &mut self.ghost_index; - self.body.insert_terminator(source, InsertPosition::After, terminator); + pub fn call( + &mut self, + callee: Signature, + args: Vec, + dest: Local, + source: &SourceInstruction, + ) -> Result { + let fn_local = self.register_fn(callee)?; + let func = Operand::Copy(Place::from(fn_local)); + let args = args.into_iter().map(|local| Operand::Copy(Place::from(local))).collect(); + let destination = Place::from(self.unit); + let target = Some(0); // doesn't matter, updated later + let unwind = UnwindAction::Terminate; + let kind = TerminatorKind::Call { func, args, destination, target, unwind }; + let span = source.span(self.body.blocks()); + Ok(Terminator { kind, span }) + } + + /// Instrument the call generated by "call" for these parameters. + pub fn instrument_call( + &mut self, + callee: Signature, + args: Vec, + dest: Local, + source: &mut SourceInstruction) -> Result<()> { + let terminator = self.call(callee, args, dest, source)?; + let bb = BasicBlock { statements: vec![], terminator }; + self.body.insert_bb(bb, source, InsertPosition::Before); Ok(()) } /// Instrument an assignment to a local - pub fn assign_pointer(&mut self, lvalue: Local, rvalue: Local) { - let source = &mut self.ghost_index; - let position = InsertPosition::After; - self.body.assign_to( - Place::from(lvalue), - Rvalue::AddressOf(Mutability::Not, Place::from(rvalue)), - source, - position, - ); + pub fn assign_pointer( + &mut self, + lvalue: Local, + rvalue: Local, + source: &SourceInstruction, + ) -> Statement { + let span = source.span(&self.body.blocks()); + let rvalue = Rvalue::AddressOf(Mutability::Not, Place::from(rvalue)); + Statement { kind: StatementKind::Assign(Place::from(lvalue), rvalue), span } + } + + pub fn first_instruction(&self) -> SourceInstruction { + if self.body.blocks()[0].statements.is_empty() { + SourceInstruction::Terminator { bb: 0 } + } else { + SourceInstruction::Statement { idx: 0, bb: 0 } + } } /// For some local, say let x: T; @@ -172,87 +156,30 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// initialize_local(ptr_x); pub fn instrument_local(&mut self, local: Local) -> Result<()> { let ty = self.body.locals()[local].ty; - let ptr_ty = Ty::new_ptr(ty, Mutability::Not); - let span = self.span; - let body = &mut self.body; - let local_ptr = self - .meta_stack - .entry(local) - .or_insert_with(|| body.new_local(ptr_ty, span, Mutability::Not)); - let local_ptr = *local_ptr; - self.assign_pointer(local_ptr, local); - self.call( + let local_ptr = *self.meta_stack.get(&local).unwrap(); + let statement = self.assign_pointer(local_ptr, local, &self.first_instruction()); + let statements = vec![statement]; + let terminator = self.call( Signature::new("KaniInitializeLocal", &[GenericArgKind::Type(ty)]), vec![local_ptr], self.unit, + &self.first_instruction(), )?; + let bb = BasicBlock { statements, terminator }; + self.body.insert_bb(bb, &mut self.first_instruction(), InsertPosition::Before); Ok(()) } - /// Split at the minimum processed instruction, - /// allowing instrumentation of ghost code following - /// that source instruction. - pub fn process_instruction(&mut self) { - // If the instruction under processing is a terminator, - // special care is needed; it is impossible to instrument - // "after" a terminator with no target. - // Therefore we handle the terminators manually, - // inserting the terminator into the ghost code, - // then inserting a jump to that terminator. - // These will be called the "enter ghost," - // "execute ghost", and "execute terminator" - // terminators - match self.min_processed { - SourceInstruction::Terminator { bb } => { - let original = self.body.blocks()[bb].terminator.clone(); - let original_span = self.body.blocks()[bb].terminator.span; - let span = self.span; - let kind = TerminatorKind::Goto { target: 0 }; - let terminator = Terminator { kind, span }; - let source = &mut self.min_processed.clone(); - let enter_ghost_block = *source; - let body = &mut self.body; - body.replace_terminator(source, terminator.clone()); // replace terminator so you can instrument "after" it - body.insert_terminator(source, InsertPosition::After, terminator.clone()); - let execute_terminator_block = *source; - body.insert_terminator(source, InsertPosition::After, terminator.clone()); - let execute_ghost_block = *source; - - // Instrument enter ghost: - let span = original_span; - let target = execute_ghost_block.bb(); - let kind = TerminatorKind::Goto { target }; - let terminator = Terminator { kind, span }; - body.replace_terminator(&enter_ghost_block, terminator); - // Instrument execute ghost: - let target = execute_terminator_block.bb(); - let kind = TerminatorKind::Goto { target }; - let terminator = Terminator { kind, span }; - body.replace_terminator(&execute_ghost_block, terminator); - // Instrument execute terminator - body.replace_terminator(&execute_terminator_block, original); - - self.ghost_index = execute_ghost_block; - } - SourceInstruction::Statement { idx, bb } => { - // In this case it is simple, merely goto the ghost code - // immdediately. - let span = self.body.blocks()[bb].statements[idx].span; - let target = self.body.blocks().len(); - let kind = TerminatorKind::Goto { target }; - let terminator = Terminator { kind, span }; - let min_processed = &mut self.min_processed.clone(); - self.body.insert_terminator(min_processed, InsertPosition::After, terminator); - self.ghost_index = *min_processed; - } - } - } - /// Instrument a stack reference of the fo /// lvalue = &rvalue /// with an update to the stacked borrows state, /// at the code index source. - pub fn instrument_new_stack_reference(&mut self, lvalue: Local, rvalue: Local) -> Result<()> { + pub fn instrument_new_stack_reference( + &mut self, + source: &mut SourceInstruction, + lvalue: Local, + rvalue: Local, + ) -> Result<()> { // Initialize the constants let ty = self.body.locals()[rvalue].ty; let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); @@ -261,21 +188,21 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Signature::new("KaniNewMutRefFromValue", &[GenericArgKind::Type(ty)]), vec![*lvalue_ref, *rvalue_ref], self.unit, + source, )?; Ok(()) } /// Instrument with stack violated / not violated - pub fn instrument_stack_check(&mut self) -> Result<()> { - self.call(Signature::new("KaniStackValid", &[]), vec![], self.valid)?; + pub fn instrument_stack_check(&mut self, source: &mut SourceInstruction) -> Result<()> { + self.instrument_call(Signature::new("KaniStackValid", &[]), vec![], self.valid, source)?; + let assert = self.register_fn(Signature::new("KaniAssert", &[]))?; let msg = "Stacked borrows aliasing model violated."; - let check_fn = self.cache.register_assert(&self.tcx)?; - let check_type = &CheckType::Assert(*check_fn); - self.body.insert_check( + self.body.insert_check_with_local( self.tcx, - check_type, - &mut self.ghost_index, - InsertPosition::After, + assert, + source, + InsertPosition::Before, self.valid, &msg, ); @@ -284,26 +211,38 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// Instrument a validity assertion on the stacked borrows state /// at idx for (place: &mut T). - pub fn instrument_stack_update_ref(&mut self, place: Local, ty: Ty) -> Result<()> { + pub fn instrument_stack_update_ref( + &mut self, + source: &mut SourceInstruction, + place: Local, + ty: Ty, + ) -> Result<()> { // Initialize the constants let place_ref = self.meta_stack.get(&place).unwrap(); - self.call( + self.instrument_call( Signature::new("KaniStackCheckRef", &[GenericArgKind::Type(ty)]), vec![*place_ref], self.unit, + source, )?; Ok(()) } /// Instrument a validity assertion on the stacked borrows state /// at idx for (place: *const T). - pub fn instrument_stack_update_ptr(&mut self, place: Local, ty: Ty) -> Result<()> { + pub fn instrument_stack_update_ptr( + &mut self, + source: &mut SourceInstruction, + place: Local, + ty: Ty, + ) -> Result<()> { // Initialize the constants let place_ref = self.meta_stack.get(&place).unwrap(); - self.call( + self.instrument_call( Signature::new("KaniStackCheckPtr", &[GenericArgKind::Type(ty)]), vec![*place_ref], self.unit, + source, )?; Ok(()) } @@ -312,6 +251,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// created = &mut *(raw: const *T). pub fn instrument_new_mut_ref_from_raw( &mut self, + source: &mut SourceInstruction, created: Local, raw: Local, ty: Ty, @@ -319,10 +259,11 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { // Initialize the constants let created_ref = self.meta_stack.get(&created).unwrap(); let reference_ref = self.meta_stack.get(&raw).unwrap(); - self.call( + self.instrument_call( Signature::new("KaniNewMutRefFromRaw", &[GenericArgKind::Type(ty)]), vec![*created_ref, *reference_ref], self.unit, + source, )?; Ok(()) } @@ -331,6 +272,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// created = (ref: &mut T) as *mut T pub fn instrument_new_mut_raw_from_ref( &mut self, + source: &mut SourceInstruction, created: Local, reference: Local, ty: Ty, @@ -338,10 +280,11 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { // Initialize the constants let created_ref = self.meta_stack.get(&created).unwrap(); let reference_ref = self.meta_stack.get(&reference).unwrap(); - self.call( + self.instrument_call( Signature::new("KaniNewMutRawFromRef", &[GenericArgKind::Type(ty)]), vec![*created_ref, *reference_ref], self.unit, + source, )?; Ok(()) } @@ -349,45 +292,32 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// Instrument each of the locals collected into values with /// initialization data. pub fn instrument_locals(&mut self) -> Result<()> { - for local in (self.body.arg_count() + 1)..self.local_count { + for local in ((self.body.arg_count() + 1)..self.local_count).rev() { self.instrument_local(local)? } Ok(()) } - /// Fetch the actions to be instrumented at the current instruction. - pub fn instruction_actions(&self) -> Vec { - let mut visitor = CollectActions::new(self.body.locals()); - match self.min_processed { - SourceInstruction::Terminator { .. } => { - eprintln!( - "Terminators (calls, gotos) do not yet update the stacked borrows state. " - ) - } - SourceInstruction::Statement { idx, bb } => { - visitor.visit_statement(&self.body.blocks()[bb].statements[idx]); - } - } - visitor.finalize() - } - /// Instrument the action given in "action" with the appropriate /// update to the stacked borrows state. - fn instrument_action(&mut self, action: Action) -> Result<()> { + fn instrument_action(&mut self, source: &mut SourceInstruction, action: Action) -> Result<()> { match action { - Action::StackCheck => self.instrument_stack_check(), + Action::StackCheck => self.instrument_stack_check(source), Action::NewStackReference { lvalue, rvalue } => { - self.instrument_new_stack_reference(lvalue, rvalue) + eprintln!("instrumenting stack ref"); + self.instrument_new_stack_reference(source, lvalue, rvalue) } Action::StackUpdateReference { place, ty } => { - self.instrument_stack_update_ref(place, ty) + self.instrument_stack_update_ref(source, place, ty) } Action::NewMutRefFromRaw { lvalue, rvalue, ty } => { - self.instrument_new_mut_ref_from_raw(lvalue, rvalue, ty) + self.instrument_new_mut_ref_from_raw(source, lvalue, rvalue, ty) + } + Action::StackUpdatePointer { place, ty } => { + self.instrument_stack_update_ptr(source, place, ty) } - Action::StackUpdatePointer { place, ty } => self.instrument_stack_update_ptr(place, ty), Action::NewMutRawFromRef { lvalue, rvalue, ty } => { - self.instrument_new_mut_raw_from_ref(lvalue, rvalue, ty) + self.instrument_new_mut_raw_from_ref(source, lvalue, rvalue, ty) } } } @@ -396,47 +326,26 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// with appropriate updates to the stacked borrows state /// and with validity assertions on the stacked borrows state. pub fn instrument_instructions(&mut self) -> Result<()> { - loop { - let actions = self.instruction_actions(); - if !actions.is_empty() { - eprintln!("Instrumenting actions:"); - self.process_instruction(); - } - for action in actions { - eprintln!("Action is: {:?}", action); - self.instrument_action(action)?; - } - self.min_processed = match self.min_processed { - SourceInstruction::Statement { idx: 0, bb: 0 } => { - break; - } - SourceInstruction::Statement { idx: 0, bb } => { - SourceInstruction::Terminator { bb: bb - 1 } - } - SourceInstruction::Statement { idx, bb } => { - SourceInstruction::Statement { idx: idx - 1, bb } - } - SourceInstruction::Terminator { bb } - if !self.body.blocks()[bb].statements.is_empty() => - { - SourceInstruction::Statement { - idx: self.body.blocks()[bb].statements.len() - 1, - bb, - } - } - SourceInstruction::Terminator { bb } if bb > 0 => { - SourceInstruction::Terminator { bb: bb - 1 } - } - SourceInstruction::Terminator { .. } => { - break; - } + let to_instrument = self.actions.take().unwrap(); + for (mut source, actions) in to_instrument.into_iter().rev() { + for action in actions.into_iter() { + self.instrument_action(&mut source, action)?; } } Ok(()) } - /// Finalize the instrumentation of the body - pub fn finalize(self) -> MutableBody { - self.body + /// Run the passes and retrieve the mutable body + pub fn finalize(mut self) -> Result { + for local in ((self.body.arg_count() + 1)..self.local_count).rev() { + let ty = self.body.locals()[local].ty; + let ptr_ty = Ty::new_ptr(ty, Mutability::Not); + let local_ptr = self.body.new_local(ptr_ty, self.span, Mutability::Not); + self.meta_stack.insert(local, local_ptr); + } + eprintln!("instrumenting instructions"); + self.instrument_instructions()?; + // self.instrument_locals()?; + Ok(self.body) } } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 84184cc7a747..be8fca7652ad 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -11,8 +11,8 @@ use stable_mir::CrateDef; pub use stable_mir::mir::mono::Instance as MirInstance; pub use stable_mir::Error as MirError; -mod actions; -use actions::*; +mod visitor; +use visitor::*; mod function_cache; use function_cache::*; mod instrumentation; @@ -25,6 +25,7 @@ use rustc_middle::ty::TyCtxt; use stable_mir::mir::Body; use std::collections::{HashSet, VecDeque}; use std::fmt::Debug; +use std::io::stderr; use tracing::trace; use super::GlobalPass; @@ -78,11 +79,11 @@ impl<'cache> TransformPass for AliasingPass<'cache> { fn transform(&mut self, tcx: TyCtxt, body: Body, instance: MirInstance) -> (bool, Body) { trace!(function=?instance.name(), "transform: aliasing pass"); // let body = CachedBodyMutator::from(body); - let mut instrumentation_data = InstrumentationData::new(tcx, &mut self.cache, body); - // let out = BodyMutationPassState::new(instrumentation_data).finalize(); - instrumentation_data.instrument_locals().unwrap(); - instrumentation_data.instrument_instructions().unwrap(); - (true, instrumentation_data.finalize().into()) + body.dump(&mut stderr(), "main"); + let instrumentation_data = InstrumentationData::new(tcx, &mut self.cache, body); + let out = instrumentation_data.finalize().unwrap().into(); + out.dump(&mut stderr(), "the output"); + (true, out) } } @@ -125,7 +126,7 @@ impl GlobalPass for GlobalAliasingPass { .def .all_attrs() .into_iter() - .all(|attr| attr.as_str().contains("kanitool::proof")) + .any(|attr| attr.as_str().contains("kanitool::proof")) && found.insert(instance) { queue.push_back(instance) From cff210215722c88473346665b02ac50fc95efac4 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Wed, 28 Aug 2024 12:58:12 -0400 Subject: [PATCH 53/72] Continue implementing the changes Artem suggested --- .../check_aliasing/instrumentation.rs | 36 ++- .../transform/check_aliasing/visitor.rs | 249 ++++++++++++++++++ 2 files changed, 274 insertions(+), 11 deletions(-) create mode 100644 kani-compiler/src/kani_middle/transform/check_aliasing/visitor.rs diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 2d8f331f1eed..665b5a4c82a1 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -10,10 +10,9 @@ use crate::kani_middle::transform::body::InsertPosition; use rustc_middle::ty::TyCtxt; use stable_mir::mir::{ - BasicBlock, Body, Local, MirVisitor, Mutability, Operand, Place, Rvalue, Statement, - StatementKind, Terminator, TerminatorKind, UnwindAction, + BasicBlock, Body, ConstOperand, Local, MirVisitor, Mutability, Operand, Place, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, UnwindAction }; -use stable_mir::ty::{GenericArgKind, Span, Ty}; +use stable_mir::ty::{GenericArgKind, MirConst, Span, Ty}; use std::collections::HashMap; use super::super::body::{MutableBody, SourceInstruction}; @@ -184,7 +183,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let ty = self.body.locals()[rvalue].ty; let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); let rvalue_ref = self.meta_stack.get(&rvalue).unwrap(); - self.call( + self.instrument_call( Signature::new("KaniNewMutRefFromValue", &[GenericArgKind::Type(ty)]), vec![*lvalue_ref, *rvalue_ref], self.unit, @@ -302,23 +301,26 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// update to the stacked borrows state. fn instrument_action(&mut self, source: &mut SourceInstruction, action: Action) -> Result<()> { match action { - Action::StackCheck => self.instrument_stack_check(source), + Action::StackCheck => { + self.instrument_stack_check(source); + Ok(()) + }, Action::NewStackReference { lvalue, rvalue } => { eprintln!("instrumenting stack ref"); self.instrument_new_stack_reference(source, lvalue, rvalue) - } + }, Action::StackUpdateReference { place, ty } => { self.instrument_stack_update_ref(source, place, ty) - } + }, Action::NewMutRefFromRaw { lvalue, rvalue, ty } => { self.instrument_new_mut_ref_from_raw(source, lvalue, rvalue, ty) - } + }, Action::StackUpdatePointer { place, ty } => { self.instrument_stack_update_ptr(source, place, ty) - } + }, Action::NewMutRawFromRef { lvalue, rvalue, ty } => { self.instrument_new_mut_raw_from_ref(source, lvalue, rvalue, ty) - } + }, } } @@ -335,6 +337,17 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Ok(()) } + pub fn instrument_initialize_stack_check(&mut self) { + let mut source = self.first_instruction(); + let span = self.first_instruction().span(self.body.blocks()); + let lvalue = Place::from(0); + let user_ty = None; + let const_ = MirConst::from_bool(true); + let operand = Operand::Constant(ConstOperand { span, user_ty, const_ }); + let statement = Statement { kind: StatementKind::Assign(Place::from(self.valid), Rvalue::Use(operand)), span }; + self.body.insert_stmt(statement, &mut source, InsertPosition::Before); + } + /// Run the passes and retrieve the mutable body pub fn finalize(mut self) -> Result { for local in ((self.body.arg_count() + 1)..self.local_count).rev() { @@ -345,7 +358,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { } eprintln!("instrumenting instructions"); self.instrument_instructions()?; - // self.instrument_locals()?; + self.instrument_locals()?; + self.instrument_initialize_stack_check(); Ok(self.body) } } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/visitor.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/visitor.rs new file mode 100644 index 000000000000..a184e95a2838 --- /dev/null +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/visitor.rs @@ -0,0 +1,249 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +//! This module contains stacked borrows "actions," +//! or updates to the stacked borrows state, as well as +//! methods that collect the actions that need to be applied from the +//! statements of the code. + +use std::usize; + +use stable_mir::mir::visit::Location; +use stable_mir::mir::{ + BorrowKind, Local, LocalDecl, MirVisitor, Mutability, Operand, Place, ProjectionElem, Rvalue, + Statement, StatementKind, Terminator, +}; +use stable_mir::ty::{RigidTy, Ty, TyKind}; + +use crate::kani_middle::transform::body::SourceInstruction; + +/// Update action to the stacked borrows state +#[derive(Debug)] +pub enum Action { + StackCheck, + NewStackReference { lvalue: Local, rvalue: usize }, + StackUpdateReference { place: usize, ty: Ty }, + NewMutRefFromRaw { lvalue: usize, rvalue: usize, ty: Ty }, + StackUpdatePointer { place: usize, ty: Ty }, + NewMutRawFromRef { lvalue: usize, rvalue: usize, ty: Ty }, +} + +/// The actions of a statement +pub struct CollectActions<'locals> { + /// The source instruction currently being visited + source: SourceInstruction, + /// The current actions collected + collected: Vec, + /// The code actions to insert behind the given + /// instruction + actions: Vec<(SourceInstruction, Vec)>, + /// The locals, required to ensure that the references + /// and pointers are picked appropriately. + locals: &'locals [LocalDecl], +} + +impl<'locals> CollectActions<'locals> { + /// Initialize the struct using the given locals + pub fn new(locals: &'locals [LocalDecl]) -> Self { + CollectActions { + source: SourceInstruction::Statement { idx: 0, bb: 0 }, + collected: Vec::new(), + actions: Vec::new(), + locals, + } + } + + pub fn finalize(self) -> Vec<(SourceInstruction, Vec)> { + self.actions + } + + /// Collect the actions for assigning the lvalue + /// to the dereferenced rvalue + fn visit_assign_reference_dereference(&mut self, lvalue: Local, rvalue: Local) { + match self.locals[rvalue].ty.kind() { + TyKind::RigidTy(RigidTy::Ref(_, ty, _)) | TyKind::RigidTy(RigidTy::RawPtr(ty, _)) => { + self.collected.push(Action::NewMutRefFromRaw { lvalue, rvalue, ty }); + } + _ => {} + } + } + + /// Collect the actions for assigning the reference in + /// "place" to the local at "to". + fn visit_assign_reference(&mut self, to: Local, from: Place) { + match from.projection[..] { + [] => { + // Direct reference to stack local + // x = &y; + let lvalue = to; + let rvalue = from.local; + self.collected.push(Action::NewStackReference { lvalue, rvalue }); + } + [ProjectionElem::Deref] => { + // Reborrow + // x : &mut T = &*(y : *mut T OR &mut T) + let lvalue = to; // Copy to avoid borrow + let rvalue = from.local; // Copy to avoid borrow + self.visit_assign_reference_dereference(lvalue, rvalue); + } + _ => { + eprintln!("not yet handled: assignment to reference {:?}", from) + } + } + } + + /// Collect the actions for assigning the data at + /// from to the local at to. + fn visit_assign_pointer(&mut self, to: Local, from: Place) { + match from.projection[..] { + [] => { + // x = &raw y + panic!("Addr of not yet handled"); + } + [ProjectionElem::Deref] => { + // x = &raw mut *(y: &mut T OR *mut T) + let rvalue = from.local; // Copy to avoid borrow + let lvalue = to; + match self.locals[rvalue].ty.kind() { + TyKind::RigidTy(RigidTy::Ref(_, ty, _)) => { + self.collected.push(Action::NewMutRawFromRef { lvalue, rvalue, ty }); + } + _ => { + panic!( + "Dereference of rvalue case not yet handled for raw pointers {:?}", + from + ); + } + } + } + _ => {} + } + } + + /// Collect the actions for a Place, + /// incurring a stack check in the case of a + /// dereference of a pointer or reference + fn visit_place(&mut self, place: &Place) { + match &place.projection[..] { + [] => { + // Direct usage -- no update needed + return; + } + [ProjectionElem::Deref] => { + // Dereference -- instrument stack check + } + _ => { + // Field access -- not yet handled. + return; + } + }; + if self.locals[place.local].ty.kind().is_ref() { + let ty = place.ty(self.locals).unwrap(); + self.collected.push(Action::StackUpdateReference { place: place.local, ty }); + self.collected.push(Action::StackCheck); + } + if self.locals[place.local].ty.kind().is_raw_ptr() { + let ty = place.ty(self.locals).unwrap(); + self.collected.push(Action::StackUpdatePointer { place: place.local, ty }); + self.collected.push(Action::StackCheck); + } + } + + /// Collect the actions for the places of an Rvalue + fn visit_rvalue_places(&mut self, rvalue: &Rvalue) { + match rvalue { + Rvalue::AddressOf(_, place) => { + self.visit_place(place); + } + Rvalue::Ref(_, _, place) => { + self.visit_place(place); + } + // The rest are not yet handled + _ => { + eprintln!("Not yet handled: {:?}", rvalue); + } + } + } + + /// Collect the actions for the places of a statement + fn visit_statement_places(&mut self, stmt: &Statement) { + match &stmt.kind { + StatementKind::Assign(place, rvalue) => { + self.visit_rvalue_places(rvalue); + self.visit_place(place); + } + _ => { + eprintln!("Not yet handled: {:?}", stmt); + } + } + } + + /// Collect the actions for the places of the statement, + /// then find assignments of pointer values to lvalues + /// and collect updates to the stacked borrows state + /// accordingly. + /// This is performed in a different order than Mir Visitor's + /// visit statement, and so we call it from the visitor. + fn visit_statement_internal(&mut self, stmt: &Statement) { + self.visit_statement_places(stmt); + match &stmt.kind { + StatementKind::Assign(to, rvalue) => { + match rvalue { + Rvalue::Ref(_, BorrowKind::Mut { .. }, from) => { + self.visit_assign_reference(to.local, from.clone()); + } + Rvalue::AddressOf(Mutability::Mut, from) => { + self.visit_assign_pointer(to.local, from.clone()); + } + Rvalue::Use(Operand::Constant(_)) => { + // Do nothing for the constants case + } + Rvalue::Use(Operand::Copy(_)) => { + eprintln!("Copy not yet handled"); + // Do nothing for the constants case + } + Rvalue::Use(Operand::Move(_)) => { + eprintln!("Move not yet handled"); + // Do nothing for the constants case + } + Rvalue::BinaryOp(_, _, _) => { + eprintln!("Binary op not yet handled"); + } + Rvalue::CheckedBinaryOp(_, _, _) => { + eprintln!("Checked binary op not yet handled"); + } + _ => { + panic!("Rvalue kind: {:?} not yet handled", rvalue); + } + } + } + _ => { + eprintln!("Not yet handled, {:?}", stmt); + } + } + } +} + +impl<'locals> MirVisitor for CollectActions<'locals> { + /// Visit the statement stmt + fn visit_statement(&mut self, stmt: &Statement, _location: Location) { + let collected = std::mem::replace(&mut self.collected, Vec::new()); + self.actions.push((self.source.clone(), collected)); + self.visit_statement_internal(stmt); + self.source = match self.source { + SourceInstruction::Statement { idx, bb } => { + SourceInstruction::Statement { idx: idx + 1, bb } + } + _ => { + unreachable!("Statements follow the first instruction or a terminator.") + } + }; + } + + /// Visit the terminator. + fn visit_terminator(&mut self, _term: &Terminator, _location: Location) { + let collected = std::mem::replace(&mut self.collected, Vec::new()); + let bb = self.source.bb(); + self.actions.push((SourceInstruction::Terminator { bb }, collected)); + self.source = SourceInstruction::Statement { idx: 0, bb: bb + 1 }; + } +} From 05cb010b9f0100a7649cfbea120bd042494964dc Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Wed, 28 Aug 2024 22:15:37 -0400 Subject: [PATCH 54/72] Complete instrumentation of instructions accum. by visitor --- .../check_aliasing/instrumentation.rs | 24 ++++++++++++++----- .../transform/check_aliasing/mod.rs | 2 -- tests/expected/aliasing/control_flow.expected | 5 +--- .../aliasing/duplicate_write.expected | 3 +-- .../duplicate_write_compressed.expected | 4 ++-- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 665b5a4c82a1..12869739ad43 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -108,7 +108,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let fn_local = self.register_fn(callee)?; let func = Operand::Copy(Place::from(fn_local)); let args = args.into_iter().map(|local| Operand::Copy(Place::from(local))).collect(); - let destination = Place::from(self.unit); + let destination = Place::from(dest); let target = Some(0); // doesn't matter, updated later let unwind = UnwindAction::Terminate; let kind = TerminatorKind::Call { func, args, destination, target, unwind }; @@ -192,11 +192,22 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Ok(()) } + fn decrement_source(&self, source: &SourceInstruction) -> SourceInstruction { + match source { + SourceInstruction::Statement { idx: 0, bb: 0 } => SourceInstruction::Terminator { bb: 0 }, + SourceInstruction::Statement { idx: 0, bb } => SourceInstruction::Terminator { bb: *bb - 1 }, + SourceInstruction::Statement { idx, bb } => SourceInstruction::Statement { idx: idx - 1, bb: *bb }, + SourceInstruction::Terminator { bb } if self.body.blocks()[*bb].statements.is_empty() => SourceInstruction::Terminator { bb: *bb - 1 }, + SourceInstruction::Terminator { bb } => SourceInstruction::Statement { idx: self.body.blocks()[*bb].statements.len() - 1, bb: *bb } + } + } + /// Instrument with stack violated / not violated - pub fn instrument_stack_check(&mut self, source: &mut SourceInstruction) -> Result<()> { + pub fn instrument_stack_check(&mut self, source: &mut SourceInstruction, check_source: &SourceInstruction) -> Result<()> { + let less = self.decrement_source(check_source); + let msg = format!("Stacked borrows state invalidated at {:?}", less.span(self.body.blocks()).get_lines()); self.instrument_call(Signature::new("KaniStackValid", &[]), vec![], self.valid, source)?; let assert = self.register_fn(Signature::new("KaniAssert", &[]))?; - let msg = "Stacked borrows aliasing model violated."; self.body.insert_check_with_local( self.tcx, assert, @@ -299,10 +310,10 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// Instrument the action given in "action" with the appropriate /// update to the stacked borrows state. - fn instrument_action(&mut self, source: &mut SourceInstruction, action: Action) -> Result<()> { + fn instrument_action(&mut self, source: &mut SourceInstruction, check_source: &SourceInstruction, action: Action) -> Result<()> { match action { Action::StackCheck => { - self.instrument_stack_check(source); + self.instrument_stack_check(source, check_source); Ok(()) }, Action::NewStackReference { lvalue, rvalue } => { @@ -330,8 +341,9 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { pub fn instrument_instructions(&mut self) -> Result<()> { let to_instrument = self.actions.take().unwrap(); for (mut source, actions) in to_instrument.into_iter().rev() { + let check_source = source.clone(); for action in actions.into_iter() { - self.instrument_action(&mut source, action)?; + self.instrument_action(&mut source, &check_source, action)?; } } Ok(()) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index be8fca7652ad..0f5ad7a8697d 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -79,10 +79,8 @@ impl<'cache> TransformPass for AliasingPass<'cache> { fn transform(&mut self, tcx: TyCtxt, body: Body, instance: MirInstance) -> (bool, Body) { trace!(function=?instance.name(), "transform: aliasing pass"); // let body = CachedBodyMutator::from(body); - body.dump(&mut stderr(), "main"); let instrumentation_data = InstrumentationData::new(tcx, &mut self.cache, body); let out = instrumentation_data.finalize().unwrap().into(); - out.dump(&mut stderr(), "the output"); (true, out) } } diff --git a/tests/expected/aliasing/control_flow.expected b/tests/expected/aliasing/control_flow.expected index 42969176f422..17ee23f65ea5 100644 --- a/tests/expected/aliasing/control_flow.expected +++ b/tests/expected/aliasing/control_flow.expected @@ -1,4 +1 @@ -Failed Checks: Stacked borrows aliasing model violated. - File: "./tests/expected/aliasing/control_flow.rs", line 23, in main -Failed Checks: Stacked borrows aliasing model violated. - File: "./tests/expected/aliasing/control_flow.rs", line 24, in main +Failed Checks: Stacked borrows state invalidated at LineInfo { start_line: 24, start_col: 17, end_line: 24, end_col: 36 } diff --git a/tests/expected/aliasing/duplicate_write.expected b/tests/expected/aliasing/duplicate_write.expected index 2a5160581626..35c16764bb35 100644 --- a/tests/expected/aliasing/duplicate_write.expected +++ b/tests/expected/aliasing/duplicate_write.expected @@ -1,2 +1 @@ -Failed Checks: Stacked borrows aliasing model violated. - File: "./tests/expected/aliasing/duplicate_write.rs", line 21, in main +Failed Checks: Stacked borrows state invalidated at LineInfo { start_line: 21, start_col: 9, end_line: 21, end_col: 28 } diff --git a/tests/expected/aliasing/duplicate_write_compressed.expected b/tests/expected/aliasing/duplicate_write_compressed.expected index 584132639cae..d7c837715c8e 100644 --- a/tests/expected/aliasing/duplicate_write_compressed.expected +++ b/tests/expected/aliasing/duplicate_write_compressed.expected @@ -1,2 +1,2 @@ -Failed Checks: Stacked borrows aliasing model violated. - File: "./tests/expected/aliasing/duplicate_write_compressed.rs", line 14, in main + +Failed Checks: Stacked borrows state invalidated at LineInfo { start_line: 14, start_col: 9, end_line: 14, end_col: 28 } From b44789881d58af87edb31575a9c60e1c7002b2ba Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Wed, 28 Aug 2024 22:25:39 -0400 Subject: [PATCH 55/72] Comment the instrumentation file --- .../check_aliasing/instrumentation.rs | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 12869739ad43..03295577e1b4 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -192,19 +192,32 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Ok(()) } - fn decrement_source(&self, source: &SourceInstruction) -> SourceInstruction { + /// Return the source instruction one below "source," + /// or the instruction at 0 if there are none below + fn predecessor(&self, source: &SourceInstruction) -> SourceInstruction { match source { - SourceInstruction::Statement { idx: 0, bb: 0 } => SourceInstruction::Terminator { bb: 0 }, + // One below 0, 0 is 0, 0 + SourceInstruction::Statement { idx: 0, bb: 0 } => SourceInstruction::Statement { idx: 0, bb: 0 }, + // One below the first statement of a block is the + // previous terminator SourceInstruction::Statement { idx: 0, bb } => SourceInstruction::Terminator { bb: *bb - 1 }, + // Otherwise one below the statement of a block is the + // previous statement SourceInstruction::Statement { idx, bb } => SourceInstruction::Statement { idx: idx - 1, bb: *bb }, + // One below the terminator of the first block with no statements + // is 0, 0 + SourceInstruction::Terminator { bb: 0 } if self.body.blocks()[*bb].statements.is_empty() => SourceInstruction::Statement { idx: 0, bb: 0 }, + // Otherwise one below the terminator of a block is the previous + // terminator if there are no statements in the block, SourceInstruction::Terminator { bb } if self.body.blocks()[*bb].statements.is_empty() => SourceInstruction::Terminator { bb: *bb - 1 }, + // Or the last statement of the current block. SourceInstruction::Terminator { bb } => SourceInstruction::Statement { idx: self.body.blocks()[*bb].statements.len() - 1, bb: *bb } } } /// Instrument with stack violated / not violated pub fn instrument_stack_check(&mut self, source: &mut SourceInstruction, check_source: &SourceInstruction) -> Result<()> { - let less = self.decrement_source(check_source); + let less = self.predecessor(check_source); let msg = format!("Stacked borrows state invalidated at {:?}", less.span(self.body.blocks()).get_lines()); self.instrument_call(Signature::new("KaniStackValid", &[]), vec![], self.valid, source)?; let assert = self.register_fn(Signature::new("KaniAssert", &[]))?; @@ -349,14 +362,17 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { Ok(()) } + /// Initialize the "valid" local, used to check whether + /// the stack has been invalidated pub fn instrument_initialize_stack_check(&mut self) { let mut source = self.first_instruction(); let span = self.first_instruction().span(self.body.blocks()); - let lvalue = Place::from(0); + let lvalue = Place::from(self.valid); let user_ty = None; let const_ = MirConst::from_bool(true); let operand = Operand::Constant(ConstOperand { span, user_ty, const_ }); - let statement = Statement { kind: StatementKind::Assign(Place::from(self.valid), Rvalue::Use(operand)), span }; + let rvalue = Rvalue::Use(operand); + let statement = Statement { kind: StatementKind::Assign(lvalue, rvalue), span }; self.body.insert_stmt(statement, &mut source, InsertPosition::Before); } From aa0e69172e980e9fa29ec4ea48bfeffba946dab5 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Wed, 28 Aug 2024 22:28:26 -0400 Subject: [PATCH 56/72] Comment the visitor file --- .../kani_middle/transform/check_aliasing/visitor.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/visitor.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/visitor.rs index a184e95a2838..19041cc15574 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/visitor.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/visitor.rs @@ -224,10 +224,13 @@ impl<'locals> CollectActions<'locals> { } impl<'locals> MirVisitor for CollectActions<'locals> { - /// Visit the statement stmt + /// Visit the statement stmt. + /// Associate the actions collected so far with the + /// current source index, then collect the actions + /// of the statement and increase the source index fn visit_statement(&mut self, stmt: &Statement, _location: Location) { let collected = std::mem::replace(&mut self.collected, Vec::new()); - self.actions.push((self.source.clone(), collected)); + self.actions.push((self.source, collected)); self.visit_statement_internal(stmt); self.source = match self.source { SourceInstruction::Statement { idx, bb } => { @@ -240,6 +243,9 @@ impl<'locals> MirVisitor for CollectActions<'locals> { } /// Visit the terminator. + /// Associate the actions collected so far with the + /// current source index, then increase the source index + /// to the next basic block fn visit_terminator(&mut self, _term: &Terminator, _location: Location) { let collected = std::mem::replace(&mut self.collected, Vec::new()); let bb = self.source.bb(); From a4133c4d3916f2c2044365c922ddc3676d9e6524 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Wed, 28 Aug 2024 23:50:53 -0400 Subject: [PATCH 57/72] Fix warnings, regression caused by terminator case --- kani-compiler/src/kani_middle/reachability.rs | 3 +++ kani-compiler/src/kani_middle/transform/body.rs | 4 +--- .../kani_middle/transform/check_aliasing/function_cache.rs | 5 ----- .../kani_middle/transform/check_aliasing/instrumentation.rs | 6 ++---- .../src/kani_middle/transform/check_aliasing/mod.rs | 1 - 5 files changed, 6 insertions(+), 13 deletions(-) diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 9062c1f1b82e..59f779d1c7d5 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -579,6 +579,7 @@ impl CallGraph { /// Print the graph in DOT format to a file. /// See for more information. + #[allow(unused)] fn dump_dot(&self, tcx: TyCtxt) -> std::io::Result<()> { if let Ok(target) = std::env::var("KANI_REACH_DEBUG") { debug!(?target, "dump_dot"); @@ -601,6 +602,7 @@ impl CallGraph { } /// Write all notes to the given writer. + #[allow(unused)] 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 { @@ -614,6 +616,7 @@ impl CallGraph { } /// Write all notes that may have led to the discovery of the given target. + #[allow(unused)] 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(); diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index 8608421dce87..7cf2d9cc1722 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -174,7 +174,6 @@ impl MutableBody { /// point to the new terminator. pub fn insert_check_with_local( &mut self, - tcx: TyCtxt, local: Local, source: &mut SourceInstruction, position: InsertPosition, @@ -225,7 +224,6 @@ impl MutableBody { 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) => { @@ -234,7 +232,7 @@ impl MutableBody { span, Mutability::Not, ); - self.insert_check_with_local(tcx, local, source, position, value, msg); + self.insert_check_with_local(local, source, position, value, msg); } CheckType::Panic | CheckType::NoCore => { tcx.sess diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs index 493779137035..9717376e278b 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs @@ -89,9 +89,4 @@ impl Cache { ) .map(|entry| &entry.instance) } - - /// Register the kani assertion function - pub fn register_assert(&mut self, ctx: &TyCtxt) -> Result<&MirInstance, MirError> { - self.register(ctx, Signature::new("KaniAssert", &[])) - } } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 03295577e1b4..ef5a61a50b13 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -206,7 +206,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { SourceInstruction::Statement { idx, bb } => SourceInstruction::Statement { idx: idx - 1, bb: *bb }, // One below the terminator of the first block with no statements // is 0, 0 - SourceInstruction::Terminator { bb: 0 } if self.body.blocks()[*bb].statements.is_empty() => SourceInstruction::Statement { idx: 0, bb: 0 }, + SourceInstruction::Terminator { bb: 0 } if self.body.blocks()[0].statements.is_empty() => SourceInstruction::Statement { idx: 0, bb: 0 }, // Otherwise one below the terminator of a block is the previous // terminator if there are no statements in the block, SourceInstruction::Terminator { bb } if self.body.blocks()[*bb].statements.is_empty() => SourceInstruction::Terminator { bb: *bb - 1 }, @@ -222,7 +222,6 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { self.instrument_call(Signature::new("KaniStackValid", &[]), vec![], self.valid, source)?; let assert = self.register_fn(Signature::new("KaniAssert", &[]))?; self.body.insert_check_with_local( - self.tcx, assert, source, InsertPosition::Before, @@ -326,8 +325,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { fn instrument_action(&mut self, source: &mut SourceInstruction, check_source: &SourceInstruction, action: Action) -> Result<()> { match action { Action::StackCheck => { - self.instrument_stack_check(source, check_source); - Ok(()) + self.instrument_stack_check(source, check_source) }, Action::NewStackReference { lvalue, rvalue } => { eprintln!("instrumenting stack ref"); diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 0f5ad7a8697d..6a83e05a766e 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -25,7 +25,6 @@ use rustc_middle::ty::TyCtxt; use stable_mir::mir::Body; use std::collections::{HashSet, VecDeque}; use std::fmt::Debug; -use std::io::stderr; use tracing::trace; use super::GlobalPass; From 61f8ce7ed325a91f0fe6e869814a535889879b94 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 29 Aug 2024 11:54:15 -0400 Subject: [PATCH 58/72] Modify comments, change to enums. --- .../check_aliasing/instrumentation.rs | 2 +- .../transform/check_aliasing/visitor.rs | 6 +- library/kani/src/aliasing.rs | 119 +++++++++--------- 3 files changed, 59 insertions(+), 68 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index ef5a61a50b13..efebb5ab05cb 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -352,7 +352,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { pub fn instrument_instructions(&mut self) -> Result<()> { let to_instrument = self.actions.take().unwrap(); for (mut source, actions) in to_instrument.into_iter().rev() { - let check_source = source.clone(); + let check_source = source; for action in actions.into_iter() { self.instrument_action(&mut source, &check_source, action)?; } diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/visitor.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/visitor.rs index 19041cc15574..5ace61e85723 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/visitor.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/visitor.rs @@ -5,8 +5,6 @@ //! methods that collect the actions that need to be applied from the //! statements of the code. -use std::usize; - use stable_mir::mir::visit::Location; use stable_mir::mir::{ BorrowKind, Local, LocalDecl, MirVisitor, Mutability, Operand, Place, ProjectionElem, Rvalue, @@ -229,7 +227,7 @@ impl<'locals> MirVisitor for CollectActions<'locals> { /// current source index, then collect the actions /// of the statement and increase the source index fn visit_statement(&mut self, stmt: &Statement, _location: Location) { - let collected = std::mem::replace(&mut self.collected, Vec::new()); + let collected = std::mem::take(&mut self.collected); self.actions.push((self.source, collected)); self.visit_statement_internal(stmt); self.source = match self.source { @@ -247,7 +245,7 @@ impl<'locals> MirVisitor for CollectActions<'locals> { /// current source index, then increase the source index /// to the next basic block fn visit_terminator(&mut self, _term: &Terminator, _location: Location) { - let collected = std::mem::replace(&mut self.collected, Vec::new()); + let collected = std::mem::take(&mut self.collected); let bb = self.source.bb(); self.actions.push((SourceInstruction::Terminator { bb }, collected)); self.source = SourceInstruction::Statement { idx: 0, bb: bb + 1 }; diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 54d16939bf41..03a422b13c1b 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -22,32 +22,32 @@ //! //! For example: //! ```rust -//! let mut x: i32 = 10; //! // Stack allocate 10 and store it at x. //! // Stack at addr_of!(x) through addr_of!(x) + 4: -//! // [(0, Permission::UNIQUE)] -//! let y = &mut x; +//! // [(TAG_0, Permission::UNIQUE)] +//! let mut x: i32 = 10; //! // Make the pointer object `&mut x`. Associate `&mut x` //! // with the tag and permission `(1, Permission::UNIQUE)` //! // by associating `addr_of!(y)` with `(1, Permission::UNIQUE)` //! // in shadow memory. Push the tag to the borrow stacks of //! // `addr_of!(x)` through `addr_of!(x) + 4` yielding -//! // the stacks [(0, Permission::UNIQUE), (1, Permission::UNIQUE)] -//! let z = y as *mut i32; +//! // the stacks [(TAG_0, Permission::UNIQUE), (TAG_1, Permission::UNIQUE)] +//! let y = &mut x; //! // Make the pointer object `y as *mut i32`. //! // associate `addr_of!(z)` and push the stacks as //! // above with the tag (2, Permission::SHAREDRW), //! // corresponding to a raw pointer, yielding the stacks -//! // [(0, Permission::UNIQUE), (1, Permission::UNIQUE), -//! // (2, Permission::SHAREDRW)]. +//! // [(TAG_0, Permission::UNIQUE), (TAG_1, Permission::UNIQUE), +//! // (TAG_2, Permission::SHAREDRW)]. +//! let z = y as *mut i32; +//! // Pop elements from the pointee object stack until it matches +//! // the tag associated with the pointer value, yielding +//! // [(TAG_0, Permission::UNIQUE), (TAG_1, Permission::UNIQUE)] //! *y = 10; -//! // Pop the stack down to the tag associated with the pointer -//! // object created at `&mut x` yielding -//! // [(0, Permission::UNIQUE), (1, Permission::UNIQUE)] -//! unsafe { *(&mut *z) = 10; } //! // Run stack lookup on the tag associated with the pointer -//! // object created at `y as *mut i32`, ie, (2, Permission::SHAREDRW) +//! // object created at `y as *mut i32`, ie, (TAG_2, Permission::SHAREDRW) //! // resulting in an error. +//! unsafe { *(&mut *z) = 10; } //! ``` //! When demonic nondeterminism is used (currently it is always used), //! a nondeterminism oracle is queried to select a single byte of the program's @@ -83,55 +83,48 @@ fn stack_valid() -> bool { } /// Type of access. -/// To ensure that 1 bit, instead of -/// larger, representations are used in cbmc, -/// this is encoded using associated constants. -struct Access; -impl Access { - const READ: bool = false; - const WRITE: bool = true; +#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] +enum Access { + /// Read access + Read, + /// Write access + Write } -type AccessBit = bool; /// Type of permission. /// To ensure that 8 bit, instead of larger, /// repreesentations are used in cbmc, this /// is encoded using associated constants. -struct Permission; -impl Permission { - /// Unique corresponds to the original allocation - /// and to &mut. For each byte, this permission can - /// only be aquired once at any given time in the program, - /// therefore it is called "unique." - const UNIQUE: u8 = 0; - /// SharedRW corresponds to a mutable pointer. - const SHAREDRW: u8 = 1; - /// SharedRO corresponds to a const pointer - const SHAREDRO: u8 = 2; - /// Disabled corresponds to disabling writable pointers - /// during 2-phase borrows (not yet implemented) - const DISABLED: u8 = 3; +#[repr(u8)] +#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] +enum Permission { + /// Read/write reference with unique ownership + Unique, + /// Pointer with read/write access + SharedRW, + /// Pointer with read access + SharedRO, + /// Disabled pointer + Disabled, } -type PermissionByte = u8; - impl Permission { /// Returns whether the access bit is granted by the permission /// byte - fn grants(access: AccessBit, tag: PermissionByte) -> bool { - tag != Self::DISABLED && (access != Access::WRITE || tag != Self::SHAREDRO) + fn grants(access: Access, perm: Permission) -> bool { + perm != Permission::Disabled && (access != Access::Write || perm != Permission::SharedRO) } } /// Associate every pointer object with a permission -static mut PERMS: ShadowMem = ShadowMem::new(Permission::UNIQUE); +static mut PERMS: ShadowMem = ShadowMem::new(Permission::SharedRO); /// State of the borrows stack monitor for a byte pub(super) mod monitors { - struct MonitorState; - impl MonitorState { - const ON: bool = true; - const OFF: bool = false; + #[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] + enum MonitorState { + On, + Off } #[allow(unused)] @@ -140,13 +133,13 @@ pub(super) mod monitors { /// Whether the monitor is on. Initially, the monitor is /// "off", and it will remain so until an allocation is found /// to track. - static mut STATE: bool = MonitorState::OFF; + static mut STATE: MonitorState = MonitorState::Off; /// Object + offset being monitored pub static mut MONITORED: *const u8 = std::ptr::null(); /// The tags of the pointer objects borrowing the byte - static mut STACK_TAGS: [PointerTag; STACK_DEPTH] = [INITIAL_TAG; STACK_DEPTH]; + static mut STACK_TAGS: [PointerTag; STACK_DEPTH] = [0; STACK_DEPTH]; /// The permissions of the pointer objects borrowing the byte - static mut STACK_PERMS: [PermissionByte; STACK_DEPTH] = [Permission::UNIQUE; STACK_DEPTH]; + static mut STACK_PERMS: [Permission; STACK_DEPTH] = [Permission::Unique; STACK_DEPTH]; /// The "top" of the stack static mut STACK_TOP: usize = 0; @@ -158,26 +151,26 @@ pub(super) mod monitors { // Decide whether to initialize the stacks // for location:location+size_of(U). unsafe { - if demonic_nondet() && STATE == MonitorState::OFF { - STATE = MonitorState::ON; + if demonic_nondet() && STATE == MonitorState::Off { + STATE = MonitorState::On; let offset: usize = kani::any(); crate::assume(offset < std::mem::size_of::()); MONITORED = pointer.byte_add(offset) as *const u8; STACK_TAGS[STACK_TOP] = tag; - STACK_PERMS[STACK_TOP] = Permission::UNIQUE; + STACK_PERMS[STACK_TOP] = Permission::Unique; STACK_TOP += 1; } } } /// Push a tag with a permission perm at pointer - pub(super) fn push(tag: u8, perm: u8, pointer: *const U) { + pub(super) fn push(tag: u8, perm: Permission, pointer: *const U) { // Decide whether to initialize the stacks // for location:location+size_of(U). // Offset has already been picked earlier. unsafe { use self::*; - if STATE == MonitorState::ON + if STATE == MonitorState::On && pointer_object(MONITORED) == pointer_object(pointer) && pointer_offset(MONITORED) <= std::mem::size_of::() { @@ -188,10 +181,10 @@ pub(super) mod monitors { } } - pub(super) fn stack_check(tag: u8, access: bool) { + pub(super) fn stack_check(tag: u8, access: Access) { unsafe { use self::*; - if STATE == MonitorState::ON { + if STATE == MonitorState::On { let mut found = false; let mut j = 0; let mut new_top = 0; @@ -215,7 +208,7 @@ pub(super) mod monitors { } /// Push the permissions at the given location -fn push(tag: u8, perm: u8, address: *const U) { +fn push(tag: u8, perm: Permission, address: *const U) { self::monitors::push(tag, perm, address) } @@ -234,7 +227,7 @@ fn initialize_local(pointer: *const U) { unsafe { let tag = NEXT_TAG; TAGS.set(pointer, tag); - PERMS.set(pointer, Permission::UNIQUE); + PERMS.set(pointer, Permission::Unique); NEXT_TAG += 1; monitors::track_local(tag, pointer); } @@ -249,10 +242,10 @@ fn stack_check_ptr(pointer_value: *const *mut U) { if pointer_object(pointer) == pointer_object(MONITORED) && pointer_offset(MONITORED) < std::mem::size_of::() { - if Permission::grants(Access::READ, perm) { - self::monitors::stack_check(tag, Access::READ); - } else if Permission::grants(Access::WRITE, perm) { - self::monitors::stack_check(tag, Access::WRITE); + if Permission::grants(Access::Read, perm) { + self::monitors::stack_check(tag, Access::Read); + } else if Permission::grants(Access::Write, perm) { + self::monitors::stack_check(tag, Access::Write); } } } @@ -272,8 +265,8 @@ fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); - PERMS.set(pointer_to_created, Permission::SHAREDRW); - push(NEXT_TAG, Permission::SHAREDRW, pointer_to_val); + PERMS.set(pointer_to_created, Permission::SharedRW); + push(NEXT_TAG, Permission::SharedRW, pointer_to_val); NEXT_TAG += 1; } } @@ -287,7 +280,7 @@ fn new_mut_raw_from_ref(pointer_to_created: *const *mut T, pointer_to_ref: *c unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); - push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); + push(NEXT_TAG, Permission::SharedRW, *pointer_to_ref); NEXT_TAG += 1; } } @@ -301,7 +294,7 @@ fn new_mut_ref_from_raw(pointer_to_created: *const &mut T, pointer_to_ref: *c unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); - push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); + push(NEXT_TAG, Permission::SharedRW, *pointer_to_ref); NEXT_TAG += 1; } } From 07028ee3e82047fa9338b74130fc6ab9ce22e084 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 29 Aug 2024 12:06:20 -0400 Subject: [PATCH 59/72] Try using bool instead --- library/kani/src/aliasing.rs | 57 +++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 03a422b13c1b..f5ce12018220 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -82,15 +82,19 @@ fn stack_valid() -> bool { unsafe { STACK_VALID } } -/// Type of access. -#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] -enum Access { - /// Read access - Read, - /// Write access - Write +/// Access bit. +/// Encoded as associated constants +/// instead of as an enum to ensure +/// that the representation uses +/// 1 bit. +type AccessBit = bool; +struct Access; +impl Access { + const READ: AccessBit = false; + const WRITE: AccessBit = true; } + /// Type of permission. /// To ensure that 8 bit, instead of larger, /// repreesentations are used in cbmc, this @@ -111,8 +115,8 @@ enum Permission { impl Permission { /// Returns whether the access bit is granted by the permission /// byte - fn grants(access: Access, perm: Permission) -> bool { - perm != Permission::Disabled && (access != Access::Write || perm != Permission::SharedRO) + fn grants(access: AccessBit, perm: Permission) -> bool { + perm != Permission::Disabled && (access != Access::WRITE || perm != Permission::SharedRO) } } @@ -121,10 +125,15 @@ static mut PERMS: ShadowMem = ShadowMem::new(Permission::SharedRO); /// State of the borrows stack monitor for a byte pub(super) mod monitors { - #[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] - enum MonitorState { - On, - Off + /// Tracks whether the monitor is on or off. + /// Encoded as associated constants instead + /// of as an enum to ensure that the representation + /// uses 1 bit. + type MonitorBit = bool; + struct MonitorState; + impl MonitorState { + const ON: MonitorBit = false; + const OFF: MonitorBit = true; } #[allow(unused)] @@ -133,7 +142,7 @@ pub(super) mod monitors { /// Whether the monitor is on. Initially, the monitor is /// "off", and it will remain so until an allocation is found /// to track. - static mut STATE: MonitorState = MonitorState::Off; + static mut STATE: MonitorBit = MonitorState::OFF; /// Object + offset being monitored pub static mut MONITORED: *const u8 = std::ptr::null(); /// The tags of the pointer objects borrowing the byte @@ -151,8 +160,8 @@ pub(super) mod monitors { // Decide whether to initialize the stacks // for location:location+size_of(U). unsafe { - if demonic_nondet() && STATE == MonitorState::Off { - STATE = MonitorState::On; + if demonic_nondet() && STATE == MonitorState::OFF { + STATE = MonitorState::ON; let offset: usize = kani::any(); crate::assume(offset < std::mem::size_of::()); MONITORED = pointer.byte_add(offset) as *const u8; @@ -170,7 +179,7 @@ pub(super) mod monitors { // Offset has already been picked earlier. unsafe { use self::*; - if STATE == MonitorState::On + if STATE == MonitorState::ON && pointer_object(MONITORED) == pointer_object(pointer) && pointer_offset(MONITORED) <= std::mem::size_of::() { @@ -181,10 +190,12 @@ pub(super) mod monitors { } } - pub(super) fn stack_check(tag: u8, access: Access) { + /// Run a stack check on the monitored byte for the given + /// tag and the given access permission. + pub(super) fn stack_check(tag: u8, access: AccessBit) { unsafe { use self::*; - if STATE == MonitorState::On { + if STATE == MonitorState::ON { let mut found = false; let mut j = 0; let mut new_top = 0; @@ -242,10 +253,10 @@ fn stack_check_ptr(pointer_value: *const *mut U) { if pointer_object(pointer) == pointer_object(MONITORED) && pointer_offset(MONITORED) < std::mem::size_of::() { - if Permission::grants(Access::Read, perm) { - self::monitors::stack_check(tag, Access::Read); - } else if Permission::grants(Access::Write, perm) { - self::monitors::stack_check(tag, Access::Write); + if Permission::grants(Access::READ, perm) { + self::monitors::stack_check(tag, Access::READ); + } else if Permission::grants(Access::WRITE, perm) { + self::monitors::stack_check(tag, Access::WRITE); } } } From 623278b182dcc6063a325da8d066314868732f6f Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 29 Aug 2024 12:21:56 -0400 Subject: [PATCH 60/72] Address performance regression by using assoc. constants --- library/kani/src/aliasing.rs | 46 ++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index f5ce12018220..a5f4cffb82b6 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -99,29 +99,29 @@ impl Access { /// To ensure that 8 bit, instead of larger, /// repreesentations are used in cbmc, this /// is encoded using associated constants. -#[repr(u8)] -#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)] -enum Permission { - /// Read/write reference with unique ownership - Unique, - /// Pointer with read/write access - SharedRW, - /// Pointer with read access - SharedRO, - /// Disabled pointer - Disabled, +type PermissionByte = u8; +struct Permission; +impl Permission { + /// Unique ownership of a memory location + const UNIQUE: u8 = 0; + /// Raw pointer read/write permission + const SHAREDRW: u8 = 1; + /// Raw pointer read permission + const SHAREDRO: u8 = 2; + /// Disabled -- no accesses allowed + const DISABLED: u8 = 3; } impl Permission { /// Returns whether the access bit is granted by the permission /// byte - fn grants(access: AccessBit, perm: Permission) -> bool { - perm != Permission::Disabled && (access != Access::WRITE || perm != Permission::SharedRO) + fn grants(access: AccessBit, perm: PermissionByte) -> bool { + perm != Permission::DISABLED && (access != Access::WRITE || perm != Permission::SHAREDRO) } } /// Associate every pointer object with a permission -static mut PERMS: ShadowMem = ShadowMem::new(Permission::SharedRO); +static mut PERMS: ShadowMem = ShadowMem::new(Permission::SHAREDRO); /// State of the borrows stack monitor for a byte pub(super) mod monitors { @@ -148,7 +148,7 @@ pub(super) mod monitors { /// The tags of the pointer objects borrowing the byte static mut STACK_TAGS: [PointerTag; STACK_DEPTH] = [0; STACK_DEPTH]; /// The permissions of the pointer objects borrowing the byte - static mut STACK_PERMS: [Permission; STACK_DEPTH] = [Permission::Unique; STACK_DEPTH]; + static mut STACK_PERMS: [PermissionByte; STACK_DEPTH] = [Permission::UNIQUE; STACK_DEPTH]; /// The "top" of the stack static mut STACK_TOP: usize = 0; @@ -166,14 +166,14 @@ pub(super) mod monitors { crate::assume(offset < std::mem::size_of::()); MONITORED = pointer.byte_add(offset) as *const u8; STACK_TAGS[STACK_TOP] = tag; - STACK_PERMS[STACK_TOP] = Permission::Unique; + STACK_PERMS[STACK_TOP] = Permission::UNIQUE; STACK_TOP += 1; } } } /// Push a tag with a permission perm at pointer - pub(super) fn push(tag: u8, perm: Permission, pointer: *const U) { + pub(super) fn push(tag: u8, perm: PermissionByte, pointer: *const U) { // Decide whether to initialize the stacks // for location:location+size_of(U). // Offset has already been picked earlier. @@ -219,7 +219,7 @@ pub(super) mod monitors { } /// Push the permissions at the given location -fn push(tag: u8, perm: Permission, address: *const U) { +fn push(tag: u8, perm: PermissionByte, address: *const U) { self::monitors::push(tag, perm, address) } @@ -238,7 +238,7 @@ fn initialize_local(pointer: *const U) { unsafe { let tag = NEXT_TAG; TAGS.set(pointer, tag); - PERMS.set(pointer, Permission::Unique); + PERMS.set(pointer, Permission::UNIQUE); NEXT_TAG += 1; monitors::track_local(tag, pointer); } @@ -276,8 +276,8 @@ fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); - PERMS.set(pointer_to_created, Permission::SharedRW); - push(NEXT_TAG, Permission::SharedRW, pointer_to_val); + PERMS.set(pointer_to_created, Permission::SHAREDRW); + push(NEXT_TAG, Permission::SHAREDRW, pointer_to_val); NEXT_TAG += 1; } } @@ -291,7 +291,7 @@ fn new_mut_raw_from_ref(pointer_to_created: *const *mut T, pointer_to_ref: *c unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); - push(NEXT_TAG, Permission::SharedRW, *pointer_to_ref); + push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); NEXT_TAG += 1; } } @@ -305,7 +305,7 @@ fn new_mut_ref_from_raw(pointer_to_created: *const &mut T, pointer_to_ref: *c unsafe { // Then associate the lvalue and push it TAGS.set(pointer_to_created, NEXT_TAG); - push(NEXT_TAG, Permission::SharedRW, *pointer_to_ref); + push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); NEXT_TAG += 1; } } From 00ef0e91fab5b983a965300b6f05e9aaaa73f807 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 29 Aug 2024 12:46:25 -0400 Subject: [PATCH 61/72] Run kani-fmt --- .../src/kani_middle/transform/body.rs | 6 +- .../check_aliasing/instrumentation.rs | 75 ++++++++++++------- library/kani/src/aliasing.rs | 1 - 3 files changed, 50 insertions(+), 32 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index 348b3e918d09..6b77e0d2106b 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -227,11 +227,7 @@ impl MutableBody { let span = source.span(&self.blocks); match check_type { CheckType::Assert(assert_fn) => { - let local = self.new_local( - assert_fn.ty(), - span, - Mutability::Not, - ); + let local = self.new_local(assert_fn.ty(), span, Mutability::Not); self.insert_check_with_local(local, source, position, value, msg); } CheckType::Panic | CheckType::NoCore => { diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index efebb5ab05cb..6f447cb898e8 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -10,7 +10,8 @@ use crate::kani_middle::transform::body::InsertPosition; use rustc_middle::ty::TyCtxt; use stable_mir::mir::{ - BasicBlock, Body, ConstOperand, Local, MirVisitor, Mutability, Operand, Place, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, UnwindAction + BasicBlock, Body, ConstOperand, Local, MirVisitor, Mutability, Operand, Place, Rvalue, + Statement, StatementKind, Terminator, TerminatorKind, UnwindAction, }; use stable_mir::ty::{GenericArgKind, MirConst, Span, Ty}; use std::collections::HashMap; @@ -122,7 +123,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { callee: Signature, args: Vec, dest: Local, - source: &mut SourceInstruction) -> Result<()> { + source: &mut SourceInstruction, + ) -> Result<()> { let terminator = self.call(callee, args, dest, source)?; let bb = BasicBlock { statements: vec![], terminator }; self.body.insert_bb(bb, source, InsertPosition::Before); @@ -197,37 +199,55 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { fn predecessor(&self, source: &SourceInstruction) -> SourceInstruction { match source { // One below 0, 0 is 0, 0 - SourceInstruction::Statement { idx: 0, bb: 0 } => SourceInstruction::Statement { idx: 0, bb: 0 }, + SourceInstruction::Statement { idx: 0, bb: 0 } => { + SourceInstruction::Statement { idx: 0, bb: 0 } + } // One below the first statement of a block is the // previous terminator - SourceInstruction::Statement { idx: 0, bb } => SourceInstruction::Terminator { bb: *bb - 1 }, + SourceInstruction::Statement { idx: 0, bb } => { + SourceInstruction::Terminator { bb: *bb - 1 } + } // Otherwise one below the statement of a block is the // previous statement - SourceInstruction::Statement { idx, bb } => SourceInstruction::Statement { idx: idx - 1, bb: *bb }, + SourceInstruction::Statement { idx, bb } => { + SourceInstruction::Statement { idx: idx - 1, bb: *bb } + } // One below the terminator of the first block with no statements // is 0, 0 - SourceInstruction::Terminator { bb: 0 } if self.body.blocks()[0].statements.is_empty() => SourceInstruction::Statement { idx: 0, bb: 0 }, + SourceInstruction::Terminator { bb: 0 } + if self.body.blocks()[0].statements.is_empty() => + { + SourceInstruction::Statement { idx: 0, bb: 0 } + } // Otherwise one below the terminator of a block is the previous // terminator if there are no statements in the block, - SourceInstruction::Terminator { bb } if self.body.blocks()[*bb].statements.is_empty() => SourceInstruction::Terminator { bb: *bb - 1 }, + SourceInstruction::Terminator { bb } + if self.body.blocks()[*bb].statements.is_empty() => + { + SourceInstruction::Terminator { bb: *bb - 1 } + } // Or the last statement of the current block. - SourceInstruction::Terminator { bb } => SourceInstruction::Statement { idx: self.body.blocks()[*bb].statements.len() - 1, bb: *bb } + SourceInstruction::Terminator { bb } => SourceInstruction::Statement { + idx: self.body.blocks()[*bb].statements.len() - 1, + bb: *bb, + }, } } /// Instrument with stack violated / not violated - pub fn instrument_stack_check(&mut self, source: &mut SourceInstruction, check_source: &SourceInstruction) -> Result<()> { + pub fn instrument_stack_check( + &mut self, + source: &mut SourceInstruction, + check_source: &SourceInstruction, + ) -> Result<()> { let less = self.predecessor(check_source); - let msg = format!("Stacked borrows state invalidated at {:?}", less.span(self.body.blocks()).get_lines()); + let msg = format!( + "Stacked borrows state invalidated at {:?}", + less.span(self.body.blocks()).get_lines() + ); self.instrument_call(Signature::new("KaniStackValid", &[]), vec![], self.valid, source)?; let assert = self.register_fn(Signature::new("KaniAssert", &[]))?; - self.body.insert_check_with_local( - assert, - source, - InsertPosition::Before, - self.valid, - &msg, - ); + self.body.insert_check_with_local(assert, source, InsertPosition::Before, self.valid, &msg); Ok(()) } @@ -322,27 +342,30 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// Instrument the action given in "action" with the appropriate /// update to the stacked borrows state. - fn instrument_action(&mut self, source: &mut SourceInstruction, check_source: &SourceInstruction, action: Action) -> Result<()> { + fn instrument_action( + &mut self, + source: &mut SourceInstruction, + check_source: &SourceInstruction, + action: Action, + ) -> Result<()> { match action { - Action::StackCheck => { - self.instrument_stack_check(source, check_source) - }, + Action::StackCheck => self.instrument_stack_check(source, check_source), Action::NewStackReference { lvalue, rvalue } => { eprintln!("instrumenting stack ref"); self.instrument_new_stack_reference(source, lvalue, rvalue) - }, + } Action::StackUpdateReference { place, ty } => { self.instrument_stack_update_ref(source, place, ty) - }, + } Action::NewMutRefFromRaw { lvalue, rvalue, ty } => { self.instrument_new_mut_ref_from_raw(source, lvalue, rvalue, ty) - }, + } Action::StackUpdatePointer { place, ty } => { self.instrument_stack_update_ptr(source, place, ty) - }, + } Action::NewMutRawFromRef { lvalue, rvalue, ty } => { self.instrument_new_mut_raw_from_ref(source, lvalue, rvalue, ty) - }, + } } } diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index a5f4cffb82b6..51cfef4aeec3 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -94,7 +94,6 @@ impl Access { const WRITE: AccessBit = true; } - /// Type of permission. /// To ensure that 8 bit, instead of larger, /// repreesentations are used in cbmc, this From a4bb9be172bb051d461c30f1f3c2a94d60629c0f Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 29 Aug 2024 12:58:30 -0400 Subject: [PATCH 62/72] Remove local variable that is only an alias --- .../kani_middle/transform/check_aliasing/instrumentation.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 6f447cb898e8..14bd6852acd9 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -86,12 +86,11 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// Register the function described by the diagnostic /// and generic arguments in "Signature". fn register_fn(&mut self, callee: Signature) -> Result { - let span = self.span; let instance = self.cache.register(&self.tcx, callee)?; let func_local = self .fn_pointers .entry(*instance) - .or_insert_with(|| self.body.new_local(instance.ty(), span, Mutability::Not)); + .or_insert_with(|| self.body.new_local(instance.ty(), self.span, Mutability::Not)); Ok(*func_local) } From 3a2d693d26eb636c09da582ab2400b2661458604 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 29 Aug 2024 13:00:26 -0400 Subject: [PATCH 63/72] Remove unhelpful doc comment --- library/kani/src/aliasing.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 51cfef4aeec3..7c74297c5585 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -33,8 +33,7 @@ //! // `addr_of!(x)` through `addr_of!(x) + 4` yielding //! // the stacks [(TAG_0, Permission::UNIQUE), (TAG_1, Permission::UNIQUE)] //! let y = &mut x; -//! // Make the pointer object `y as *mut i32`. -//! // associate `addr_of!(z)` and push the stacks as +//! // Associate `addr_of!(z)` and push the stacks as //! // above with the tag (2, Permission::SHAREDRW), //! // corresponding to a raw pointer, yielding the stacks //! // [(TAG_0, Permission::UNIQUE), (TAG_1, Permission::UNIQUE), From 443fb61a0a0e82d4fed83e82f58e0d27fe19407e Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Thu, 29 Aug 2024 13:03:09 -0400 Subject: [PATCH 64/72] Fix tag --> PointerTag --- library/kani/src/aliasing.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 7c74297c5585..f744b5abf6f0 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -154,7 +154,7 @@ pub(super) mod monitors { /// Initialize local when track local is true, picking a monitor, /// and setting its object and offset to within pointer. - pub(super) unsafe fn track_local(tag: u8, pointer: *const U) { + pub(super) unsafe fn track_local(tag: PointerTag, pointer: *const U) { // Decide whether to initialize the stacks // for location:location+size_of(U). unsafe { @@ -171,7 +171,7 @@ pub(super) mod monitors { } /// Push a tag with a permission perm at pointer - pub(super) fn push(tag: u8, perm: PermissionByte, pointer: *const U) { + pub(super) fn push(tag: PointerTag, perm: PermissionByte, pointer: *const U) { // Decide whether to initialize the stacks // for location:location+size_of(U). // Offset has already been picked earlier. @@ -190,7 +190,7 @@ pub(super) mod monitors { /// Run a stack check on the monitored byte for the given /// tag and the given access permission. - pub(super) fn stack_check(tag: u8, access: AccessBit) { + pub(super) fn stack_check(tag: PointerTag, access: AccessBit) { unsafe { use self::*; if STATE == MonitorState::ON { @@ -217,7 +217,7 @@ pub(super) mod monitors { } /// Push the permissions at the given location -fn push(tag: u8, perm: PermissionByte, address: *const U) { +fn push(tag: PointerTag, perm: PermissionByte, address: *const U) { self::monitors::push(tag, perm, address) } From 23dad3b320766a2d7acffd5fa180c67cf69db026 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 30 Aug 2024 13:00:22 -0400 Subject: [PATCH 65/72] Cache FnDef instead of Instance; document test cases --- kani-compiler/src/kani_middle/mod.rs | 80 +++++++++++++++++++ kani-compiler/src/kani_middle/reachability.rs | 2 +- .../src/kani_middle/transform/body.rs | 2 +- .../check_aliasing/function_cache.rs | 50 ++---------- .../check_aliasing/instrumentation.rs | 50 +++++++----- .../transform/check_aliasing/mod.rs | 57 +++++-------- tests/expected/aliasing/box.rs.fixme | 16 ++-- tests/expected/aliasing/control_flow.expected | 2 +- tests/expected/aliasing/control_flow.rs | 15 +++- .../aliasing/duplicate_write.expected | 3 +- tests/expected/aliasing/duplicate_write.rs | 10 ++- .../duplicate_write_compressed.expected | 2 +- .../aliasing/duplicate_write_compressed.rs | 4 + 13 files changed, 183 insertions(+), 110 deletions(-) diff --git a/kani-compiler/src/kani_middle/mod.rs b/kani-compiler/src/kani_middle/mod.rs index a5d077d9c16e..5dcc2280121f 100644 --- a/kani-compiler/src/kani_middle/mod.rs +++ b/kani-compiler/src/kani_middle/mod.rs @@ -232,3 +232,83 @@ fn find_fn_def(tcx: TyCtxt, diagnostic: &str) -> Option { }; Some(def) } + +/// NamedFnDef encapsulates the diagnostic along with the fn def. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct NamedFnDef { + /// The diagnostic with which the function definition + /// is found + diagnostic: String, + /// The "value", the resolved function instance itself + def: FnDef, +} + +impl NamedFnDef { + /// Create a new cacheable instance with the given signature and + /// instance + pub fn new(diagnostic: String, def: FnDef) -> NamedFnDef { + NamedFnDef { diagnostic, def } + } +} + +trait Cache +where + T: PartialEq, +{ + /// Generate a mutable reference to the cache's first item, + /// or if none exists try generating a new one with `f` + /// and return it. When `f` fails, return an error + /// `vec`'s first item that + /// passes the predicate `p`, or if none exists try generating + /// a new one with `f` and return it. + /// When `f` fails, return an error. + fn try_get_or_insert(&mut self, p: P, f: F) -> Result<&mut T, E> + where + F: FnOnce() -> Result, + P: Fn(&T) -> bool; +} + +impl Cache for Vec +where + T: PartialEq, +{ + fn try_get_or_insert(&mut self, p: P, f: F) -> Result<&mut T, E> + where + F: FnOnce() -> Result, + P: Fn(&T) -> bool, + { + if let Some(i) = self.iter().position(p) { + Ok(&mut self[i]) + } else { + self.push(f()?); + Ok(self.last_mut().unwrap()) + } + } +} + +/// Caches function instances for later lookups. +#[derive(Default, Debug)] +pub struct FnDefCache { + /// The cache + cache: Vec, +} + +impl FnDefCache { + pub fn register( + &mut self, + ctx: &TyCtxt, + diagnostic: String, + ) -> Result<&FnDef, stable_mir::Error> { + let test_diagnostic = diagnostic.clone(); + self.cache + .try_get_or_insert( + |item: &NamedFnDef| item.diagnostic == test_diagnostic, + || { + let fndef = find_fn_def(*ctx, diagnostic.as_str()) + .ok_or(stable_mir::Error::new(format!("Not found: {}", &diagnostic)))?; + Ok(NamedFnDef::new(diagnostic, fndef)) + }, + ) + .map(|entry| &entry.def) + } +} diff --git a/kani-compiler/src/kani_middle/reachability.rs b/kani-compiler/src/kani_middle/reachability.rs index 59f779d1c7d5..ca60b4684e08 100644 --- a/kani-compiler/src/kani_middle/reachability.rs +++ b/kani-compiler/src/kani_middle/reachability.rs @@ -651,7 +651,7 @@ impl CallGraph { /// Get all items adjacent to the current item in the /// call graph. - pub fn adjacencies(&self, node: MonoItem) -> Vec<&MonoItem> { + pub fn successors(&self, node: MonoItem) -> Vec<&MonoItem> { match self.edges.get(&Node(node)) { None => vec![], Some(list) => list.iter().map(|collected| &collected.0.item).collect(), diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index 6b77e0d2106b..ebdd7e06bbe1 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -165,7 +165,7 @@ impl MutableBody { self.insert_stmt(stmt, source, position); } - /// Add a new assert to the basic block indicated by the given index, using + /// Add a new assert to the basic block by the source instruction, using /// "local" as the checking function. /// /// The new assertion will have the same span as the source instruction, and the basic block diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs index 9717376e278b..1e5cd45aea5f 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/function_cache.rs @@ -2,60 +2,22 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! This module contains a cache of resolved generic functions -use super::{MirError, MirInstance}; use crate::kani_middle::find_fn_def; use rustc_middle::ty::TyCtxt; -use stable_mir::ty::{GenericArgKind as GenericArg, GenericArgs}; -/// FunctionSignature encapsulates the data -/// for rust functions with generic arguments -/// to ensure that it can be cached. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Signature { - /// The diagnostic string associated with the function - diagnostic: String, - /// The generic arguments applied - args: Vec, -} - -impl Signature { - /// Create a new signature from the name and args - pub fn new(name: &str, args: &[GenericArg]) -> Signature { - Signature { diagnostic: name.to_string(), args: args.to_vec() } - } -} +type Result = std::result::Result; -/// FunctionInstance encapsulates the -/// data for a resolved rust function with -/// generic arguments to ensure that it can be cached. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Instance { - /// The "key" with which the function instance - /// is created, and with which the function instance - /// can be looked up - signature: Signature, - /// The "value", the resolved function instance itself - instance: MirInstance, -} - -impl Instance { - /// Create a new cacheable instance with the given signature and - /// instance - pub fn new(signature: Signature, instance: MirInstance) -> Instance { - Instance { signature, instance } - } -} /// Caches function instances for later lookups. #[derive(Default, Debug)] pub struct Cache { /// The cache - cache: Vec, + cache: Vec, } -fn try_get_or_insert(vec: &mut Vec, p: P, f: F) -> Result<&mut T, E> +fn try_get_or_insert(vec: &mut Vec, p: P, f: F) -> Result<&mut T> where - F: FnOnce() -> Result, + F: FnOnce() -> Result, P: Fn(&T) -> bool, T: PartialEq, { @@ -74,7 +36,7 @@ impl Cache { &mut self, ctx: &TyCtxt, signature: Signature, - ) -> Result<&MirInstance, MirError> { + ) -> Result<&> { let test_sig = signature.clone(); let Cache { cache } = self; try_get_or_insert( @@ -84,7 +46,7 @@ impl Cache { let fndef = find_fn_def(*ctx, &signature.diagnostic) .ok_or(MirError::new(format!("Not found: {}", &signature.diagnostic)))?; let instance = MirInstance::resolve(fndef, &GenericArgs(signature.args.clone()))?; - Ok(Instance::new(signature, instance)) + Ok(NamedFnDef::new(signature, instance)) }, ) .map(|entry| &entry.instance) diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs index 14bd6852acd9..c1d412c3db54 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/instrumentation.rs @@ -7,17 +7,20 @@ //! are instrumented, and that no code that is added by the instrumentation //! pass itself is instrumented or analyzed. +use super::Cache; use crate::kani_middle::transform::body::InsertPosition; use rustc_middle::ty::TyCtxt; +use stable_mir::mir::mono::Instance; use stable_mir::mir::{ BasicBlock, Body, ConstOperand, Local, MirVisitor, Mutability, Operand, Place, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, UnwindAction, }; -use stable_mir::ty::{GenericArgKind, MirConst, Span, Ty}; +use stable_mir::ty::{GenericArgKind, GenericArgs, MirConst, Span, Ty}; use std::collections::HashMap; +use tracing::debug; use super::super::body::{MutableBody, SourceInstruction}; -use super::{Action, Cache, CollectActions, MirError, MirInstance, Signature}; +use super::{Action, CollectActions, MirError}; type Result = std::result::Result; @@ -41,7 +44,7 @@ pub struct InstrumentationData<'tcx, 'cache> { valid: Local, /// A map associating resolved generic functions with /// locals in the body that can be used to call them - fn_pointers: HashMap, + fn_pointers: HashMap, /// The span of the body span: Span, /// The body being instrumented @@ -85,11 +88,12 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// Register the function described by the diagnostic /// and generic arguments in "Signature". - fn register_fn(&mut self, callee: Signature) -> Result { - let instance = self.cache.register(&self.tcx, callee)?; + fn register_fn(&mut self, diagnostic: &str, args: &[GenericArgKind]) -> Result { + let def = self.cache.register(&self.tcx, diagnostic.to_owned())?; + let instance = Instance::resolve(*def, &GenericArgs(args.to_vec()))?; let func_local = self .fn_pointers - .entry(*instance) + .entry(instance) .or_insert_with(|| self.body.new_local(instance.ty(), self.span, Mutability::Not)); Ok(*func_local) } @@ -100,12 +104,13 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// function name is cached. pub fn call( &mut self, - callee: Signature, + diagnostic: &str, + tys: &[GenericArgKind], args: Vec, dest: Local, source: &SourceInstruction, ) -> Result { - let fn_local = self.register_fn(callee)?; + let fn_local = self.register_fn(diagnostic, tys)?; let func = Operand::Copy(Place::from(fn_local)); let args = args.into_iter().map(|local| Operand::Copy(Place::from(local))).collect(); let destination = Place::from(dest); @@ -119,12 +124,13 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { /// Instrument the call generated by "call" for these parameters. pub fn instrument_call( &mut self, - callee: Signature, + callee: &str, + tys: &[GenericArgKind], args: Vec, dest: Local, source: &mut SourceInstruction, ) -> Result<()> { - let terminator = self.call(callee, args, dest, source)?; + let terminator = self.call(callee, tys, args, dest, source)?; let bb = BasicBlock { statements: vec![], terminator }; self.body.insert_bb(bb, source, InsertPosition::Before); Ok(()) @@ -160,7 +166,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let statement = self.assign_pointer(local_ptr, local, &self.first_instruction()); let statements = vec![statement]; let terminator = self.call( - Signature::new("KaniInitializeLocal", &[GenericArgKind::Type(ty)]), + "KaniInitializeLocal", + &[GenericArgKind::Type(ty)], vec![local_ptr], self.unit, &self.first_instruction(), @@ -185,7 +192,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let lvalue_ref = self.meta_stack.get(&lvalue).unwrap(); let rvalue_ref = self.meta_stack.get(&rvalue).unwrap(); self.instrument_call( - Signature::new("KaniNewMutRefFromValue", &[GenericArgKind::Type(ty)]), + "KaniNewMutRefFromValue", + &[GenericArgKind::Type(ty)], vec![*lvalue_ref, *rvalue_ref], self.unit, source, @@ -244,8 +252,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { "Stacked borrows state invalidated at {:?}", less.span(self.body.blocks()).get_lines() ); - self.instrument_call(Signature::new("KaniStackValid", &[]), vec![], self.valid, source)?; - let assert = self.register_fn(Signature::new("KaniAssert", &[]))?; + self.instrument_call("KaniStackValid", &[], vec![], self.valid, source)?; + let assert = self.register_fn("KaniAssert", &[])?; self.body.insert_check_with_local(assert, source, InsertPosition::Before, self.valid, &msg); Ok(()) } @@ -261,7 +269,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { // Initialize the constants let place_ref = self.meta_stack.get(&place).unwrap(); self.instrument_call( - Signature::new("KaniStackCheckRef", &[GenericArgKind::Type(ty)]), + "KaniStackCheckRef", + &[GenericArgKind::Type(ty)], vec![*place_ref], self.unit, source, @@ -280,7 +289,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { // Initialize the constants let place_ref = self.meta_stack.get(&place).unwrap(); self.instrument_call( - Signature::new("KaniStackCheckPtr", &[GenericArgKind::Type(ty)]), + "KaniStackCheckPtr", + &[GenericArgKind::Type(ty)], vec![*place_ref], self.unit, source, @@ -301,7 +311,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let created_ref = self.meta_stack.get(&created).unwrap(); let reference_ref = self.meta_stack.get(&raw).unwrap(); self.instrument_call( - Signature::new("KaniNewMutRefFromRaw", &[GenericArgKind::Type(ty)]), + "KaniNewMutRefFromRaw", + &[GenericArgKind::Type(ty)], vec![*created_ref, *reference_ref], self.unit, source, @@ -322,7 +333,8 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let created_ref = self.meta_stack.get(&created).unwrap(); let reference_ref = self.meta_stack.get(&reference).unwrap(); self.instrument_call( - Signature::new("KaniNewMutRawFromRef", &[GenericArgKind::Type(ty)]), + "KaniNewMutRawFromRef", + &[GenericArgKind::Type(ty)], vec![*created_ref, *reference_ref], self.unit, source, @@ -404,7 +416,7 @@ impl<'tcx, 'cache> InstrumentationData<'tcx, 'cache> { let local_ptr = self.body.new_local(ptr_ty, self.span, Mutability::Not); self.meta_stack.insert(local, local_ptr); } - eprintln!("instrumenting instructions"); + debug!("instrumenting instructions"); self.instrument_instructions()?; self.instrument_locals()?; self.instrument_initialize_stack_check(); diff --git a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs index 6a83e05a766e..25e92a0799ce 100644 --- a/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs +++ b/kani-compiler/src/kani_middle/transform/check_aliasing/mod.rs @@ -4,22 +4,20 @@ //! Implement a pass that instruments code with assertions //! that will fail when the aliasing model is violated. -use stable_mir::mir::mono::MonoItem; +use stable_mir::mir::mono::{Instance, MonoItem}; use stable_mir::CrateDef; // Reimport components of mir that conflict with // parts of the sub-pass's API. -pub use stable_mir::mir::mono::Instance as MirInstance; pub use stable_mir::Error as MirError; mod visitor; use visitor::*; -mod function_cache; -use function_cache::*; mod instrumentation; use instrumentation::*; use crate::args::ExtraChecks; use crate::kani_middle::transform::{TransformPass, TransformationResult, TransformationType}; +use crate::kani_middle::FnDefCache as Cache; use crate::kani_queries::QueryDb; use rustc_middle::ty::TyCtxt; use stable_mir::mir::Body; @@ -32,34 +30,22 @@ use super::GlobalPass; /// Instrument the code with checks for aliasing model /// violations. /// Cache functions in-between applications of the pass. -/// Architecturally, this is implemented as the composition -/// of several sub passes on functions: -/// First, information is collected on the variables in the -/// function body and on the arguments to the function. -/// (LocalCollectionPassState) -/// Then, enough information from the body -/// is collected for instrumentation. -/// -/// The body is transformed into a CachedBodyMutator to -/// be used in the BodyMutationPass, which combines the -/// body with (initially empty) storage for -/// instrumented locals and instrumented instructions, -/// and which caches function items referring to -/// resolved function instances. -/// -/// The prologue of the function is then instrumented with data for every -/// stack allocation referenced by a local (instrument_locals). -/// Pointers to these locals are kept in InstrumentationData, -/// which then checks all instructions that modify memory for -/// aliasing violations (instrument_instructions). -/// -/// Finally, a new body is made from the code + the instrumented -/// code. +/// This is performed by taking the incoming body, +/// using a visitor to find instructions relevant to +/// the instrumentation, then iterating over these +/// instructions backwards, inserting code prior to their +/// execution. #[derive(Debug)] struct AliasingPass<'cache> { cache: &'cache mut Cache, } +/// Returns whether ExtraChecks::Aliasing is included +/// in the command line arguments +fn db_includes_aliasing(query_db: &QueryDb) -> bool { + query_db.args().ub_check.contains(&ExtraChecks::Aliasing) +} + impl<'cache> TransformPass for AliasingPass<'cache> { fn transformation_type() -> TransformationType where @@ -68,16 +54,15 @@ impl<'cache> TransformPass for AliasingPass<'cache> { TransformationType::Instrumentation } - fn is_enabled(&self, _query_db: &QueryDb) -> bool + fn is_enabled(&self, query_db: &QueryDb) -> bool where Self: Sized, { - true + db_includes_aliasing(query_db) } - fn transform(&mut self, tcx: TyCtxt, body: Body, instance: MirInstance) -> (bool, Body) { + fn transform(&mut self, tcx: TyCtxt, body: Body, instance: Instance) -> (bool, Body) { trace!(function=?instance.name(), "transform: aliasing pass"); - // let body = CachedBodyMutator::from(body); let instrumentation_data = InstrumentationData::new(tcx, &mut self.cache, body); let out = instrumentation_data.finalize().unwrap().into(); (true, out) @@ -87,6 +72,9 @@ impl<'cache> TransformPass for AliasingPass<'cache> { /// The global aliasing pass keeps a cache of resolved generic functions, /// and ensures that only the functions that are called /// from the proof harness itself are instrumented. +/// To avoid instrumenting functions that were not present in the source, +/// but added in the instrumented code, this first collects the functions +/// present in the source, then instruments them. #[derive(Debug, Default)] pub struct GlobalAliasingPass { cache: Cache, @@ -103,8 +91,7 @@ impl GlobalPass for GlobalAliasingPass { where Self: Sized, { - let args = query_db.args(); - args.ub_check.contains(&ExtraChecks::Aliasing) + db_includes_aliasing(query_db) } fn transform( @@ -112,7 +99,7 @@ impl GlobalPass for GlobalAliasingPass { tcx: TyCtxt, call_graph: &crate::kani_middle::reachability::CallGraph, _starting_items: &[stable_mir::mir::mono::MonoItem], - instances: Vec, + instances: Vec, transformer: &mut super::BodyTransformation, ) { let mut found = HashSet::new(); @@ -133,7 +120,7 @@ impl GlobalPass for GlobalAliasingPass { let mut pass = AliasingPass { cache: &mut self.cache }; let (_, body) = pass.transform(tcx, transformer.body(tcx, *instance), *instance); transformer.cache.insert(*instance, TransformationResult::Modified(body)); - for node in call_graph.adjacencies(MonoItem::Fn(*instance).clone()) { + for node in call_graph.successors(MonoItem::Fn(*instance).clone()) { if let MonoItem::Fn(adjacent) = node { if found.insert(adjacent) { queue.push_back(adjacent); diff --git a/tests/expected/aliasing/box.rs.fixme b/tests/expected/aliasing/box.rs.fixme index 798f25ee50a9..2e1e72171bde 100644 --- a/tests/expected/aliasing/box.rs.fixme +++ b/tests/expected/aliasing/box.rs.fixme @@ -1,14 +1,20 @@ // Copyright Kani Contributors // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zaliasing +// In the following code, the aliasing pattern +// is disallowed by stacked borrows. +// That is, since raw_1 borrows raw_2, +// write raw_2, followed by read raw_1, followed +// by write raw_2, causes an error: +// the scope of the borrow ends when raw_1 is written. #[kani::proof] -fn main() { +fn write_out_of_order_to_box() { let x = Box::new(10); let ref_x = Box::into_raw(x); let raw_1 = ref_x as *mut i32; - let raw_2 = ref_x as *const i32; - let _write = unsafe { *raw_1 = 100 }; - let _read = unsafe { *raw_2 }; - let _write = unsafe { *raw_1 = 110 }; + let raw_2 = ref_x as *mut i32; + let _write = unsafe { *raw_2 = 100 }; + let _read = unsafe { *raw_1 }; + let _write = unsafe { *raw_2 = 110 }; } diff --git a/tests/expected/aliasing/control_flow.expected b/tests/expected/aliasing/control_flow.expected index 17ee23f65ea5..0f13dc515e6f 100644 --- a/tests/expected/aliasing/control_flow.expected +++ b/tests/expected/aliasing/control_flow.expected @@ -1 +1 @@ -Failed Checks: Stacked borrows state invalidated at LineInfo { start_line: 24, start_col: 17, end_line: 24, end_col: 36 } +Failed Checks: Stacked borrows state invalidated at LineInfo { start_line: 37, start_col: 17, end_line: 37, end_col: 36 } diff --git a/tests/expected/aliasing/control_flow.rs b/tests/expected/aliasing/control_flow.rs index 3b18b0f27cda..f9bc99340b3d 100644 --- a/tests/expected/aliasing/control_flow.rs +++ b/tests/expected/aliasing/control_flow.rs @@ -2,9 +2,22 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zghost-state -Zaliasing +// In the following code, +// ref_from_raw_1 and ref_from_raw_2 +// borrow from the memory location of local. +// In the second iteration of the loop, +// the write to ref_from_raw_2 should end the scope +// of ref_from_raw_1's borrow. +// When ref_from_raw_1 then is written, an aliasing +// error should be thrown. + +// This example is used as a litmus test to check +// that multiple basic blocks with non-linear +// control flow are instrumented properly + #[allow(unused)] #[kani::proof] -fn main() { +fn violation_within_control_flow() { let mut local: i32 = 10; let mut referent_1: i32 = 0; let mut referent_2: i32 = 0; diff --git a/tests/expected/aliasing/duplicate_write.expected b/tests/expected/aliasing/duplicate_write.expected index 35c16764bb35..63aeba869f3f 100644 --- a/tests/expected/aliasing/duplicate_write.expected +++ b/tests/expected/aliasing/duplicate_write.expected @@ -1 +1,2 @@ -Failed Checks: Stacked borrows state invalidated at LineInfo { start_line: 21, start_col: 9, end_line: 21, end_col: 28 } + +Failed Checks: Stacked borrows state invalidated at LineInfo { start_line: 29, start_col: 9, end_line: 29, end_col: 28 } diff --git a/tests/expected/aliasing/duplicate_write.rs b/tests/expected/aliasing/duplicate_write.rs index bf3a6d11b307..e85be58ba1d2 100644 --- a/tests/expected/aliasing/duplicate_write.rs +++ b/tests/expected/aliasing/duplicate_write.rs @@ -2,8 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zghost-state -Zaliasing +// In the following code, +// ref_from_raw_1 and ref_from_raw_2 both +// borrow the memory location of local. +// After ref_from_raw_2 is written, ref_from_raw_1's +// borrow ends. +// The subsequent write to ref_from_raw_1 will cause an aliasing +// error. + #[kani::proof] -fn main() { +fn use_after_borrow_ends() { let mut local: i32; let temp_ref: &mut i32; let raw_pointer: *mut i32; diff --git a/tests/expected/aliasing/duplicate_write_compressed.expected b/tests/expected/aliasing/duplicate_write_compressed.expected index d7c837715c8e..acbb8d1e361d 100644 --- a/tests/expected/aliasing/duplicate_write_compressed.expected +++ b/tests/expected/aliasing/duplicate_write_compressed.expected @@ -1,2 +1,2 @@ -Failed Checks: Stacked borrows state invalidated at LineInfo { start_line: 14, start_col: 9, end_line: 14, end_col: 28 } +Failed Checks: Stacked borrows state invalidated at LineInfo { start_line: 18, start_col: 9, end_line: 18, end_col: 28 } diff --git a/tests/expected/aliasing/duplicate_write_compressed.rs b/tests/expected/aliasing/duplicate_write_compressed.rs index 466d9f219ade..298accae8dcc 100644 --- a/tests/expected/aliasing/duplicate_write_compressed.rs +++ b/tests/expected/aliasing/duplicate_write_compressed.rs @@ -2,6 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // kani-flags: -Zghost-state -Zaliasing +// The following code is equivalent to duplicate_write, +// the only difference being that operations may be chained +// in one line. + #[kani::proof] fn main() { let mut local: i32 = 0; From a36a451b880bca7e1ea6217efac15418c034d642 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 30 Aug 2024 13:05:47 -0400 Subject: [PATCH 66/72] Update comment --- kani-compiler/src/kani_middle/mod.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/kani-compiler/src/kani_middle/mod.rs b/kani-compiler/src/kani_middle/mod.rs index 5dcc2280121f..00eeb98baebe 100644 --- a/kani-compiler/src/kani_middle/mod.rs +++ b/kani-compiler/src/kani_middle/mod.rs @@ -245,16 +245,13 @@ pub struct NamedFnDef { impl NamedFnDef { /// Create a new cacheable instance with the given signature and - /// instance + /// def pub fn new(diagnostic: String, def: FnDef) -> NamedFnDef { NamedFnDef { diagnostic, def } } } -trait Cache -where - T: PartialEq, -{ +trait Cache { /// Generate a mutable reference to the cache's first item, /// or if none exists try generating a new one with `f` /// and return it. When `f` fails, return an error From 16e4bdc1ffae41872314710f0bac1a27b1912024 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 30 Aug 2024 17:02:26 -0400 Subject: [PATCH 67/72] Separate the constants and the types. Use the underlying value size. --- library/kani/src/aliasing.rs | 293 ++++++++++++++++++++--------------- library/kani/src/lib.rs | 2 + 2 files changed, 172 insertions(+), 123 deletions(-) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index f744b5abf6f0..9458b716f99c 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -56,110 +56,133 @@ //! relations between the stacks (such as, for example, equality between //! the top two tags of two different stacks) are never needed. -use std::ptr::read; - -use monitors::MONITORED; - use crate::mem::{pointer_object, pointer_offset}; use crate::shadow::ShadowMem; -type PointerTag = u8; - -use super::*; -/// Associate every pointer object with a tag -static mut TAGS: ShadowMem = ShadowMem::new(0); -/// Next pointer id: the next pointer id in sequence -const INITIAL_TAG: PointerTag = 0; -static mut NEXT_TAG: PointerTag = INITIAL_TAG; - -/// Set to true whenever the stack has been -/// invalidated by a failed lookup. -static mut STACK_VALID: bool = true; - -#[rustc_diagnostic_item = "KaniStackValid"] -fn stack_valid() -> bool { - unsafe { STACK_VALID } +/// Bounds on the space usage of the analysis +mod limits { + pub(super) const STACK_DEPTH: usize = 15; } -/// Access bit. -/// Encoded as associated constants -/// instead of as an enum to ensure -/// that the representation uses -/// 1 bit. -type AccessBit = bool; -struct Access; -impl Access { - const READ: AccessBit = false; - const WRITE: AccessBit = true; -} +/// Types used in the analysis +mod types { + /// Pointer tag. + /// Up to 256 pointers are tracked; and so they are + /// given a symbolic name by the PointerTag type. + pub(super) type PointerTag = u8; -/// Type of permission. -/// To ensure that 8 bit, instead of larger, -/// repreesentations are used in cbmc, this -/// is encoded using associated constants. -type PermissionByte = u8; -struct Permission; -impl Permission { - /// Unique ownership of a memory location - const UNIQUE: u8 = 0; - /// Raw pointer read/write permission - const SHAREDRW: u8 = 1; - /// Raw pointer read permission - const SHAREDRO: u8 = 2; - /// Disabled -- no accesses allowed - const DISABLED: u8 = 3; -} + /// Access bit. + /// Encoded as associated constants + /// instead of as an enum to ensure + /// that the representation uses + /// 1 bit. + pub(super) type AccessBit = bool; + pub(super) struct Access; + impl Access { + pub(super) const READ: AccessBit = false; + pub(super) const WRITE: AccessBit = true; + } -impl Permission { - /// Returns whether the access bit is granted by the permission - /// byte - fn grants(access: AccessBit, perm: PermissionByte) -> bool { - perm != Permission::DISABLED && (access != Access::WRITE || perm != Permission::SHAREDRO) + /// Type of permission. + /// To ensure that 8 bit, instead of larger, + /// repreesentations are used in cbmc, this + /// is encoded using associated constants. + pub(super) type PermissionByte = u8; + pub(super) struct Permission; + impl Permission { + /// Unique ownership of a memory location + pub(super) const UNIQUE: u8 = 0; + /// Raw pointer read/write permission + pub(super) const SHAREDRW: u8 = 1; + /// Raw pointer read permission + pub(super) const SHAREDRO: u8 = 2; + /// Disabled -- no accesses allowed + pub(super) const DISABLED: u8 = 3; } -} -/// Associate every pointer object with a permission -static mut PERMS: ShadowMem = ShadowMem::new(Permission::SHAREDRO); + impl Permission { + /// Returns whether the access bit is granted by the permission + /// byte + pub(super) fn grants(access: AccessBit, perm: PermissionByte) -> bool { + perm != Permission::DISABLED + && (access != Access::WRITE || perm != Permission::SHAREDRO) + } + } -/// State of the borrows stack monitor for a byte -pub(super) mod monitors { /// Tracks whether the monitor is on or off. /// Encoded as associated constants instead /// of as an enum to ensure that the representation /// uses 1 bit. - type MonitorBit = bool; - struct MonitorState; + pub(super) type MonitorBit = bool; + pub(super) struct MonitorState; impl MonitorState { - const ON: MonitorBit = false; - const OFF: MonitorBit = true; + pub(super) const ON: MonitorBit = false; + pub(super) const OFF: MonitorBit = true; } +} - #[allow(unused)] - const STACK_DEPTH: usize = 15; +// The global state of the analysis. +mod global { + use super::limits::*; + use super::types::*; + use super::ShadowMem; + + pub(super) const INITIAL_TAG: PointerTag = 0; + + /// Associate every pointer object with a tag + pub(super) static mut TAGS: ShadowMem = ShadowMem::new(INITIAL_TAG); + + /// Associate every pointer object with a permission + pub(super) static mut PERMS: ShadowMem = ShadowMem::new(Permission::SHAREDRO); + + /// Next pointer id: the next pointer id in sequence + pub(super) static mut NEXT_TAG: PointerTag = INITIAL_TAG; + + /// Set to true whenever the stack has been + /// invalidated by a failed lookup. + pub(super) static mut STACK_VALID: bool = true; - /// Whether the monitor is on. Initially, the monitor is - /// "off", and it will remain so until an allocation is found - /// to track. - static mut STATE: MonitorBit = MonitorState::OFF; /// Object + offset being monitored - pub static mut MONITORED: *const u8 = std::ptr::null(); + pub(super) static mut MONITORED: *const u8 = std::ptr::null(); /// The tags of the pointer objects borrowing the byte - static mut STACK_TAGS: [PointerTag; STACK_DEPTH] = [0; STACK_DEPTH]; + pub(super) static mut STACK_TAGS: [PointerTag; STACK_DEPTH] = [INITIAL_TAG; STACK_DEPTH]; /// The permissions of the pointer objects borrowing the byte - static mut STACK_PERMS: [PermissionByte; STACK_DEPTH] = [Permission::UNIQUE; STACK_DEPTH]; + pub(super) static mut STACK_PERMS: [PermissionByte; STACK_DEPTH] = + [Permission::UNIQUE; STACK_DEPTH]; /// The "top" of the stack - static mut STACK_TOP: usize = 0; + pub(super) static mut STACK_TOP: usize = 0; +} + +#[rustc_diagnostic_item = "KaniStackValid"] +fn stack_valid() -> bool { + unsafe { global::STACK_VALID } +} + +/// Manipulation of the monitor of the stacked +/// borrows state +pub(super) mod monitor_transitions { + use super::global::*; + use super::limits::*; + use super::types::*; + use crate::mem::{pointer_object, pointer_offset}; + + fn demonic_nondet() -> bool { + crate::any() + } - use super::*; + #[allow(unused)] + pub(super) const STACK_DEPTH: usize = 15; /// Initialize local when track local is true, picking a monitor, /// and setting its object and offset to within pointer. - pub(super) unsafe fn track_local(tag: PointerTag, pointer: *const U) { + pub(super) unsafe fn track_local(tag: PointerTag, pointer: *const U) + where + U: Sized, + { // Decide whether to initialize the stacks // for location:location+size_of(U). unsafe { - if demonic_nondet() && STATE == MonitorState::OFF { - STATE = MonitorState::ON; + if demonic_nondet() { let offset: usize = kani::any(); crate::assume(offset < std::mem::size_of::()); MONITORED = pointer.byte_add(offset) as *const u8; @@ -171,14 +194,16 @@ pub(super) mod monitors { } /// Push a tag with a permission perm at pointer - pub(super) fn push(tag: PointerTag, perm: PermissionByte, pointer: *const U) { + pub(super) fn push(tag: PointerTag, perm: PermissionByte, pointer: *const U) + where + U: Sized, + { // Decide whether to initialize the stacks // for location:location+size_of(U). // Offset has already been picked earlier. unsafe { use self::*; - if STATE == MonitorState::ON - && pointer_object(MONITORED) == pointer_object(pointer) + if pointer_object(MONITORED) == pointer_object(pointer) && pointer_offset(MONITORED) <= std::mem::size_of::() { STACK_TAGS[STACK_TOP] = tag; @@ -193,32 +218,30 @@ pub(super) mod monitors { pub(super) fn stack_check(tag: PointerTag, access: AccessBit) { unsafe { use self::*; - if STATE == MonitorState::ON { - let mut found = false; - let mut j = 0; - let mut new_top = 0; - crate::assert(STACK_TOP < STACK_DEPTH, "Max # of nested borrows (15) exceeded"); - while j < STACK_DEPTH { - if j < STACK_TOP { - let id = STACK_TAGS[j]; - let kind = STACK_PERMS[j]; - if Permission::grants(access, kind) && id == tag { - new_top = j + 1; - found = true; - } + let mut found = false; + let mut j = 0; + let mut new_top = 0; + crate::assert(STACK_TOP < STACK_DEPTH, "Max # of nested borrows (15) exceeded"); + while j < STACK_DEPTH { + if j < STACK_TOP { + let id = STACK_TAGS[j]; + let kind = STACK_PERMS[j]; + if Permission::grants(access, kind) && id == tag { + new_top = j + 1; + found = true; } - j += 1; } - STACK_TOP = new_top; - STACK_VALID = STACK_VALID && found; + j += 1; } + STACK_TOP = new_top; + STACK_VALID = STACK_VALID && found; } } } /// Push the permissions at the given location -fn push(tag: PointerTag, perm: PermissionByte, address: *const U) { - self::monitors::push(tag, perm, address) +fn push(tag: types::PointerTag, perm: types::PermissionByte, address: *const U) { + self::monitor_transitions::push(tag, perm, address) } /// Initialize the local stored at reference if initialized is set to false, @@ -234,27 +257,55 @@ fn push(tag: PointerTag, perm: PermissionByte, address: *const U) { #[rustc_diagnostic_item = "KaniInitializeLocal"] fn initialize_local(pointer: *const U) { unsafe { - let tag = NEXT_TAG; - TAGS.set(pointer, tag); - PERMS.set(pointer, Permission::UNIQUE); - NEXT_TAG += 1; - monitors::track_local(tag, pointer); + let tag = global::NEXT_TAG; + global::TAGS.set(pointer, tag); + global::PERMS.set(pointer, types::Permission::UNIQUE); + global::NEXT_TAG += 1; + monitor_transitions::track_local(tag, pointer); } } +/// Stack check the object pointed by pointer_value. +/// +/// This is done by checking `crate::mem::pointer_object` and +/// `crate::mem::pointer_offset`, which for arrays: +/// ```rust +/// let mut x: [i32; 100] = [0; 100]; +/// let x_ptr: *const [i32; 100] = std::ptr::addr_of!(x); +/// let y = &mut x[10]; +/// let y_ptr = y as *mut i32; +/// crate::assert(crate::mem::pointer_object(x_ptr) == +/// crate::mem::pointer_object(y_ptr), "pointers ="); +/// ``` +/// and for fields: +/// ```rust +/// struct TwoElements { +/// x: i32, +/// y: i32, +/// } +/// let mut x: TwoElements = TwoElements { x: 0, y: 0 }; +/// let x_ptr: *const TwoElements = std::ptr::addr_of!(x); +/// let y = &mut x.x; +/// let y_ptr = y as *mut i32; +/// crate::assert(crate::mem::pointer_object(x_ptr) == +/// crate::mem::pointer_object(y_ptr), "pointers ="); +/// ``` +/// will succeed, given that offsets within the same allocation +/// are considered parts of the same pointer object by cbmc. #[rustc_diagnostic_item = "KaniStackCheckPtr"] fn stack_check_ptr(pointer_value: *const *mut U) { unsafe { - let tag = TAGS.get(pointer_value); - let perm = PERMS.get(pointer_value); + let tag = global::TAGS.get(pointer_value); + let perm = global::PERMS.get(pointer_value); let pointer = *pointer_value; - if pointer_object(pointer) == pointer_object(MONITORED) - && pointer_offset(MONITORED) < std::mem::size_of::() + let size = unsafe { std::mem::size_of_val_raw::(pointer) }; + if pointer_object(pointer) == pointer_object(global::MONITORED) + && pointer_offset(global::MONITORED) < size { - if Permission::grants(Access::READ, perm) { - self::monitors::stack_check(tag, Access::READ); - } else if Permission::grants(Access::WRITE, perm) { - self::monitors::stack_check(tag, Access::WRITE); + if types::Permission::grants(types::Access::READ, perm) { + self::monitor_transitions::stack_check(tag, types::Access::READ); + } else if types::Permission::grants(types::Access::WRITE, perm) { + self::monitor_transitions::stack_check(tag, types::Access::WRITE); } } } @@ -273,10 +324,10 @@ fn stack_check_ref(pointer_value: *const &mut U) { fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: *const T) { unsafe { // Then associate the lvalue and push it - TAGS.set(pointer_to_created, NEXT_TAG); - PERMS.set(pointer_to_created, Permission::SHAREDRW); - push(NEXT_TAG, Permission::SHAREDRW, pointer_to_val); - NEXT_TAG += 1; + global::TAGS.set(pointer_to_created, global::NEXT_TAG); + global::PERMS.set(pointer_to_created, types::Permission::SHAREDRW); + push(global::NEXT_TAG, types::Permission::SHAREDRW, pointer_to_val); + global::NEXT_TAG += 1; } } @@ -288,9 +339,9 @@ fn new_mut_ref_from_value(pointer_to_created: *const &mut T, pointer_to_val: fn new_mut_raw_from_ref(pointer_to_created: *const *mut T, pointer_to_ref: *const &mut T) { unsafe { // Then associate the lvalue and push it - TAGS.set(pointer_to_created, NEXT_TAG); - push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); - NEXT_TAG += 1; + global::TAGS.set(pointer_to_created, global::NEXT_TAG); + push(global::NEXT_TAG, types::Permission::SHAREDRW, *pointer_to_ref); + global::NEXT_TAG += 1; } } @@ -302,12 +353,8 @@ fn new_mut_raw_from_ref(pointer_to_created: *const *mut T, pointer_to_ref: *c fn new_mut_ref_from_raw(pointer_to_created: *const &mut T, pointer_to_ref: *const *mut T) { unsafe { // Then associate the lvalue and push it - TAGS.set(pointer_to_created, NEXT_TAG); - push(NEXT_TAG, Permission::SHAREDRW, *pointer_to_ref); - NEXT_TAG += 1; + global::TAGS.set(pointer_to_created, global::NEXT_TAG); + push(global::NEXT_TAG, types::Permission::SHAREDRW, *pointer_to_ref); + global::NEXT_TAG += 1; } } - -fn demonic_nondet() -> bool { - crate::any() -} diff --git a/library/kani/src/lib.rs b/library/kani/src/lib.rs index cee6bde0eea5..d1ea796617e7 100644 --- a/library/kani/src/lib.rs +++ b/library/kani/src/lib.rs @@ -19,6 +19,8 @@ #![feature(ptr_metadata)] #![feature(f16)] #![feature(f128)] +// required for using size_of_val_raw +#![feature(layout_for_ptr)] // Allow us to use `kani::` to access crate features. extern crate self as kani; From cfbb31eedf920e55a523ffea4944bedf808d8bfd Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 30 Aug 2024 17:05:58 -0400 Subject: [PATCH 68/72] Remove unneccessary "use self::*" in functions --- library/kani/src/aliasing.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 9458b716f99c..a00771c10817 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -202,7 +202,6 @@ pub(super) mod monitor_transitions { // for location:location+size_of(U). // Offset has already been picked earlier. unsafe { - use self::*; if pointer_object(MONITORED) == pointer_object(pointer) && pointer_offset(MONITORED) <= std::mem::size_of::() { @@ -217,7 +216,6 @@ pub(super) mod monitor_transitions { /// tag and the given access permission. pub(super) fn stack_check(tag: PointerTag, access: AccessBit) { unsafe { - use self::*; let mut found = false; let mut j = 0; let mut new_top = 0; From 846975d101d1af7decb9a16a9d3def0b09664aa7 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 30 Aug 2024 17:07:03 -0400 Subject: [PATCH 69/72] Move lookup into the if block --- library/kani/src/aliasing.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index a00771c10817..b01416e4a6f7 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -293,13 +293,13 @@ fn initialize_local(pointer: *const U) { #[rustc_diagnostic_item = "KaniStackCheckPtr"] fn stack_check_ptr(pointer_value: *const *mut U) { unsafe { - let tag = global::TAGS.get(pointer_value); - let perm = global::PERMS.get(pointer_value); let pointer = *pointer_value; let size = unsafe { std::mem::size_of_val_raw::(pointer) }; if pointer_object(pointer) == pointer_object(global::MONITORED) && pointer_offset(global::MONITORED) < size { + let tag = global::TAGS.get(pointer_value); + let perm = global::PERMS.get(pointer_value); if types::Permission::grants(types::Access::READ, perm) { self::monitor_transitions::stack_check(tag, types::Access::READ); } else if types::Permission::grants(types::Access::WRITE, perm) { From 9f1054589c64daf0823ade2cbd92a34ed75c4611 Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 30 Aug 2024 17:17:44 -0400 Subject: [PATCH 70/72] Add size of val everywhere --- library/kani/src/aliasing.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index b01416e4a6f7..02c009b4ee71 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -75,7 +75,8 @@ mod types { /// Encoded as associated constants /// instead of as an enum to ensure /// that the representation uses - /// 1 bit. + /// 8 bits. Using an enum instead can cause + /// factor of 2 blowups. pub(super) type AccessBit = bool; pub(super) struct Access; impl Access { @@ -202,8 +203,9 @@ pub(super) mod monitor_transitions { // for location:location+size_of(U). // Offset has already been picked earlier. unsafe { + let size = std::mem::size_of_val_raw(pointer); if pointer_object(MONITORED) == pointer_object(pointer) - && pointer_offset(MONITORED) <= std::mem::size_of::() + && pointer_offset(MONITORED) <= size { STACK_TAGS[STACK_TOP] = tag; STACK_PERMS[STACK_TOP] = perm; From f24c3271965186909a88676b39e9776e93036b8f Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 30 Aug 2024 17:21:29 -0400 Subject: [PATCH 71/72] Add assertions, initialize in track_local --- library/kani/src/aliasing.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/library/kani/src/aliasing.rs b/library/kani/src/aliasing.rs index 02c009b4ee71..ef634e817a24 100644 --- a/library/kani/src/aliasing.rs +++ b/library/kani/src/aliasing.rs @@ -186,16 +186,16 @@ pub(super) mod monitor_transitions { if demonic_nondet() { let offset: usize = kani::any(); crate::assume(offset < std::mem::size_of::()); + STACK_TOP = 1; MONITORED = pointer.byte_add(offset) as *const u8; - STACK_TAGS[STACK_TOP] = tag; - STACK_PERMS[STACK_TOP] = Permission::UNIQUE; - STACK_TOP += 1; + STACK_TAGS[0] = tag; + STACK_PERMS[0] = Permission::UNIQUE; } } } /// Push a tag with a permission perm at pointer - pub(super) fn push(tag: PointerTag, perm: PermissionByte, pointer: *const U) + pub(super) unsafe fn push(tag: PointerTag, perm: PermissionByte, pointer: *const U) where U: Sized, { @@ -203,6 +203,7 @@ pub(super) mod monitor_transitions { // for location:location+size_of(U). // Offset has already been picked earlier. unsafe { + crate::assert(STACK_TOP < STACK_DEPTH, "Max # of nested borrows (15) exceeded"); let size = std::mem::size_of_val_raw(pointer); if pointer_object(MONITORED) == pointer_object(pointer) && pointer_offset(MONITORED) <= size @@ -240,7 +241,7 @@ pub(super) mod monitor_transitions { } /// Push the permissions at the given location -fn push(tag: types::PointerTag, perm: types::PermissionByte, address: *const U) { +unsafe fn push(tag: types::PointerTag, perm: types::PermissionByte, address: *const U) { self::monitor_transitions::push(tag, perm, address) } @@ -293,7 +294,7 @@ fn initialize_local(pointer: *const U) { /// will succeed, given that offsets within the same allocation /// are considered parts of the same pointer object by cbmc. #[rustc_diagnostic_item = "KaniStackCheckPtr"] -fn stack_check_ptr(pointer_value: *const *mut U) { +unsafe fn stack_check_ptr(pointer_value: *const *mut U) { unsafe { let pointer = *pointer_value; let size = unsafe { std::mem::size_of_val_raw::(pointer) }; @@ -312,7 +313,7 @@ fn stack_check_ptr(pointer_value: *const *mut U) { } #[rustc_diagnostic_item = "KaniStackCheckRef"] -fn stack_check_ref(pointer_value: *const &mut U) { +unsafe fn stack_check_ref(pointer_value: *const &mut U) { stack_check_ptr(pointer_value as *const *mut U); } From e33d6e933a0f9ef386f91cd68a37ecde0e383d9e Mon Sep 17 00:00:00 2001 From: Jacob Salzberg Date: Fri, 30 Aug 2024 17:24:35 -0400 Subject: [PATCH 72/72] Clarify index comment --- kani-compiler/src/kani_middle/transform/body.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kani-compiler/src/kani_middle/transform/body.rs b/kani-compiler/src/kani_middle/transform/body.rs index ebdd7e06bbe1..4ca4f4b4671b 100644 --- a/kani-compiler/src/kani_middle/transform/body.rs +++ b/kani-compiler/src/kani_middle/transform/body.rs @@ -204,7 +204,7 @@ impl MutableBody { self.insert_terminator(source, position, terminator); } - /// Add a new assert to the basic block indicated by the given index. + /// Add a new assert to the basic block indicated by "source". /// /// The new assertion 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