Skip to content

Commit 49253b3

Browse files
adpaco-awstedinski
authored andcommitted
Audit for write_bytes (rust-lang#1102)
1 parent b8369e5 commit 49253b3

File tree

8 files changed

+132
-9
lines changed

8 files changed

+132
-9
lines changed

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

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -622,16 +622,9 @@ impl<'tcx> GotocCtx<'tcx> {
622622
"wrapping_mul" => codegen_wrapping_op!(mul),
623623
"wrapping_sub" => codegen_wrapping_op!(sub),
624624
"write_bytes" => {
625-
let dst = fargs.remove(0).cast_to(Type::void_pointer());
626-
let val = fargs.remove(0).cast_to(Type::c_int());
627-
let count = fargs.remove(0);
628-
let ty = self.monomorphize(instance.substs.type_at(0));
629-
let layout = self.layout_of(ty);
630-
let sz = Expr::int_constant(layout.size.bytes(), Type::size_t());
631-
let e = BuiltinFn::Memset.call(vec![dst, val, count.mul(sz)], loc);
632-
self.codegen_expr_to_place(p, e)
625+
assert!(self.place_ty(p).is_unit());
626+
self.codegen_write_bytes(instance, intrinsic, fargs, loc)
633627
}
634-
635628
// Unimplemented
636629
_ => codegen_unimplemented_intrinsic!(
637630
"https://github.com/model-checking/kani/issues/new/choose"
@@ -1188,4 +1181,48 @@ impl<'tcx> GotocCtx<'tcx> {
11881181
let expr = dst.dereference().assign(src, loc.clone());
11891182
Stmt::block(vec![align_check, expr], loc)
11901183
}
1184+
1185+
/// Sets `count * size_of::<T>()` bytes of memory starting at `dst` to `val`
1186+
/// https://doc.rust-lang.org/std/ptr/fn.write_bytes.html
1187+
///
1188+
/// Undefined behavior if any of these conditions are violated:
1189+
/// * `dst` must be valid for writes (done by memset writable check)
1190+
/// * `dst` must be properly aligned (done by `align_check` below)
1191+
/// In addition, we check that computing `bytes` (i.e., the third argument
1192+
/// for the `memset` call) would not overflow
1193+
fn codegen_write_bytes(
1194+
&mut self,
1195+
instance: Instance<'tcx>,
1196+
intrinsic: &str,
1197+
mut fargs: Vec<Expr>,
1198+
loc: Location,
1199+
) -> Stmt {
1200+
let dst = fargs.remove(0).cast_to(Type::void_pointer());
1201+
let val = fargs.remove(0).cast_to(Type::c_int());
1202+
let count = fargs.remove(0);
1203+
1204+
// Check that `dst` is properly aligned
1205+
let ty = self.monomorphize(instance.substs.type_at(0));
1206+
let align = self.is_aligned(ty, dst.clone());
1207+
let align_check = self.codegen_assert(
1208+
align,
1209+
PropertyClass::DefaultAssertion,
1210+
"`dst` is properly aligned",
1211+
loc,
1212+
);
1213+
1214+
// Check that computing `bytes` would not overflow
1215+
let layout = self.layout_of(ty);
1216+
let size = Expr::int_constant(layout.size.bytes(), Type::size_t());
1217+
let bytes = count.mul_overflow(size);
1218+
let overflow_check = self.codegen_assert(
1219+
bytes.overflowed.not(),
1220+
PropertyClass::ArithmeticOverflow,
1221+
format!("{}: attempt to compute `bytes` which would overflow", intrinsic).as_str(),
1222+
loc,
1223+
);
1224+
1225+
let memset_call = BuiltinFn::Memset.call(vec![dst, val, bytes.result], loc);
1226+
Stmt::block(vec![align_check, overflow_check, memset_call.as_stmt(loc)], loc)
1227+
}
11911228
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FAILURE\
2+
memset destination region writeable
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Checks that `write_bytes` fails if an out-of-bounds write is made.
5+
6+
// This test is a modified version of the example in
7+
// https://doc.rust-lang.org/std/ptr/fn.write_bytes.html
8+
#![feature(core_intrinsics)]
9+
use std::intrinsics::write_bytes;
10+
11+
#[kani::proof]
12+
fn main() {
13+
let mut vec = vec![0u32; 4];
14+
unsafe {
15+
let vec_ptr = vec.as_mut_ptr().add(4);
16+
write_bytes(vec_ptr, 0xfe, 1);
17+
}
18+
assert_eq!(vec, [0, 0, 0, 0]);
19+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FAILURE\
2+
write_bytes: attempt to compute `bytes` which would overflow
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Checks that `write_bytes` triggers the overflow check.
5+
6+
// This test is a modified version of the example in
7+
// https://doc.rust-lang.org/std/ptr/fn.write_bytes.html
8+
#![feature(core_intrinsics)]
9+
use std::intrinsics::write_bytes;
10+
11+
#[kani::proof]
12+
fn main() {
13+
let mut vec = vec![0u32; 4];
14+
unsafe {
15+
let vec_ptr = vec.as_mut_ptr();
16+
// Passing `usize::MAX + 1` is guaranteed to
17+
// overflow when computing the number of bytes
18+
write_bytes(vec_ptr, 0xfe, usize::MAX / 4 + 1);
19+
}
20+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FAILURE\
2+
`dst` is properly aligned
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Checks that `write_bytes` fails when `dst` is not aligned.
5+
6+
// This test is a modified version of the example in
7+
// https://doc.rust-lang.org/std/ptr/fn.write_bytes.html
8+
use std::intrinsics::write_bytes;
9+
10+
#[kani::proof]
11+
fn main() {
12+
let mut vec = vec![0u32; 4];
13+
unsafe {
14+
let vec_ptr = vec.as_mut_ptr();
15+
// Obtain an unaligned pointer by casting into `*mut u8`,
16+
// adding an offset of 1 and casting back into `*mut u32`
17+
let vec_ptr_u8: *mut u8 = vec_ptr as *mut u8;
18+
let unaligned_ptr = vec_ptr_u8.add(1) as *mut u32;
19+
write_bytes(unaligned_ptr, 0xfe, 2);
20+
}
21+
assert_eq!(vec, [0xfefefe00, 0xfefefefe, 0x000000fe, 0]);
22+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Checks that `write_bytes` works as expected.
5+
6+
// This test is a modified version of the example in
7+
// https://doc.rust-lang.org/std/ptr/fn.write_bytes.html
8+
#![feature(core_intrinsics)]
9+
use std::intrinsics::write_bytes;
10+
11+
#[kani::proof]
12+
fn main() {
13+
let mut vec = vec![0u32; 4];
14+
unsafe {
15+
let vec_ptr = vec.as_mut_ptr();
16+
write_bytes(vec_ptr, 0xfe, 2);
17+
}
18+
assert_eq!(vec, [0xfefefefe, 0xfefefefe, 0, 0]);
19+
}

0 commit comments

Comments
 (0)