Skip to content

Commit c18754d

Browse files
committed
Fix regression
1 parent 5f4a69b commit c18754d

File tree

6 files changed

+105
-46
lines changed

6 files changed

+105
-46
lines changed

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,11 +505,27 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> {
505505
// We only care about *mut T as *mut U
506506
return;
507507
};
508+
if dest_pointee_ty.kind().is_unit() {
509+
// Ignore cast to *mut () since nothing can be written to it.
510+
// This is a common pattern
511+
return;
512+
}
513+
508514
let src_ty = op.ty(self.locals).unwrap();
509515
debug!(?src_ty, ?dest_ty, "visit_rvalue mutcast");
510516
let TyKind::RigidTy(RigidTy::RawPtr(src_pointee_ty, _)) = src_ty.kind() else {
511517
unreachable!()
512518
};
519+
520+
if src_pointee_ty.kind().is_unit() {
521+
// We cannot track what was the initial type. Thus, fail.
522+
self.push_target(SourceOp::UnsupportedCheck {
523+
check: "mutable cast".to_string(),
524+
ty: src_ty,
525+
});
526+
return;
527+
}
528+
513529
if let Ok(src_validity) =
514530
ty_validity_per_offset(&self.machine, src_pointee_ty, 0)
515531
{
@@ -558,6 +574,8 @@ impl<'a> MirVisitor for CheckValueVisitor<'a> {
558574
})
559575
}
560576
}
577+
// `DynStar` is not currently supported in Kani.
578+
// TODO: https://github.com/model-checking/kani/issues/1784
561579
CastKind::DynStar => self.push_target(UnsupportedCheck {
562580
check: "Dyn*".to_string(),
563581
ty: (rvalue.ty(self.locals).unwrap()),

library/kani/src/mem.rs

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,23 @@ where
6363
T: ?Sized,
6464
<T as Pointee>::Metadata: PtrProperties<T>,
6565
{
66+
// The interface takes a mutable pointer to improve readability of the signature.
67+
// However, using constant pointer avoid unnecessary instrumentation, and it is as powerful.
68+
// Hence, cast to `*const T`.
69+
let ptr: *const T = ptr;
6670
let (thin_ptr, metadata) = ptr.to_raw_parts();
6771
metadata.is_ptr_aligned(thin_ptr, Internal) && is_inbounds(&metadata, thin_ptr)
6872
}
6973

70-
/// Assert that the pointer is valid for access according to [crate::mem] conditions 1, 2 and 3.
74+
/// Check if the pointer is valid for unaligned write access according to [crate::mem] conditions
75+
/// 1, 2 and 3.
7176
///
7277
/// Note this function succeeds for unaligned pointers. See [self::can_write] if you also
7378
/// want to check pointer alignment.
7479
///
75-
/// This function will either panic or return `true`. This is to make it easier to use it in
80+
/// This function will panic today if the pointer is not null, and it points to an unallocated or
81+
/// deallocated memory location. This is an existing Kani limitation.
82+
/// See <https://github.com/model-checking/kani/issues/2690> for more details.
7683
#[crate::unstable(
7784
feature = "mem-predicates",
7885
issue = 2690,
@@ -96,7 +103,9 @@ where
96103
/// TODO: Kani should automatically add those checks when a de-reference happens.
97104
/// <https://github.com/model-checking/kani/issues/2975>
98105
///
99-
/// Invoking this with an invalid pointer will panic due to a memory violation error.
106+
/// This function will panic today if the pointer is not null, and it points to an unallocated or
107+
/// deallocated memory location. This is an existing Kani limitation.
108+
/// See <https://github.com/model-checking/kani/issues/2690> for more details.
100109
#[crate::unstable(
101110
feature = "mem-predicates",
102111
issue = 2690,
@@ -114,6 +123,33 @@ where
114123
&& unsafe { has_valid_value(ptr) }
115124
}
116125

126+
/// Checks that pointer `ptr` point to a valid value of type `T`.
127+
///
128+
/// For that, the pointer has to be a valid pointer according to [crate::mem] conditions 1, 2
129+
/// and 3,
130+
/// and the value stored must respect the validity invariants for type `T`.
131+
///
132+
/// Note this function succeeds for unaligned pointers. See [self::can_dereference] if you also
133+
/// want to check pointer alignment.
134+
///
135+
/// This function will panic today if the pointer is not null, and it points to an unallocated or
136+
/// deallocated memory location. This is an existing Kani limitation.
137+
/// See <https://github.com/model-checking/kani/issues/2690> for more details.
138+
#[crate::unstable(
139+
feature = "mem-predicates",
140+
issue = 2690,
141+
reason = "experimental memory predicate API"
142+
)]
143+
#[allow(clippy::not_unsafe_ptr_arg_deref)]
144+
pub fn can_read_unaligned<T>(ptr: *const T) -> bool
145+
where
146+
T: ?Sized,
147+
<T as Pointee>::Metadata: PtrProperties<T>,
148+
{
149+
let (thin_ptr, metadata) = ptr.to_raw_parts();
150+
is_inbounds(&metadata, thin_ptr) && unsafe { has_valid_value(ptr) }
151+
}
152+
117153
/// Checks that `data_ptr` points to an allocation that can hold data of size calculated from `T`.
118154
///
119155
/// This will panic if `data_ptr` points to an invalid `non_null`
@@ -244,6 +280,10 @@ unsafe fn is_allocated(_ptr: *const (), _size: usize) -> bool {
244280
}
245281

