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 18 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 @@ -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,
jsalzbergedu marked this conversation as resolved.
Show resolved Hide resolved
/// Check for violations of pointer aliasing model
Aliasing,
/// Check for using uninitialized memory.
Uninit,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use super::{Local, InstrumentationData, MirError, Body};

pub struct BodyMutationPassState<'tcx, 'cache> {
values: Vec<Local>,
instrumentation_data: InstrumentationData<'tcx, 'cache>,
}

impl<'tcx, 'cache> BodyMutationPassState<'tcx, 'cache> {
pub fn new(values: Vec<Local>, 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 {
jsalzbergedu marked this conversation as resolved.
Show resolved Hide resolved
self.instrument_locals().unwrap();
self.instrumentation_data.body.finalize_prologue();
self.instrument_instructions().unwrap();
self.instrumentation_data.body.finalize()
}
}
258 changes: 258 additions & 0 deletions kani-compiler/src/kani_middle/transform/check_aliasing/body_mutator.rs
Original file line number Diff line number Diff line change
@@ -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<BasicBlock>,
locals: Vec<LocalDecl>,
arg_count: usize,
var_debug_info: Vec<VarDebugInfo>,
spread_arg: Option<Local>,
span: Span,

ghost_locals: Vec<LocalDecl>,
ghost_blocks: Vec<BasicBlock>,
ghost_statements: Vec<Statement>,
}

impl BodyMutator {
jsalzbergedu marked this conversation as resolved.
Show resolved Hide resolved
/// Instantiate the body mutator
pub fn new(
blocks: Vec<BasicBlock>,
locals: Vec<LocalDecl>,
arg_count: usize,
var_debug_info: Vec<VarDebugInfo>,
spread_arg: Option<Local>,
span: Span,
ghost_locals: Vec<LocalDecl>,
ghost_blocks: Vec<BasicBlock>,
statements: Vec<Statement>,
) -> 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: 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),
}
Original file line number Diff line number Diff line change
@@ -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 {
jsalzbergedu marked this conversation as resolved.
Show resolved Hide resolved
body: BodyMutator,
unit: Local,
cache: HashMap<MirInstance, Local>,
}

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: 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
}
}
Loading