Skip to content

Commit b4480ac

Browse files
authored
Retrieve info for recursion tracker reliably (rust-lang#3045)
Fixes model-checking/kani#3035. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Signed-off-by: Felipe R. Monteiro <[email protected]>
1 parent fa9f61d commit b4480ac

File tree

4 files changed

+65
-16
lines changed

4 files changed

+65
-16
lines changed

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

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,46 @@ impl<'tcx> GotocCtx<'tcx> {
3535
) -> AssignsContract {
3636
let tcx = self.tcx;
3737
let function_under_contract_attrs = KaniAttributes::for_item(tcx, function_under_contract);
38-
let wrapped_fn = function_under_contract_attrs.inner_check().unwrap().unwrap();
3938

39+
let recursion_wrapper_id =
40+
function_under_contract_attrs.checked_with_id().unwrap().unwrap();
41+
let expected_name = format!("{}::REENTRY", tcx.item_name(recursion_wrapper_id));
42+
let mut recursion_tracker = items.iter().filter_map(|i| match i {
43+
MonoItem::Static(recursion_tracker)
44+
if (*recursion_tracker).name().contains(expected_name.as_str()) =>
45+
{
46+
Some(*recursion_tracker)
47+
}
48+
_ => None,
49+
});
50+
let recursion_tracker_def = recursion_tracker
51+
.next()
52+
.expect("There should be at least one recursion tracker (REENTRY) in scope");
53+
assert!(
54+
recursion_tracker.next().is_none(),
55+
"Only one recursion tracker (REENTRY) may be in scope"
56+
);
57+
58+
let span_of_recursion_wrapper = tcx.def_span(recursion_wrapper_id);
59+
let location_of_recursion_wrapper = self.codegen_span(&span_of_recursion_wrapper);
60+
// The name and location for the recursion tracker should match the exact information added
61+
// to the symbol table, otherwise our contract instrumentation will silently failed.
62+
// This happens because Kani relies on `--nondet-static-exclude` from CBMC to properly
63+
// handle this tracker. CBMC silently fails if there is no match in the symbol table
64+
// that correspond to the argument of this flag.
65+
// More details at https://github.com/model-checking/kani/pull/3045.
66+
let full_recursion_tracker_name = format!(
67+
"{}:{}",
68+
location_of_recursion_wrapper
69+
.filename()
70+
.expect("recursion location wrapper should have a file name"),
71+
// We must use the pretty name of the tracker instead of the mangled name.
72+
// This restrictions comes from `--nondet-static-exclude` in CBMC.
73+
// Mode details at https://github.com/diffblue/cbmc/issues/8225.
74+
recursion_tracker_def.name(),
75+
);
76+
77+
let wrapped_fn = function_under_contract_attrs.inner_check().unwrap().unwrap();
4078
let mut instance_under_contract = items.iter().filter_map(|i| match i {
4179
MonoItem::Fn(instance @ Instance { def, .. })
4280
if wrapped_fn == rustc_internal::internal(tcx, def.def_id()) =>
@@ -56,23 +94,12 @@ impl<'tcx> GotocCtx<'tcx> {
5694
vec![]
5795
});
5896
self.attach_modifies_contract(instance_of_check, assigns_contract);
59-
6097
let wrapper_name = self.symbol_name_stable(instance_of_check);
6198

62-
let recursion_wrapper_id =
63-
function_under_contract_attrs.checked_with_id().unwrap().unwrap();
64-
let span_of_recursion_wrapper = tcx.def_span(recursion_wrapper_id);
65-
let location_of_recursion_wrapper = self.codegen_span(&span_of_recursion_wrapper);
66-
67-
let full_name = format!(
68-
"{}:{}::REENTRY",
69-
location_of_recursion_wrapper
70-
.filename()
71-
.expect("recursion location wrapper should have a file name"),
72-
tcx.item_name(recursion_wrapper_id),
73-
);
74-
75-
AssignsContract { recursion_tracker: full_name, contracted_function_name: wrapper_name }
99+
AssignsContract {
100+
recursion_tracker: full_recursion_tracker_name,
101+
contracted_function_name: wrapper_name,
102+
}
76103
}
77104

78105
/// Convert the Kani level contract into a CBMC level contract by creating a

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ impl<'tcx> GotocCtx<'tcx> {
3333

3434
let typ = self.codegen_ty_stable(instance.ty());
3535
let location = self.codegen_span_stable(def.span());
36+
// Contracts instrumentation relies on `--nondet-static-exclude` to properly
37+
// havoc static variables. Kani uses the location and pretty name to identify
38+
// the correct variables. If the wrong name is used, CBMC may fail silently.
39+
// More details at https://github.com/diffblue/cbmc/issues/8225.
3640
let symbol = Symbol::static_variable(symbol_name.clone(), symbol_name, typ, location)
3741
.with_is_hidden(false) // Static items are always user defined.
3842
.with_pretty_name(pretty_name);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
VERIFICATION:- SUCCESSFUL
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-flags: -Zfunction-contracts
4+
5+
//! Check Kani handling of generics and recursion with function contracts.
6+
7+
#[kani::requires(x != 0)]
8+
fn foo<T: std::cmp::PartialEq<i32>>(x: T) {
9+
assert_ne!(x, 0);
10+
foo(x);
11+
}
12+
13+
#[kani::proof_for_contract(foo)]
14+
fn foo_harness() {
15+
let input: i32 = kani::any();
16+
foo(input);
17+
}

0 commit comments

Comments
 (0)