Skip to content

Commit e9eeef7

Browse files
celinvaljaisnan
andauthored
Remove empty box creation from contracts impl (#3233)
There seems to be an issue in CBMC contracts implementation that it assumes that `free` must have a body. However, slicing can remove `free` body if the harness does not allocate anything. diffblue/cbmc#8317 We used to create an empty Box before to force `free` to be in scope. Instead, just invoke `free(NULL)` which is a no-op. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. Co-authored-by: Jaisurya Nanduri <[email protected]>
1 parent a6332fc commit e9eeef7

File tree

3 files changed

+55
-1
lines changed

3 files changed

+55
-1
lines changed

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,45 @@ impl GotocHook for UntrackedDeref {
458458
}
459459
}
460460

461+
struct InitContracts;
462+
463+
/// CBMC contracts currently has a limitation where `free` has to be in scope.
464+
/// However, if there is no dynamic allocation in the harness, slicing removes `free` from the
465+
/// scope.
466+
///
467+
/// Thus, this function will basically translate into:
468+
/// ```c
469+
/// // This is a no-op.
470+
/// free(NULL);
471+
/// ```
472+
impl GotocHook for InitContracts {
473+
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
474+
matches_function(tcx, instance.def, "KaniInitContracts")
475+
}
476+
477+
fn handle(
478+
&self,
479+
gcx: &mut GotocCtx,
480+
_instance: Instance,
481+
fargs: Vec<Expr>,
482+
_assign_to: &Place,
483+
target: Option<BasicBlockIdx>,
484+
span: Span,
485+
) -> Stmt {
486+
assert_eq!(fargs.len(), 0,);
487+
let loc = gcx.codegen_span_stable(span);
488+
Stmt::block(
489+
vec![
490+
BuiltinFn::Free
491+
.call(vec![Expr::pointer_constant(0, Type::void_pointer())], loc)
492+
.as_stmt(loc),
493+
Stmt::goto(bb_label(target.unwrap()), loc),
494+
],
495+
loc,
496+
)
497+
}
498+
}
499+
461500
pub fn fn_hooks() -> GotocHooks {
462501
GotocHooks {
463502
hooks: vec![
@@ -472,6 +511,7 @@ pub fn fn_hooks() -> GotocHooks {
472511
Rc::new(RustAlloc),
473512
Rc::new(MemCmp),
474513
Rc::new(UntrackedDeref),
514+
Rc::new(InitContracts),
475515
],
476516
}
477517
}

library/kani/src/internal.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,17 @@ impl<'a, T> Pointer<'a> for *mut T {
7676
pub fn untracked_deref<T>(_: &T) -> T {
7777
todo!()
7878
}
79+
80+
/// CBMC contracts currently has a limitation where `free` has to be in scope.
81+
/// However, if there is no dynamic allocation in the harness, slicing removes `free` from the
82+
/// scope.
83+
///
84+
/// Thus, this function will basically translate into:
85+
/// ```c
86+
/// // This is a no-op.
87+
/// free(NULL);
88+
/// ```
89+
#[inline(never)]
90+
#[doc(hidden)]
91+
#[rustc_diagnostic_item = "KaniInitContracts"]
92+
pub fn init_contracts() {}

library/kani_macros/src/sysroot/contracts/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ pub fn proof_for_contract(attr: TokenStream, item: TokenStream) -> TokenStream {
294294
#[kanitool::proof_for_contract = stringify!(#args)]
295295
#(#attrs)*
296296
#vis #sig {
297-
let _ = std::boxed::Box::new(0_usize);
297+
kani::internal::init_contracts();
298298
#block
299299
}
300300
)

0 commit comments

Comments
 (0)