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 all 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,
}
77 changes: 77 additions & 0 deletions kani-compiler/src/kani_middle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,80 @@ fn find_fn_def(tcx: TyCtxt, diagnostic: &str) -> Option<FnDef> {
};
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
/// def
pub fn new(diagnostic: String, def: FnDef) -> NamedFnDef {
NamedFnDef { diagnostic, def }
}
}

trait Cache<T> {
/// 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<P, F, E>(&mut self, p: P, f: F) -> Result<&mut T, E>
where
F: FnOnce() -> Result<T, E>,
P: Fn(&T) -> bool;
}

impl<T> Cache<T> for Vec<T>
where
T: PartialEq,
{
fn try_get_or_insert<P, F, E>(&mut self, p: P, f: F) -> Result<&mut T, E>
where
F: FnOnce() -> Result<T, E>,
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<NamedFnDef>,
}

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)
}
}
12 changes: 12 additions & 0 deletions kani-compiler/src/kani_middle/reachability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@ impl CallGraph {

/// Print the graph in DOT format to a file.
/// See <https://graphviz.org/doc/info/lang.html> 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");
Expand All @@ -601,6 +602,7 @@ impl CallGraph {
}

/// Write all notes to the given writer.
#[allow(unused)]
fn dump_all<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {
tracing::info!(nodes=?self.nodes.len(), edges=?self.edges.len(), "dump_all");
for node in &self.nodes {
Expand All @@ -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<W: Write>(&self, writer: &mut W, target: &str) -> std::io::Result<()> {
let mut queue: Vec<Node> =
self.nodes.iter().filter(|item| item.to_string().contains(target)).cloned().collect();
Expand Down Expand Up @@ -645,6 +648,15 @@ impl CallGraph {
}
Ok(())
}

/// Get all items adjacent to the current item in the
/// call graph.
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(),
}
}
}

impl Display for Node {
Expand Down
86 changes: 62 additions & 24 deletions kani-compiler/src/kani_middle/transform/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,46 @@ impl MutableBody {
self.insert_stmt(stmt, source, position);
}

/// Add a new assert to the basic block indicated by the given index.
/// 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
/// 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,
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 "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
Expand All @@ -185,28 +224,11 @@ 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) => {
let assert_op = Operand::Copy(Place::from(self.new_local(
assert_fn.ty(),
span,
Mutability::Not,
)));
let msg_op = self.new_str_operand(msg, span);
let kind = TerminatorKind::Call {
func: assert_op,
args: vec![Operand::Move(Place::from(value)), msg_op],
destination: Place {
local: self.new_local(Ty::new_tuple(&[]), span, Mutability::Not),
projection: vec![],
},
target: Some(new_bb),
unwind: UnwindAction::Terminate,
};
let terminator = Terminator { kind, span };
self.insert_terminator(source, position, terminator);
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 => {
tcx.sess
Expand All @@ -220,24 +242,40 @@ 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<Operand>,
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<Operand>,
destination: Place,
) {
let new_bb = self.blocks.len();
let span = source.span(&self.blocks);
let callee_op =
Operand::Copy(Place::from(self.new_local(callee.ty(), span, Mutability::Not)));
let callee_op = Operand::Copy(Place::from(callee));
let kind = TerminatorKind::Call {
func: callee_op,
args,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT
//! This module contains a cache of resolved generic functions

use crate::kani_middle::find_fn_def;
use rustc_middle::ty::TyCtxt;

type Result<T> = std::result::Result<T, super::MirError>;


/// Caches function instances for later lookups.
#[derive(Default, Debug)]
pub struct Cache {
/// The cache
cache: Vec<NamedFnDef>,
}

fn try_get_or_insert<T, P, F>(vec: &mut Vec<T>, p: P, f: F) -> Result<&mut T>
where
F: FnOnce() -> Result<T>,
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,
signature: Signature,
) -> Result<&> {
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(NamedFnDef::new(signature, instance))
},
)
.map(|entry| &entry.instance)
}
}
Loading
Loading