Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Stacked Borrows in Kani #3406

Merged
merged 74 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
829c3c0
Add empty pass
Aug 1, 2024
052545d
Add "boxed" test for pointers into the heap.
Aug 2, 2024
ba92c6b
Add Stacked Borrows instrumentation code for stack allocations
Aug 9, 2024
060cc6f
Fix dup. write code to take raw pointer from ref, not org. var
Aug 9, 2024
63390fb
Remove std/kani functions to get efficient impl.
Aug 9, 2024
b503aa0
Instrument new stack references.
Aug 10, 2024
bb6045b
Add case x: *mut T = &raw *(y as &mut T);
Aug 11, 2024
8df3234
Match both lvalue and rvalue projections
Aug 12, 2024
c732b70
Uncomment important lines
Aug 12, 2024
4e298ce
Instrument with use_2
Aug 12, 2024
a8e8470
Add println statements for failing stack checks
Aug 12, 2024
0294688
To clear noise, instrument for non-kani in macros.
Aug 12, 2024
dca76d9
Instrument duplicate write in both the rust and kani setting.
Aug 13, 2024
820584f
Make test pass
Aug 13, 2024
6327437
Add one without macro invocations to show the instrumentation works
Aug 13, 2024
621287c
Clean up for pr review
Aug 14, 2024
05032da
apply formatter
Aug 14, 2024
c39e8ca
Commit and refactor
Aug 14, 2024
e6126f4
Add tests for C99 format locals and for control flow.
Aug 15, 2024
26623ce
Refactor imports
Aug 15, 2024
5155f36
Factor out local collection; local indices now computed directly
Aug 15, 2024
a0fe559
Remove pub(self)
Aug 15, 2024
ef833b2
Add documentation for the module sstate.
Aug 15, 2024
553c737
Add demonic nondeterminism blurb
Aug 15, 2024
5df2dbc
provisionally add line numbers; tests failing
Aug 15, 2024
a190e1c
Remove extraneous assertion
Aug 15, 2024
6ce6942
Merge remote-tracking branch 'kani/main'
Aug 16, 2024
79d40ad
Make the pass compile on the current compiler version
Aug 16, 2024
4a718f7
Change test to emit line number.
Aug 19, 2024
cf3e19c
Delete redundant tests, provide accurate line #s.
Aug 19, 2024
afc5eca
Improve error reporting.
Aug 23, 2024
f0efc1e
Document the actions module.
Aug 23, 2024
84ed701
Add doc comments for each of the instrumentation data fields.
Aug 23, 2024
1877591
-visibility, -raw MIR manipulation, -blacklist, +doc
Aug 23, 2024
4fd5842
Add module level documentation + copyrights
Aug 23, 2024
a742547
Add message to assertion
Aug 23, 2024
69fd136
Apply formatter
Aug 23, 2024
6b76832
Remove extraneous arg
Aug 23, 2024
3376a93
Begin implementing artem's comments
Aug 26, 2024
dc332a7
Fix code actions
Aug 26, 2024
fb388f3
Redo expected files with new line info
Aug 26, 2024
0c17fdd
Notify the user of all unhandled cases
Aug 26, 2024
3cffb18
Notify the user that term. does not change sb state
Aug 26, 2024
fa201bc
Combine object & offset to reduce looping
Aug 26, 2024
25958c6
Fix all monitor code to use nondet offset
Aug 27, 2024
a86399d
Run formatter
Aug 27, 2024
c236c20
Add copyright info
Aug 27, 2024
cda8657
Update for clippy
Aug 27, 2024
db2c570
Fix lint on range
Aug 27, 2024
9953526
Run kani fmt
Aug 27, 2024
f0183d2
More clippy lints
Aug 27, 2024
cab1067
Update line numbers
Aug 27, 2024
0e99f41
Begin implementing the backwards pass artem suggested
Aug 28, 2024
cff2102
Continue implementing the changes Artem suggested
Aug 28, 2024
05cb010
Complete instrumentation of instructions accum. by visitor
Aug 29, 2024
b447898
Comment the instrumentation file
Aug 29, 2024
aa0e691
Comment the visitor file
Aug 29, 2024
a4133c4
Fix warnings, regression caused by terminator case
Aug 29, 2024
e3ee7b1
Merge branch 'kani-main'
Aug 29, 2024
61f8ce7
Modify comments, change to enums.
Aug 29, 2024
07028ee
Try using bool instead
Aug 29, 2024
623278b
Address performance regression by using assoc. constants
Aug 29, 2024
00ef0e9
Run kani-fmt
Aug 29, 2024
a4bb9be
Remove local variable that is only an alias
Aug 29, 2024
3a2d693
Remove unhelpful doc comment
Aug 29, 2024
443fb61
Fix tag --> PointerTag
Aug 29, 2024
23dad3b
Cache FnDef instead of Instance; document test cases
Aug 30, 2024
a36a451
Update comment
Aug 30, 2024
16e4bdc
Separate the constants and the types. Use the underlying value size.
Aug 30, 2024
cfbb31e
Remove unneccessary "use self::*" in functions
Aug 30, 2024
846975d
Move lookup into the if block
Aug 30, 2024
9f10545
Add size of val everywhere
Aug 30, 2024
f24c327
Add assertions, initialize in track_local
Aug 30, 2024
e33d6e9
Clarify index comment
Aug 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions kani-compiler/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ pub enum ExtraChecks {
/// Check that produced values are valid except for uninitialized values.
/// See https://github.com/model-checking/kani/issues/920.
Validity,
/// Check for violations of pointer aliasing model
Aliasing,
/// Check for using uninitialized memory.
Uninit,
}
237 changes: 237 additions & 0 deletions kani-compiler/src/kani_middle/transform/check_aliasing/actions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
// 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<Action>,
/// 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<Action> {
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.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 */ }
jsalzbergedu marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// 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;
}
};
match self.locals[place.local].ty.kind() {
jsalzbergedu marked this conversation as resolved.
Show resolved Hide resolved
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);
}
_ => {}
}
}

/// 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
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(_) => {}
}
}

/// 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);
}
StatementKind::FakeRead(_, _) => {}
StatementKind::SetDiscriminant { .. } => {}
StatementKind::Deinit(_) => {}
StatementKind::StorageLive(_) => {}
StatementKind::StorageDead(_) => {}
StatementKind::Retag(_, _) => {}
StatementKind::PlaceMention(_) => {}
StatementKind::AscribeUserType { .. } => {}
StatementKind::Coverage(_) => {}
StatementKind::Intrinsic(_) => {}
StatementKind::ConstEvalCounter => {}
StatementKind::Nop => {}
}
}

/// 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);
jsalzbergedu marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
// 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 => {}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright Kani Contributors
// 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 {
jsalzbergedu marked this conversation as resolved.
Show resolved Hide resolved
/// The diagnostic string associated with the function
diagnostic: String,
jsalzbergedu marked this conversation as resolved.
Show resolved Hide resolved
/// The generic arguments applied
args: Vec<GenericArg>,
}

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() }
}
}

/// 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<Instance>,
}

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;
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 = 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()))?;
cache.push(Instance::new(sig, instance));
Ok(&cache[cache.len() - 1].instance)
jsalzbergedu marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading
Loading