Skip to content

Commit fb6300d

Browse files
Add method to assert a pointer is valid (#3062)
This new method will assert that a pointer isn't null, and that its is valid for access of a given type. Co-authored-by: Zyad Hassan <[email protected]>
1 parent e004ecc commit fb6300d

File tree

11 files changed

+564
-19
lines changed

11 files changed

+564
-19
lines changed

cprover_bindings/src/goto_program/expr.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ pub enum ExprValue {
126126
Nondet,
127127
/// `NULL`
128128
PointerConstant(u64),
129+
ReadOk {
130+
ptr: Expr,
131+
size: Expr,
132+
},
129133
// `op++` etc
130134
SelfOp {
131135
op: SelfOperator,
@@ -717,6 +721,14 @@ impl Expr {
717721
expr!(Nondet, typ)
718722
}
719723

724+
/// `read_ok(ptr, size)`
725+
pub fn read_ok(ptr: Expr, size: Expr) -> Self {
726+
assert_eq!(*ptr.typ(), Type::void_pointer());
727+
assert_eq!(*size.typ(), Type::size_t());
728+
729+
expr!(ReadOk { ptr, size }, Type::bool())
730+
}
731+
720732
/// `e.g. NULL`
721733
pub fn pointer_constant(c: u64, typ: Type) -> Self {
722734
assert!(typ.is_pointer());

cprover_bindings/src/irep/to_irep.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,11 @@ impl ToIrep for ExprValue {
293293
Irep::just_bitpattern_id(*i, mm.pointer_width, false)
294294
)],
295295
},
296+
ExprValue::ReadOk { ptr, size } => Irep {
297+
id: IrepId::ROk,
298+
sub: vec![ptr.to_irep(mm), size.to_irep(mm)],
299+
named_sub: linear_map![],
300+
},
296301
ExprValue::SelfOp { op, e } => side_effect_irep(op.to_irep_id(), vec![e.to_irep(mm)]),
297302
ExprValue::StatementExpression { statements: ops } => side_effect_irep(
298303
IrepId::StatementExpression,

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,41 @@ impl GotocHook for Panic {
217217
}
218218
}
219219

220+
/// Encodes __CPROVER_r_ok
221+
struct IsReadOk;
222+
impl GotocHook for IsReadOk {
223+
fn hook_applies(&self, tcx: TyCtxt, instance: Instance) -> bool {
224+
matches_function(tcx, instance, "KaniIsReadOk")
225+
}
226+
227+
fn handle(
228+
&self,
229+
gcx: &mut GotocCtx,
230+
_instance: Instance,
231+
mut fargs: Vec<Expr>,
232+
assign_to: &Place,
233+
target: Option<BasicBlockIdx>,
234+
span: Span,
235+
) -> Stmt {
236+
assert_eq!(fargs.len(), 2);
237+
let size = fargs.pop().unwrap();
238+
let ptr = fargs.pop().unwrap().cast_to(Type::void_pointer());
239+
let target = target.unwrap();
240+
let loc = gcx.codegen_caller_span_stable(span);
241+
let ret_place =
242+
unwrap_or_return_codegen_unimplemented_stmt!(gcx, gcx.codegen_place_stable(assign_to));
243+
let ret_type = ret_place.goto_expr.typ().clone();
244+
245+
Stmt::block(
246+
vec![
247+
ret_place.goto_expr.assign(Expr::read_ok(ptr, size).cast_to(ret_type), loc),
248+
Stmt::goto(bb_label(target), Location::none()),
249+
],
250+
Location::none(),
251+
)
252+
}
253+
}
254+
220255
struct RustAlloc;
221256
// Removing this hook causes regression failures.
222257
// https://github.com/model-checking/kani/issues/1170
@@ -373,6 +408,7 @@ pub fn fn_hooks() -> GotocHooks {
373408
Rc::new(Assert),
374409
Rc::new(Cover),
375410
Rc::new(Nondet),
411+
Rc::new(IsReadOk),
376412
Rc::new(RustAlloc),
377413
Rc::new(MemCmp),
378414
Rc::new(UntrackedDeref),

kani_metadata/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ pub enum UnstableFeature {
8282
LineCoverage,
8383
/// Enable function contracts [RFC 9](https://model-checking.github.io/kani/rfc/rfcs/0009-function-contracts.html)
8484
FunctionContracts,
85+
/// Memory predicate APIs.
86+
MemPredicates,
8587
}
8688

8789
impl UnstableFeature {

library/kani/src/lib.rs

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@
1414
#![feature(repr_simd)]
1515
// Features used for tests only.
1616
#![cfg_attr(test, feature(core_intrinsics, portable_simd))]
17-
// Required for rustc_diagnostic_item
17+
// Required for `rustc_diagnostic_item` and `core_intrinsics`
1818
#![allow(internal_features)]
19+
// Required for implementing memory predicates.
20+
#![feature(ptr_metadata)]
1921

2022
pub mod arbitrary;
2123
#[cfg(feature = "concrete_playback")]
2224
mod concrete_playback;
2325
pub mod futures;
26+
pub mod mem;
2427
pub mod slice;
2528
pub mod tuple;
2629
pub mod vec;
@@ -33,6 +36,7 @@ mod models;
3336
pub use arbitrary::Arbitrary;
3437
#[cfg(feature = "concrete_playback")]
3538
pub use concrete_playback::concrete_playback_run;
39+
3640
#[cfg(not(feature = "concrete_playback"))]
3741
/// NOP `concrete_playback` for type checking during verification mode.
3842
pub fn concrete_playback_run<F: Fn()>(_: Vec<Vec<u8>>, _: F) {
@@ -80,18 +84,12 @@ pub fn assume(cond: bool) {
8084
/// `implies!(premise => conclusion)` means that if the `premise` is true, so
8185
/// must be the `conclusion`.
8286
///
83-
/// This simply expands to `!premise || conclusion` and is intended to be used
84-
/// in function contracts to make them more readable, as the concept of an
85-
/// implication is more natural to think about than its expansion.
86-
///
87-
/// For further convenience multiple comma separated premises are allowed, and
88-
/// are joined with `||` in the expansion. E.g. `implies!(a, b => c)` expands to
89-
/// `!a || !b || c` and says that `c` is true if both `a` and `b` are true (see
90-
/// also [Horn Clauses](https://en.wikipedia.org/wiki/Horn_clause)).
87+
/// This simply expands to `!premise || conclusion` and is intended to make checks more readable,
88+
/// as the concept of an implication is more natural to think about than its expansion.
9189
#[macro_export]
9290
macro_rules! implies {
93-
($($premise:expr),+ => $conclusion:expr) => {
94-
$(!$premise)||+ || ($conclusion)
91+
($premise:expr => $conclusion:expr) => {
92+
!($premise) || ($conclusion)
9593
};
9694
}
9795

@@ -217,13 +215,7 @@ pub(crate) unsafe fn any_raw_internal<T, const SIZE_T: usize>() -> T {
217215
#[inline(never)]
218216
#[allow(dead_code)]
219217
fn any_raw_inner<T>() -> T {
220-
// while we could use `unreachable!()` or `panic!()` as the body of this
221-
// function, both cause Kani to produce a warning on any program that uses
222-
// kani::any() (see https://github.com/model-checking/kani/issues/2010).
223-
// This function is handled via a hook anyway, so we just need to put a body
224-
// that rustc does not complain about. An infinite loop works out nicely.
225-
#[allow(clippy::empty_loop)]
226-
loop {}
218+
kani_intrinsic()
227219
}
228220

229221
/// Function used to generate panic with a static message as this is the only one currently
@@ -239,6 +231,20 @@ pub const fn panic(message: &'static str) -> ! {
239231
panic!("{}", message)
240232
}
241233

234+
/// An empty body that can be used to define Kani intrinsic functions.
235+
///
236+
/// A Kani intrinsic is a function that is interpreted by Kani compiler.
237+
/// While we could use `unreachable!()` or `panic!()` as the body of a kani intrinsic
238+
/// function, both cause Kani to produce a warning since we don't support caller location.
239+
/// (see https://github.com/model-checking/kani/issues/2010).
240+
///
241+
/// This function is dead, since its caller is always handled via a hook anyway,
242+
/// so we just need to put a body that rustc does not complain about.
243+
/// An infinite loop works out nicely.
244+
fn kani_intrinsic<T>() -> T {
245+
#[allow(clippy::empty_loop)]
246+
loop {}
247+
}
242248
/// A macro to check if a condition is satisfiable at a specific location in the
243249
/// code.
244250
///

0 commit comments

Comments
 (0)