246282
/// Check if the value stored in the given location satisfies type `T` validity requirements.
283+
///
284+
/// # Safety
285+
///
286+
/// - Users have to ensure that the pointer is aligned the pointed memory is allocated.
247287
#[rustc_diagnostic_item = "KaniValidValue"]
248288
#[inline(never)]
249289
unsafe fn has_valid_value<T: ?Sized>(_ptr: *const T) -> bool {
@@ -252,7 +292,7 @@ unsafe fn has_valid_value<T: ?Sized>(_ptr: *const T) -> bool {
252292

253293
#[cfg(test)]
254294
mod tests {
255-
use super::{can_write, PtrProperties};
295+
use super::{can_dereference, can_write, PtrProperties};
256296
use crate::mem::private::Internal;
257297
use std::fmt::Debug;
258298
use std::intrinsics::size_of;
@@ -306,39 +346,42 @@ mod tests {
306346

307347
#[test]
308348
pub fn test_empty_slice() {
309-
let slice_ptr = Vec::<char>::new().as_slice() as *const [char];
310-
can_write(slice_ptr);
349+
let slice_ptr = Vec::<char>::new().as_mut_slice() as *mut [char];
350+
assert!(can_write(slice_ptr));
311351
}
312352

313353
#[test]
314354
pub fn test_empty_str() {
315-
let slice_ptr = String::new().as_str() as *const str;
316-
can_write(slice_ptr);
355+
let slice_ptr = String::new().as_mut_str() as *mut str;
356+
assert!(can_write(slice_ptr));
317357
}
318358

319359
#[test]
320360
fn test_dangling_zst() {
321-
test_dangling_of_t::<()>();
322-
test_dangling_of_t::<[(); 10]>();
361+
test_dangling_of_zst::<()>();
362+
test_dangling_of_zst::<[(); 10]>();
323363
}
324364

325-
fn test_dangling_of_t<T>() {
326-
let dangling: *const T = NonNull::<T>::dangling().as_ptr();
327-
can_write(dangling);
365+
fn test_dangling_of_zst<T>() {
366+
let dangling: *mut T = NonNull::<T>::dangling().as_ptr();
367+
assert!(can_write(dangling));
328368

329-
let vec_ptr = Vec::<T>::new().as_ptr();
330-
can_write(vec_ptr);
369+
let vec_ptr = Vec::<T>::new().as_mut_ptr();
370+
assert!(can_write(vec_ptr));
331371
}
332372

333373
#[test]
334-
#[should_panic(expected = "Expected valid pointer, but found `null`")]
335374
fn test_null_fat_ptr() {
336-
can_write(ptr::null::<char>() as *const dyn Debug);
375+
assert!(!can_dereference(ptr::null::<char>() as *const dyn Debug));
337376
}
338377

339378
#[test]
340-
#[should_panic(expected = "Expected valid pointer, but found `null`")]
341379
fn test_null_char() {
342-
can_write(ptr::null::<char>());
380+
assert!(!can_dereference(ptr::null::<char>()));
381+
}
382+
383+
#[test]
384+
fn test_null_mut() {
385+
assert!(!can_write(ptr::null_mut::<String>()));
343386
}
344387
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
Checking harness pre_condition::harness_invalid_ptr...
2-
Failed Checks: Expected valid pointer, but found dangling pointer
2+
Failed Checks: Kani does not support reasoning about pointer to unallocated memory
3+
VERIFICATION:- SUCCESSFUL (encountered one or more panics as expected)
34

45
Checking harness pre_condition::harness_stack_ptr...
56
VERIFICATION:- SUCCESSFUL
67

78
Checking harness pre_condition::harness_head_ptr...
89
VERIFICATION:- SUCCESSFUL
910

10-
Verification failed for - pre_condition::harness_invalid_ptr
11-
Complete - 2 successfully verified harnesses, 1 failures, 3 total
11+
Complete - 3 successfully verified harnesses, 0 failures, 3 total

tests/expected/function-contract/valid_ptr.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33
// kani-flags: -Zfunction-contracts -Zmem-predicates
44

5-
//! Test that it is sound to use `assert_valid_ptr` inside a contract pre-condition.
5+
//! Test that it is sound to use memory predicates inside a contract pre-condition.
66
//! We cannot validate post-condition yet. This can be done once we fix:
77
//! <https://github.com/model-checking/kani/issues/2997>
8-
#![feature(pointer_is_aligned)]
98
109
mod pre_condition {
1110
/// This contract should succeed only if the input is a valid pointer.
12-
#[kani::requires(kani::mem::assert_valid_ptr(ptr) && ptr.is_aligned())]
11+
#[kani::requires(kani::mem::can_dereference(ptr))]
1312
unsafe fn read_ptr(ptr: *const i32) -> i32 {
1413
*ptr
1514
}
@@ -29,6 +28,7 @@ mod pre_condition {
2928
}
3029

3130
#[kani::proof_for_contract(read_ptr)]
31+
#[kani::should_panic]
3232
fn harness_invalid_ptr() {
3333
let ptr = std::ptr::NonNull::<i32>::dangling().as_ptr();
3434
assert_eq!(unsafe { read_ptr(ptr) }, -20);
@@ -40,7 +40,7 @@ mod pre_condition {
4040
mod post_condition {
4141

4242
/// This contract should fail since we are creating a dangling pointer.
43-
#[kani::ensures(kani::mem::assert_valid_ptr(result.0))]
43+
#[kani::ensures(kani::mem::can_dereference(result.0))]
4444
unsafe fn new_invalid_ptr() -> PtrWrapper<char> {
4545
let var = 'c';
4646
PtrWrapper(&var as *const _)

tests/kani/MemPredicates/fat_ptr_validity.rs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,33 @@
55
66
extern crate kani;
77

8-
use kani::mem::assert_valid_ptr;
8+
use kani::mem::{can_dereference, can_write};
99

1010
mod valid_access {
1111
use super::*;
1212
#[kani::proof]
1313
pub fn check_valid_dyn_ptr() {
14-
let boxed: Box<dyn PartialEq<u8>> = Box::new(10u8);
15-
let raw_ptr = Box::into_raw(boxed);
16-
assert_valid_ptr(raw_ptr);
17-
let boxed = unsafe { Box::from_raw(raw_ptr) };
18-
assert_ne!(*boxed, 0);
14+
let mut var = 10u8;
15+
let fat_ptr: *mut dyn PartialEq<u8> = &mut var as *mut _;
16+
assert!(can_write(fat_ptr));
1917
}
2018

2119
#[kani::proof]
2220
pub fn check_valid_slice_ptr() {
2321
let array = ['a', 'b', 'c'];
2422
let slice = &array as *const [char];
25-
assert_valid_ptr(slice);
23+
assert!(can_dereference(slice));
2624
assert_eq!(unsafe { &*slice }[0], 'a');
2725
assert_eq!(unsafe { &*slice }[2], 'c');
2826
}
2927

3028
#[kani::proof]
3129
pub fn check_valid_zst() {
3230
let slice_ptr = Vec::<char>::new().as_slice() as *const [char];
33-
assert_valid_ptr(slice_ptr);
31+
assert!(can_dereference(slice_ptr));
3432

3533
let str_ptr = String::new().as_str() as *const str;
36-
assert_valid_ptr(str_ptr);
34+
assert!(can_dereference(str_ptr));
3735
}
3836
}
3937

@@ -43,14 +41,14 @@ mod invalid_access {
4341
#[kani::should_panic]
4442
pub fn check_invalid_dyn_ptr() {
4543
let raw_ptr: *const dyn PartialEq<u8> = unsafe { new_dead_ptr::<u8>(0) };
46-
assert_valid_ptr(raw_ptr);
44+
assert!(can_dereference(raw_ptr));
4745
}
4846

4947
#[kani::proof]
5048
#[kani::should_panic]
5149
pub fn check_invalid_slice_ptr() {
5250
let raw_ptr: *const [char] = unsafe { new_dead_ptr::<[char; 2]>(['a', 'b']) };
53-
assert_valid_ptr(raw_ptr);
51+
assert!(can_dereference(raw_ptr));
5452
}
5553

5654
#[kani::proof]
@@ -59,7 +57,7 @@ mod invalid_access {
5957
let array = [10usize; 10];
6058
let invalid: *const [usize; 11] = &array as *const [usize; 10] as *const [usize; 11];
6159
let ptr: *const [usize] = invalid as *const _;
62-
assert_valid_ptr(ptr);
60+
assert!(can_dereference(ptr));
6361
}
6462

6563
unsafe fn new_dead_ptr<T>(val: T) -> *const T {

tests/kani/MemPredicates/thin_ptr_validity.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,27 @@
55
66
extern crate kani;
77

8-
use kani::mem::assert_valid_ptr;
8+
use kani::mem::can_dereference;
99
use std::ptr::NonNull;
1010

1111
mod valid_access {
1212
use super::*;
1313
#[kani::proof]
1414
pub fn check_dangling_zst() {
1515
let dangling = NonNull::<()>::dangling().as_ptr();
16-
assert_valid_ptr(dangling);
16+
assert!(can_dereference(dangling));
1717

1818
let vec_ptr = Vec::<()>::new().as_ptr();
19-
assert_valid_ptr(vec_ptr);
19+
assert!(can_dereference(vec_ptr));
2020

2121
let dangling = NonNull::<[char; 0]>::dangling().as_ptr();
22-
assert_valid_ptr(dangling);
22+
assert!(can_dereference(dangling));
2323
}
2424

2525
#[kani::proof]
2626
pub fn check_valid_array() {
2727
let array = ['a', 'b', 'c'];
28-
assert_valid_ptr(&array);
28+
assert!(can_dereference(&array));
2929
}
3030
}
3131

@@ -35,22 +35,22 @@ mod invalid_access {
3535
#[kani::should_panic]
3636
pub fn check_invalid_ptr() {
3737
let raw_ptr = unsafe { new_dead_ptr::<u8>(0) };
38-
assert_valid_ptr(raw_ptr);
38+
assert!(!can_dereference(raw_ptr));
3939
}
4040

4141
#[kani::proof]
4242
#[kani::should_panic]
4343
pub fn check_invalid_array() {
4444
let raw_ptr = unsafe { new_dead_ptr::<[char; 2]>(['a', 'b']) };
45-
assert_valid_ptr(raw_ptr);
45+
assert!(can_dereference(raw_ptr));
4646
}
4747

4848
#[kani::proof]
4949
pub fn check_invalid_zst() {
5050
let raw_ptr: *const [char; 0] =
5151
unsafe { new_dead_ptr::<[char; 2]>(['a', 'b']) } as *const _;
5252
// ZST pointer are always valid
53-
assert_valid_ptr(raw_ptr);
53+
assert!(can_dereference(raw_ptr));
5454
}
5555

5656
unsafe fn new_dead_ptr<T>(val: T) -> *const T {

0 commit comments

Comments
 (0)