Skip to content

Commit b329c85

Browse files
authored
Implement check for write_bytes (rust-lang#3108)
In the previous PR rust-lang#3085, we did not support checks for `write_bytes` which is added in this PR. I am waiting for rust-lang#3092 to add expected tests.
1 parent 81e11ba commit b329c85

File tree

3 files changed

+62
-1
lines changed

3 files changed

+62
-1
lines changed

kani-compiler/src/kani_middle/transform/check_values.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,13 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> {
440440
// The write bytes intrinsic may trigger UB in safe code.
441441
// pub unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize)
442442
// <https://doc.rust-lang.org/stable/core/intrinsics/fn.write_bytes.html>
443-
// We don't support this operation yet.
443+
// This is an over-approximation since writing an invalid value is
444+
// not UB, only reading it will be.
445+
assert_eq!(
446+
args.len(),
447+
3,
448+
"Unexpected number of arguments for `write_bytes`"
449+
);
444450
let TyKind::RigidTy(RigidTy::RawPtr(target_ty, Mutability::Mut)) =
445451
args[0].ty(self.locals).unwrap().kind()
446452
else {
@@ -449,6 +455,19 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> {
449455
let validity = ty_validity_per_offset(&self.machine, target_ty, 0);
450456
match validity {
451457
Ok(ranges) if ranges.is_empty() => {}
458+
Ok(ranges) => {
459+
let sz = Const::try_from_uint(
460+
target_ty.layout().unwrap().shape().size.bytes()
461+
as u128,
462+
UintTy::Usize,
463+
)
464+
.unwrap();
465+
self.push_target(SourceOp::BytesValidity {
466+
target_ty,
467+
rvalue: Rvalue::Repeat(args[1].clone(), sz),
468+
ranges,
469+
})
470+
}
452471
_ => self.push_target(SourceOp::UnsupportedCheck {
453472
check: "write_bytes".to_string(),
454473
ty: target_ty,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-flags: -Z valid-value-checks
4+
//! Check that Kani can identify UB when converting from a maybe uninit().
5+
6+
use std::mem::MaybeUninit;
7+
use std::num::NonZeroI64;
8+
9+
#[kani::proof]
10+
pub fn check_valid_zeroed() {
11+
let maybe = MaybeUninit::zeroed();
12+
let val: u128 = unsafe { maybe.assume_init() };
13+
assert_eq!(val, 0);
14+
}
15+
16+
#[kani::proof]
17+
#[kani::should_panic]
18+
pub fn check_invalid_zeroed() {
19+
let maybe = MaybeUninit::zeroed();
20+
let _val: NonZeroI64 = unsafe { maybe.assume_init() };
21+
}

tests/kani/ValidValues/write_bytes.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-flags: -Z valid-value-checks
4+
//! Check that Kani can identify UB when using write_bytes for initializing a variable.
5+
#![feature(core_intrinsics)]
6+
7+
#[kani::proof]
8+
#[kani::should_panic]
9+
pub fn check_invalid_write() {
10+
let mut val = 'a';
11+
let ptr = &mut val as *mut char;
12+
// Should fail given that we wrote invalid value to array of char.
13+
unsafe { std::intrinsics::write_bytes(ptr, kani::any(), 1) };
14+
}
15+
16+
#[kani::proof]
17+
pub fn check_valid_write() {
18+
let mut val = 10u128;
19+
let ptr = &mut val as *mut _;
20+
unsafe { std::intrinsics::write_bytes(ptr, kani::any(), 1) };
21+
}

0 commit comments

Comments
 (0)