Skip to content

Commit 6ffdbc2

Browse files
adpaco-awstedinski
authored andcommitted
Audit for ptr_offset_from (rust-lang#1099)
1 parent 571e269 commit 6ffdbc2

File tree

9 files changed

+55
-21
lines changed

9 files changed

+55
-21
lines changed

cprover_bindings/src/goto_program/expr.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ pub enum BinaryOperand {
181181
OverflowMult,
182182
OverflowPlus,
183183
Plus,
184+
ROk,
184185
Rol,
185186
Ror,
186187
Shl,
@@ -873,6 +874,7 @@ impl Expr {
873874
(lhs.typ == rhs.typ && lhs.typ.is_integer())
874875
|| (lhs.typ.is_pointer() && rhs.typ.is_integer())
875876
}
877+
ROk => lhs.typ.is_pointer() && rhs.typ.is_c_size_t(),
876878
}
877879
}
878880

@@ -914,6 +916,7 @@ impl Expr {
914916
IeeeFloatEqual | IeeeFloatNotequal => Type::bool(),
915917
// Overflow flags
916918
OverflowMinus | OverflowMult | OverflowPlus => Type::bool(),
919+
ROk => Type::bool(),
917920
}
918921
}
919922
/// self op right;
@@ -1072,6 +1075,11 @@ impl Expr {
10721075
pub fn ror(self, e: Expr) -> Expr {
10731076
self.binop(Ror, e)
10741077
}
1078+
1079+
/// `__CPROVER_r_ok(self, e)`
1080+
pub fn r_ok(self, e: Expr) -> Expr {
1081+
self.binop(ROk, e)
1082+
}
10751083
}
10761084

10771085
/// Constructors for self operations

cprover_bindings/src/irep/to_irep.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ impl ToIrepId for BinaryOperand {
7676
BinaryOperand::OverflowMult => IrepId::OverflowMult,
7777
BinaryOperand::OverflowPlus => IrepId::OverflowPlus,
7878
BinaryOperand::Plus => IrepId::Plus,
79+
BinaryOperand::ROk => IrepId::ROk,
7980
BinaryOperand::Rol => IrepId::Rol,
8081
BinaryOperand::Ror => IrepId::Ror,
8182
BinaryOperand::Shl => IrepId::Shl,

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

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ impl<'tcx> GotocCtx<'tcx> {
523523
"pref_align_of" => codegen_intrinsic_const!(),
524524
"ptr_guaranteed_eq" => codegen_ptr_guaranteed_cmp!(eq),
525525
"ptr_guaranteed_ne" => codegen_ptr_guaranteed_cmp!(neq),
526-
"ptr_offset_from" => self.codegen_ptr_offset_from(fargs, p, loc),
526+
"ptr_offset_from" => self.codegen_ptr_offset_from(instance, fargs, p, loc),
527527
"raw_eq" => self.codegen_intrinsic_raw_eq(instance, fargs, p, loc),
528528
"rintf32" => codegen_unimplemented_intrinsic!(
529529
"https://github.com/model-checking/kani/issues/1025"
@@ -905,26 +905,34 @@ impl<'tcx> GotocCtx<'tcx> {
905905
/// https://doc.rust-lang.org/std/intrinsics/fn.ptr_offset_from.html
906906
fn codegen_ptr_offset_from(
907907
&mut self,
908+
instance: Instance<'tcx>,
908909
mut fargs: Vec<Expr>,
909910
p: &Place<'tcx>,
910911
loc: Location,
911912
) -> Stmt {
912-
let a = fargs.remove(0);
913-
let b = fargs.remove(0);
914-
let pointers_to_same_object = a.clone().same_object(b.clone());
913+
let dst_ptr = fargs.remove(0);
914+
let src_ptr = fargs.remove(0);
915915

916-
Stmt::block(
917-
vec![
918-
self.codegen_assert(
919-
pointers_to_same_object,
920-
PropertyClass::PointerOffset,
921-
"ptr_offset_from: pointers point to same object",
922-
loc.clone(),
923-
),
924-
self.codegen_expr_to_place(p, a.sub(b)),
925-
],
916+
// Compute the offset with standard substraction using `isize`
917+
let cast_dst_ptr = dst_ptr.clone().cast_to(Type::ssize_t());
918+
let cast_src_ptr = src_ptr.clone().cast_to(Type::ssize_t());
919+
let offset = cast_dst_ptr.sub(cast_src_ptr);
920+
921+
// Check that computing `offset_bytes` would not overflow an `isize`
922+
let ty = self.monomorphize(instance.substs.type_at(0));
923+
let layout = self.layout_of(ty);
924+
let size = Expr::int_constant(layout.size.bytes(), Type::ssize_t());
925+
let offset_bytes = offset.clone().mul_overflow(size);
926+
let overflow_check = self.codegen_assert(
927+
offset_bytes.overflowed.not(),
928+
PropertyClass::ArithmeticOverflow,
929+
"attempt to compute offset in bytes which would overflow",
926930
loc,
927-
)
931+
);
932+
933+
// Re-compute the offset with standard substraction (no casts this time)
934+
let offset_expr = self.codegen_expr_to_place(p, dst_ptr.sub(src_ptr));
935+
Stmt::block(vec![overflow_check, offset_expr], loc)
928936
}
929937

930938
/// A transmute is a bitcast from the argument type to the return type.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FAILURE\
2+
"same object violation"
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
3-
// kani-verify-fail
43

4+
// Checks that `ptr_offset_from` fails if the object that the arguments
5+
// point to are not the same.
56
#![feature(core_intrinsics)]
67
use std::intrinsics::ptr_offset_from;
78

@@ -12,11 +13,6 @@ fn main() {
1213
let ptr1: *const i32 = &a[1];
1314
let ptr2: *const i32 = &b[3];
1415
unsafe {
15-
// Offset operations result in Undefined Behavior if
16-
// some conditions are violated. More info at:
17-
// https://doc.rust-lang.org/std/primitive.pointer.html#method.offset_from
18-
// In particular, the below call to `ptr_offset_from` is expected
19-
// to fail because `ptr1` and `ptr2` point to different objects.
2016
let offset = ptr_offset_from(ptr2, ptr1);
2117
}
2218
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FAILURE\
2+
attempt to compute offset in bytes which would overflow
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Check that an offset computed with `offset_from` triggers a verification failure
5+
// if it overflows an `isize` in bytes.
6+
use std::convert::TryInto;
7+
8+
#[kani::proof]
9+
fn main() {
10+
let v: &[u128] = &[0; 200];
11+
let v_100: *const u128 = &v[100];
12+
let max_offset = usize::MAX / std::mem::size_of::<u128>();
13+
unsafe {
14+
let v_wrap: *const u128 = v_100.add((max_offset + 1).try_into().unwrap());
15+
let _ = v_wrap.offset_from(v_100) == 2;
16+
}
17+
}

0 commit comments

Comments
 (0)