Skip to content

Commit 2e95d8b

Browse files
authored
Remove isize overflow check for zst offsets (#3897)
#3755 introduced additional safety checks for the `offset` intrinsic, including a check for whether `count` overflows `isize`. However, such overflow is allowed for ZSTs. This PR changes the model to check for ZSTs before computing the offset to avoid triggering an overflow failure for ZSTs. I also moved an existing test called `offset-overflow` into another test, since #3755 changed the failure for that test to be about an out of bounds allocation, not an isize overflow. Resolves #3896 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.
1 parent 51de000 commit 2e95d8b

File tree

7 files changed

+74
-45
lines changed

7 files changed

+74
-45
lines changed

library/kani_core/src/models.rs

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -99,29 +99,35 @@ macro_rules! generate_models {
9999
/// An offset model that checks UB.
100100
#[kanitool::fn_marker = "OffsetModel"]
101101
pub fn offset<T, P: Ptr<T>, O: ToISize>(ptr: P, offset: O) -> P {
102-
let offset = offset.to_isize();
103102
let t_size = core::mem::size_of::<T>() as isize;
104-
if offset == 0 || t_size == 0 {
103+
if t_size == 0 {
104+
// It's always safe to perform an offset on a ZST.
105+
return ptr;
106+
}
107+
108+
// Note that this check must come after the t_size check, c.f. https://github.com/model-checking/kani/issues/3896
109+
let offset = offset.to_isize();
110+
if offset == 0 {
105111
// It's always safe to perform an offset of length 0.
106-
ptr
107-
} else {
108-
let (byte_offset, overflow) = offset.overflowing_mul(t_size);
109-
kani::safety_check(!overflow, "Offset in bytes overflows isize");
110-
let orig_ptr = ptr.to_const_ptr();
111-
// NOTE: For CBMC, using the pointer addition can have unexpected behavior
112-
// when the offset is higher than the object bits since it will wrap around.
113-
// See for more details: https://github.com/model-checking/kani/issues/1150
114-
//
115-
// However, when I tried implementing this using usize operation, we got some
116-
// unexpected failures that still require further debugging.
117-
// let new_ptr = orig_ptr.addr().wrapping_add_signed(byte_offset) as *const T;
118-
let new_ptr = orig_ptr.wrapping_byte_offset(byte_offset);
119-
kani::safety_check(
120-
kani::mem::same_allocation_internal(orig_ptr, new_ptr),
121-
"Offset result and original pointer must point to the same allocation",
122-
);
123-
P::from_const_ptr(new_ptr)
112+
return ptr;
124113
}
114+
115+
let (byte_offset, overflow) = offset.overflowing_mul(t_size);
116+
kani::safety_check(!overflow, "Offset in bytes overflows isize");
117+
let orig_ptr = ptr.to_const_ptr();
118+
// NOTE: For CBMC, using the pointer addition can have unexpected behavior
119+
// when the offset is higher than the object bits since it will wrap around.
120+
// See for more details: https://github.com/model-checking/kani/issues/1150
121+
//
122+
// However, when I tried implementing this using usize operation, we got some
123+
// unexpected failures that still require further debugging.
124+
// let new_ptr = orig_ptr.addr().wrapping_add_signed(byte_offset) as *const T;
125+
let new_ptr = orig_ptr.wrapping_byte_offset(byte_offset);
126+
kani::safety_check(
127+
kani::mem::same_allocation_internal(orig_ptr, new_ptr),
128+
"Offset result and original pointer must point to the same allocation",
129+
);
130+
P::from_const_ptr(new_ptr)
125131
}
126132

127133
pub trait Ptr<T> {
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
Failed Checks: Offset result and original pointer must point to the same allocation
2-
Verification failed for - check_ptr_oob
2+
Failed Checks: Offset result and original pointer must point to the same allocation
33

4-
Complete - 1 successfully verified harnesses, 1 failures, 2 total.
4+
Verification failed for - test_offset_overflow
5+
Verification failed for - check_ptr_oob
6+
Complete - 1 successfully verified harnesses, 2 failures, 3 total.

tests/expected/offset-bounds-check/out_of_bounds_ub_check.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33
//! Check that Kani offset operations correctly detect out-of-bound access.
44
5+
#![feature(core_intrinsics)]
6+
use std::intrinsics::offset;
7+
58
/// Verification should fail because safety violation is not a regular panic.
69
#[kani::proof]
710
#[kani::should_panic]
@@ -24,3 +27,15 @@ fn check_ptr_end() {
2427
// Safety: Pointers point to the same allocation
2528
assert_eq!(unsafe { end_ptr.offset_from(base_ptr) }, 1);
2629
}
30+
31+
#[kani::proof]
32+
fn test_offset_overflow() {
33+
let s: &str = "123";
34+
let ptr: *const u8 = s.as_ptr();
35+
36+
unsafe {
37+
// This should fail because adding `isize::MAX` to `ptr` would overflow
38+
// `isize`
39+
let _d = offset(ptr, isize::MAX);
40+
}
41+
}

tests/expected/offset-overflow/expected

Lines changed: 0 additions & 4 deletions
This file was deleted.

tests/expected/offset-overflow/main.rs

Lines changed: 0 additions & 19 deletions
This file was deleted.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<usize as kani::rustc_intrinsics::ToISize>::to_isize.safety_check\
2+
- Status: FAILURE\
3+
- Description: "Offset value overflows isize"
4+
5+
Failed Checks: Offset value overflows isize
6+
7+
Verification failed for - test_non_zst
8+
Complete - 1 successfully verified harnesses, 1 failures, 2 total.
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+
4+
// Checks that `offset` does not accept a `count` greater than isize::MAX,
5+
// except for ZSTs, c.f. https://github.com/model-checking/kani/issues/3896
6+
7+
#[kani::proof]
8+
fn test_zst() {
9+
let mut x = ();
10+
let ptr: *mut () = &mut x as *mut ();
11+
let count: usize = (isize::MAX as usize) + 1;
12+
let _ = unsafe { ptr.add(count) };
13+
}
14+
15+
#[kani::proof]
16+
fn test_non_zst() {
17+
let mut x = 7;
18+
let ptr: *mut i32 = &mut x as *mut i32;
19+
let count: usize = (isize::MAX as usize) + 1;
20+
let _ = unsafe { ptr.add(count) };
21+
}

0 commit comments

Comments
 (0)