Skip to content

Commit e4a90e9

Browse files
Implement validity checks (rust-lang#3085)
This is still incomplete, but hopefully it can be merged as an unstable feature. I'll publish an RFC shortly. This instruments the function body with assertion checks to see if users are generating invalid values. This covers: - Union access - Raw pointer dereference - Transmute value - Field assignment of struct with invalid values - Aggregate assignment Things not covered today should trigger ICE or a delayed verification failure due to unsupported feature. ## Design This change has two main design changes which are inside the new `kani_compiler::kani_middle::transform` module: 1- Instance body should now be retrieved from the `BodyTransformation` structure. This structure will run transformation passes on instance bodies (i.e.: monomorphic instances) and cache the result. 2- Create a new transformation pass that instruments the body of a function for every potential invalid value generation. 3- Create a body builder which contains all elements of a function body and mutable functions to modify them accordingly. Related to rust-lang#2998 Fixes rust-lang#301 --------- Co-authored-by: Zyad Hassan <[email protected]>
1 parent 9de7201 commit e4a90e9

File tree

18 files changed

+1641
-36
lines changed

18 files changed

+1641
-36
lines changed

kani-compiler/src/args.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,15 @@ pub struct Arguments {
7171
#[clap(long)]
7272
/// A legacy flag that is now ignored.
7373
goto_c: bool,
74+
/// Enable specific checks.
75+
#[clap(long)]
76+
pub ub_check: Vec<ExtraChecks>,
77+
}
78+
79+
#[derive(Debug, Clone, Copy, AsRefStr, EnumString, VariantNames, PartialEq, Eq)]
80+
#[strum(serialize_all = "snake_case")]
81+
pub enum ExtraChecks {
82+
/// Check that produced values are valid except for uninitialized values.
83+
/// See https://github.com/model-checking/kani/issues/920.
84+
Validity,
7485
}

kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ impl<'tcx> GotocCtx<'tcx> {
6060
debug!("Double codegen of {:?}", old_sym);
6161
} else {
6262
assert!(old_sym.is_function());
63-
let body = instance.body().unwrap();
63+
let body = self.transformer.body(self.tcx, instance);
6464
self.set_current_fn(instance, &body);
6565
self.print_instance(instance, &body);
6666
self.codegen_function_prelude(&body);
@@ -201,7 +201,7 @@ impl<'tcx> GotocCtx<'tcx> {
201201

202202
pub fn declare_function(&mut self, instance: Instance) {
203203
debug!("declaring {}; {:?}", instance.name(), instance);
204-
let body = instance.body().unwrap();
204+
let body = self.transformer.body(self.tcx, instance);
205205
self.set_current_fn(instance, &body);
206206
debug!(krate=?instance.def.krate(), is_std=self.current_fn().is_std(), "declare_function");
207207
self.ensure(&self.symbol_name_stable(instance), |ctx, fname| {

kani-compiler/src/codegen_cprover_gotoc/compiler_interface.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::kani_middle::provide;
1212
use crate::kani_middle::reachability::{
1313
collect_reachable_items, filter_const_crate_items, filter_crate_items,
1414
};
15+
use crate::kani_middle::transform::BodyTransformation;
1516
use crate::kani_middle::{check_reachable_items, dump_mir_items};
1617
use crate::kani_queries::QueryDb;
1718
use cbmc::goto_program::Location;
@@ -86,16 +87,18 @@ impl GotocCodegenBackend {
8687
symtab_goto: &Path,
8788
machine_model: &MachineModel,
8889
check_contract: Option<InternalDefId>,
90+
mut transformer: BodyTransformation,
8991
) -> (GotocCtx<'tcx>, Vec<MonoItem>, Option<AssignsContract>) {
9092
let items = with_timer(
91-
|| collect_reachable_items(tcx, starting_items),
93+
|| collect_reachable_items(tcx, &mut transformer, starting_items),
9294
"codegen reachability analysis",
9395
);
9496
dump_mir_items(tcx, &items, &symtab_goto.with_extension("kani.mir"));
9597

9698
// Follow rustc naming convention (cx is abbrev for context).
9799
// https://rustc-dev-guide.rust-lang.org/conventions.html#naming-conventions
98-
let mut gcx = GotocCtx::new(tcx, (*self.queries.lock().unwrap()).clone(), machine_model);
100+
let mut gcx =
101+
GotocCtx::new(tcx, (*self.queries.lock().unwrap()).clone(), machine_model, transformer);
99102
check_reachable_items(gcx.tcx, &gcx.queries, &items);
100103

101104
let contract_info = with_timer(
@@ -227,6 +230,7 @@ impl CodegenBackend for GotocCodegenBackend {
227230
// - None: Don't generate code. This is used to compile dependencies.
228231
let base_filename = tcx.output_filenames(()).output_path(OutputType::Object);
229232
let reachability = queries.args().reachability_analysis;
233+
let mut transformer = BodyTransformation::new(&queries, tcx);
230234
let mut results = GotoCodegenResults::new(tcx, reachability);
231235
match reachability {
232236
ReachabilityType::Harnesses => {
@@ -248,8 +252,9 @@ impl CodegenBackend for GotocCodegenBackend {
248252
model_path,
249253
&results.machine_model,
250254
contract_metadata,
255+
transformer,
251256
);
252-
results.extend(gcx, items, None);
257+
transformer = results.extend(gcx, items, None);
253258
if let Some(assigns_contract) = contract_info {
254259
self.queries.lock().unwrap().register_assigns_contract(
255260
canonical_mangled_name(harness).intern(),
@@ -263,7 +268,7 @@ impl CodegenBackend for GotocCodegenBackend {
263268
// test closure that we want to execute
264269
// TODO: Refactor this code so we can guarantee that the pair (test_fn, test_desc) actually match.
265270
let mut descriptions = vec![];
266-
let harnesses = filter_const_crate_items(tcx, |_, item| {
271+
let harnesses = filter_const_crate_items(tcx, &mut transformer, |_, item| {
267272
if is_test_harness_description(tcx, item.def) {
268273
descriptions.push(item.def);
269274
true
@@ -282,6 +287,7 @@ impl CodegenBackend for GotocCodegenBackend {
282287
&model_path,
283288
&results.machine_model,
284289
Default::default(),
290+
transformer,
285291
);
286292
results.extend(gcx, items, None);
287293

@@ -319,9 +325,10 @@ impl CodegenBackend for GotocCodegenBackend {
319325
&model_path,
320326
&results.machine_model,
321327
Default::default(),
328+
transformer,
322329
);
323330
assert!(contract_info.is_none());
324-
results.extend(gcx, items, None);
331+
let _ = results.extend(gcx, items, None);
325332
}
326333
}
327334

@@ -613,12 +620,18 @@ impl GotoCodegenResults {
613620
}
614621
}
615622

616-
fn extend(&mut self, gcx: GotocCtx, items: Vec<MonoItem>, metadata: Option<HarnessMetadata>) {
623+
fn extend(
624+
&mut self,
625+
gcx: GotocCtx,
626+
items: Vec<MonoItem>,
627+
metadata: Option<HarnessMetadata>,
628+
) -> BodyTransformation {
617629
let mut items = items;
618630
self.harnesses.extend(metadata);
619631
self.concurrent_constructs.extend(gcx.concurrent_constructs);
620632
self.unsupported_constructs.extend(gcx.unsupported_constructs);
621633
self.items.append(&mut items);
634+
gcx.transformer
622635
}
623636

624637
/// Prints a report at the end of the compilation.

kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use super::vtable_ctx::VtableCtx;
1818
use crate::codegen_cprover_gotoc::overrides::{fn_hooks, GotocHooks};
1919
use crate::codegen_cprover_gotoc::utils::full_crate_name;
2020
use crate::codegen_cprover_gotoc::UnsupportedConstructs;
21+
use crate::kani_middle::transform::BodyTransformation;
2122
use crate::kani_queries::QueryDb;
2223
use cbmc::goto_program::{DatatypeComponent, Expr, Location, Stmt, Symbol, SymbolTable, Type};
2324
use cbmc::utils::aggr_tag;
@@ -70,6 +71,8 @@ pub struct GotocCtx<'tcx> {
7071
/// We collect them and print one warning at the end if not empty instead of printing one
7172
/// warning at each occurrence.
7273
pub concurrent_constructs: UnsupportedConstructs,
74+
/// The body transformation agent.
75+
pub transformer: BodyTransformation,
7376
}
7477

7578
/// Constructor
@@ -78,6 +81,7 @@ impl<'tcx> GotocCtx<'tcx> {
7881
tcx: TyCtxt<'tcx>,
7982
queries: QueryDb,
8083
machine_model: &MachineModel,
84+
transformer: BodyTransformation,
8185
) -> GotocCtx<'tcx> {
8286
let fhks = fn_hooks();
8387
let symbol_table = SymbolTable::new(machine_model.clone());
@@ -99,6 +103,7 @@ impl<'tcx> GotocCtx<'tcx> {
99103
global_checks_count: 0,
100104
unsupported_constructs: FxHashMap::default(),
101105
concurrent_constructs: FxHashMap::default(),
106+
transformer,
102107
}
103108
}
104109
}

kani-compiler/src/codegen_cprover_gotoc/overrides/hooks.rs

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
1111
use crate::codegen_cprover_gotoc::codegen::{bb_label, PropertyClass};
1212
use crate::codegen_cprover_gotoc::GotocCtx;
13+
use crate::kani_middle::attributes::matches_diagnostic as matches_function;
1314
use crate::unwrap_or_return_codegen_unimplemented_stmt;
1415
use cbmc::goto_program::{BuiltinFn, Expr, Location, Stmt, Type};
1516
use rustc_middle::ty::TyCtxt;
@@ -35,17 +36,6 @@ pub trait GotocHook {
3536
) -> Stmt;
3637
}
3738

38-
fn matches_function(tcx: TyCtxt, instance: Instance, attr_name: &str) -> bool {
39-
let attr_sym = rustc_span::symbol::Symbol::intern(attr_name);
40-
if let Some(attr_id) = tcx.all_diagnostic_items(()).name_to_id.get(&attr_sym) {
41-
if rustc_internal::internal(tcx, instance.def.def_id()) == *attr_id {
42-
debug!("matched: {:?} {:?}", attr_id, attr_sym);
43-
return true;
44-
}
45-
}
46-
false
47-
}
48-
4939
/// A hook for Kani's `cover` function (declared in `library/kani/src/lib.rs`).
5040
/// The function takes two arguments: a condition expression (bool) and a
5141
/// message (&'static str).
@@ -57,7 +47,7 @@ fn matches_function(tcx: TyCtxt, instance: Instance, attr_name: &str) -> bool {
5747
struct Cover;
5848
impl GotocHook for Cover {
5949
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
60-
matches_function(tcx, instance, "KaniCover")
50+
matches_function(tcx, instance.def, "KaniCover")
6151
}
6252

6353
fn handle(
@@ -92,7 +82,7 @@ impl GotocHook for Cover {
9282
struct Assume;
9383
impl GotocHook for Assume {
9484
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
95-
matches_function(tcx, instance, "KaniAssume")
85+
matches_function(tcx, instance.def, "KaniAssume")
9686
}
9787

9888
fn handle(
@@ -116,7 +106,7 @@ impl GotocHook for Assume {
116106
struct Assert;
117107
impl GotocHook for Assert {
118108
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
119-
matches_function(tcx, instance, "KaniAssert")
109+
matches_function(tcx, instance.def, "KaniAssert")
120110
}
121111

122112
fn handle(
@@ -157,7 +147,7 @@ struct Nondet;
157147

158148
impl GotocHook for Nondet {
159149
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
160-
matches_function(tcx, instance, "KaniAnyRaw")
150+
matches_function(tcx, instance.def, "KaniAnyRaw")
161151
}
162152

163153
fn handle(
@@ -201,7 +191,7 @@ impl GotocHook for Panic {
201191
|| tcx.has_attr(def_id, rustc_span::sym::rustc_const_panic_str)
202192
|| Some(def_id) == tcx.lang_items().panic_fmt()
203193
|| Some(def_id) == tcx.lang_items().begin_panic_fn()
204-
|| matches_function(tcx, instance, "KaniPanic")
194+
|| matches_function(tcx, instance.def, "KaniPanic")
205195
}
206196

207197
fn handle(
@@ -221,7 +211,7 @@ impl GotocHook for Panic {
221211
struct IsReadOk;
222212
impl GotocHook for IsReadOk {
223213
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
224-
matches_function(tcx, instance, "KaniIsReadOk")
214+
matches_function(tcx, instance.def, "KaniIsReadOk")
225215
}
226216

227217
fn handle(
@@ -365,7 +355,7 @@ struct UntrackedDeref;
365355

366356
impl GotocHook for UntrackedDeref {
367357
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
368-
matches_function(tcx, instance, "KaniUntrackedDeref")
358+
matches_function(tcx, instance.def, "KaniUntrackedDeref")
369359
}
370360

371361
fn handle(

kani-compiler/src/kani_middle/attributes.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,3 +1037,14 @@ fn attr_kind(tcx: TyCtxt, attr: &Attribute) -> Option<KaniAttributeKind> {
10371037
_ => None,
10381038
}
10391039
}
1040+
1041+
pub fn matches_diagnostic<T: CrateDef>(tcx: TyCtxt, def: T, attr_name: &str) -> bool {
1042+
let attr_sym = rustc_span::symbol::Symbol::intern(attr_name);
1043+
if let Some(attr_id) = tcx.all_diagnostic_items(()).name_to_id.get(&attr_sym) {
1044+
if rustc_internal::internal(tcx, def.def_id()) == *attr_id {
1045+
debug!("matched: {:?} {:?}", attr_id, attr_sym);
1046+
return true;
1047+
}
1048+
}
1049+
false
1050+
}

kani-compiler/src/kani_middle/mod.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use rustc_target::abi::call::FnAbi;
2323
use rustc_target::abi::{HasDataLayout, TargetDataLayout};
2424
use stable_mir::mir::mono::{Instance, InstanceKind, MonoItem};
2525
use stable_mir::mir::pretty::pretty_ty;
26-
use stable_mir::ty::{BoundVariableKind, RigidTy, Span as SpanStable, Ty, TyKind};
26+
use stable_mir::ty::{BoundVariableKind, FnDef, RigidTy, Span as SpanStable, Ty, TyKind};
2727
use stable_mir::visitor::{Visitable, Visitor as TypeVisitor};
2828
use stable_mir::{CrateDef, DefId};
2929
use std::fs::File;
@@ -41,6 +41,7 @@ pub mod provide;
4141
pub mod reachability;
4242
pub mod resolve;
4343
pub mod stubbing;
44+
pub mod transform;
4445

4546
/// Check that all crate items are supported and there's no misconfiguration.
4647
/// This method will exhaustively print any error / warning and it will abort at the end if any
@@ -316,3 +317,18 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for CompilerHelpers<'tcx> {
316317
}
317318
}
318319
}
320+
321+
/// Find an instance of a function from the given crate that has been annotated with `diagnostic`
322+
/// item.
323+
fn find_fn_def(tcx: TyCtxt, diagnostic: &str) -> Option<FnDef> {
324+
let attr_id = tcx
325+
.all_diagnostic_items(())
326+
.name_to_id
327+
.get(&rustc_span::symbol::Symbol::intern(diagnostic))?;
328+
let TyKind::RigidTy(RigidTy::FnDef(def, _)) =
329+
rustc_internal::stable(tcx.type_of(attr_id)).value.kind()
330+
else {
331+
return None;
332+
};
333+
Some(def)
334+
}

kani-compiler/src/kani_middle/provide.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::args::{Arguments, ReachabilityType};
88
use crate::kani_middle::intrinsics::ModelIntrinsics;
99
use crate::kani_middle::reachability::{collect_reachable_items, filter_crate_items};
1010
use crate::kani_middle::stubbing;
11+
use crate::kani_middle::transform::BodyTransformation;
1112
use crate::kani_queries::QueryDb;
1213
use rustc_hir::def_id::{DefId, LocalDefId};
1314
use rustc_middle::util::Providers;
@@ -79,8 +80,9 @@ fn collect_and_partition_mono_items(
7980
rustc_smir::rustc_internal::run(tcx, || {
8081
let local_reachable =
8182
filter_crate_items(tcx, |_, _| true).into_iter().map(MonoItem::Fn).collect::<Vec<_>>();
83+
8284
// We do not actually need the value returned here.
83-
collect_reachable_items(tcx, &local_reachable);
85+
collect_reachable_items(tcx, &mut BodyTransformation::dummy(), &local_reachable);
8486
})
8587
.unwrap();
8688
(rustc_interface::DEFAULT_QUERY_PROVIDERS.collect_and_partition_mono_items)(tcx, key)

0 commit comments

Comments
 (0)