Skip to content

Commit e587b77

Browse files
celinvaltedinski
authored andcommitted
Update expectation for projection of slice reference. (rust-lang#1032)
* Fix 1: Slice projection yields a thin pointer Our expectation did not considered the array decay when creating fat pointers. * Added new testcases for size_of and raw slice * Add test for different representations + fix comment
1 parent 2485833 commit e587b77

File tree

9 files changed

+575
-6
lines changed

9 files changed

+575
-6
lines changed

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,23 @@ impl<'tcx> ProjectedPlace<'tcx> {
7878
TypeOrVariant::Type(t) => {
7979
let expr_ty = expr.typ().clone();
8080
let type_from_mir = ctx.codegen_ty(*t);
81-
if expr_ty != type_from_mir { Some((expr_ty, type_from_mir)) } else { None }
81+
if expr_ty != type_from_mir {
82+
match t.kind() {
83+
// Slice references (`&[T]`) store raw pointers to the element type `T`
84+
// due to pointer decay. They are fat pointers with the following repr:
85+
// SliceRef { data: *T, len: usize }.
86+
// In those cases, the projection will yield a pointer type.
87+
ty::Slice(..) | ty::Str
88+
if expr_ty.is_pointer()
89+
&& expr_ty.base_type() == type_from_mir.base_type() =>
90+
{
91+
None
92+
}
93+
_ => Some((expr_ty, type_from_mir)),
94+
}
95+
} else {
96+
None
97+
}
8298
}
8399
// TODO: handle Variant https://github.com/model-checking/kani/issues/448
84100
TypeOrVariant::Variant(_) => None,
@@ -320,7 +336,7 @@ impl<'tcx> GotocCtx<'tcx> {
320336
inner_goto_expr.member("data", &self.symbol_table)
321337
}
322338
ty::Adt(..) if self.is_unsized(inner_mir_typ) => {
323-
// in cbmc-reg/Strings/os_str_reduced.rs, we see
339+
// in tests/kani/Strings/os_str_reduced.rs, we see
324340
// ```
325341
// p.projection = [
326342
// Deref,
@@ -461,9 +477,10 @@ impl<'tcx> GotocCtx<'tcx> {
461477
/// If it passes through a fat pointer along the way, it stores info about it,
462478
/// which can be useful in reconstructing fat pointer operations.
463479
pub fn codegen_place(&mut self, p: &Place<'tcx>) -> ProjectedPlace<'tcx> {
464-
debug!("codegen_place: {:?}", p);
480+
debug!(place=?p, "codegen_place");
465481
let initial_expr = self.codegen_local(p.local);
466482
let initial_typ = TypeOrVariant::Type(self.local_ty(p.local));
483+
debug!(?initial_typ, ?initial_expr, "codegen_place");
467484
let initial_projection = ProjectedPlace::new(initial_expr, initial_typ, None, None, self);
468485
p.projection
469486
.iter()

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -538,9 +538,12 @@ impl<'tcx> GotocCtx<'tcx> {
538538
//TODO: Ensure that this is correct
539539
ty::Dynamic(..) => self.codegen_fat_ptr(ty),
540540
// As per zulip, a raw slice/str is a variable length array
541-
// https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp
541+
// https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp/topic/Memory.20layout.20of.20DST
542+
// &[T] -> { data: *const T, len: usize }
543+
// [T] -> memory location (flexible array)
544+
// Note: This is not valid C but CBMC seems to be ok with it.
542545
ty::Slice(e) => self.codegen_ty(*e).flexible_array_of(),
543-
ty::Str => Type::c_char().array_of(0),
546+
ty::Str => Type::c_char().flexible_array_of(),
544547
ty::Ref(_, t, _) | ty::RawPtr(ty::TypeAndMut { ty: t, .. }) => self.codegen_ty_ref(*t),
545548
ty::FnDef(_, _) => {
546549
let sig = self.monomorphize(ty.fn_sig(self.tcx));
@@ -754,7 +757,7 @@ impl<'tcx> GotocCtx<'tcx> {
754757
if self.use_slice_fat_pointer(mir_type) {
755758
let pointer_name = match mir_type.kind() {
756759
ty::Slice(..) => self.ty_mangled_name(mir_type),
757-
ty::Str => "str".intern(),
760+
ty::Str => "refstr".intern(),
758761
ty::Adt(..) => format!("&{}", self.ty_mangled_name(mir_type)).intern(),
759762
kind => unreachable!("Generating a slice fat pointer to {:?}", kind),
760763
};

tests/expected/raw_slice/expected

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
Checking harness check_non_empty_raw...
2+
3+
Status: SUCCESS\
4+
Description: "assertion failed: mem::size_of_val(raw) == 4"\
5+
slice.rs:61:5 in function check_non_empty_raw
6+
7+
Status: SUCCESS\
8+
Description: "assertion failed: raw.inner.len() == 4"\
9+
slice.rs:62:5 in function check_non_empty_raw
10+
11+
Status: SUCCESS\
12+
Description: "assertion failed: raw.inner[0] == 1"\
13+
slice.rs:63:5 in function check_non_empty_raw
14+
15+
VERIFICATION:- SUCCESSFUL
16+
17+
Checking harness check_empty_raw...
18+
19+
Status: SUCCESS\
20+
Description: "assertion failed: mem::size_of_val(raw) == 0"\
21+
slice.rs:70:5 in function check_empty_raw
22+
23+
Status: SUCCESS\
24+
Description: "assertion failed: raw.inner.len() == 0"\
25+
slice.rs:71:5 in function check_empty_raw
26+
27+
VERIFICATION:- SUCCESSFUL
28+
29+
Checking harness check_non_empty_slice...
30+
31+
Status: SUCCESS\
32+
Description: "assertion failed: mem::size_of_val(slice) == 2"\
33+
slice.rs:78:5 in function check_non_empty_slice
34+
35+
Status: SUCCESS\
36+
Description: "assertion failed: slice.others.len() == 1"\
37+
slice.rs:79:5 in function check_non_empty_slice
38+
39+
Status: SUCCESS\
40+
Description: "assertion failed: slice.first == 1"\
41+
slice.rs:80:5 in function check_non_empty_slice
42+
43+
Status: SUCCESS\
44+
Description: "assertion failed: slice.others[0] == 5"\
45+
slice.rs:81:5 in function check_non_empty_slice
46+
47+
VERIFICATION:- SUCCESSFUL
48+
49+
50+
Checking harness check_naive_iterator_should_fail...
51+
52+
Status: SUCCESS\
53+
Description: ""Naive new should have the wrong slice len""\
54+
slice.rs:94:5 in function check_naive_iterator_should_fail
55+
56+
Status: SUCCESS\
57+
Description: "assertion failed: slice.first == first"\
58+
slice.rs:95:5 in function check_naive_iterator_should_fail
59+
60+
Status: SUCCESS\
61+
Description: "assertion failed: slice.others[0] == second"\
62+
slice.rs:96:5 in function check_naive_iterator_should_fail
63+
64+
Status: FAILURE\
65+
Description: "dereference failure: pointer outside object bounds"\
66+
slice.rs:100:32 in function check_naive_iterator_should_fail
67+
68+
VERIFICATION:- FAILED
69+
70+
71+
Summary:\
72+
Verification failed for - check_naive_iterator_should_fail\
73+
Complete - 3 successfully verified harnesses, 1 failures, 4 total.

tests/expected/raw_slice/slice.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-flags: --unwind 3
4+
5+
//! This test case has a bunch of checks related to structures using raw slices ([T]).
6+
use std::mem;
7+
8+
/// Non-sized structure with a sized element and an unsized element.
9+
struct NonEmptySlice {
10+
first: u8,
11+
others: [u8],
12+
}
13+
14+
/// Non-sized structure with only an unsized element.
15+
struct RawSlice {
16+
inner: [u8],
17+
}
18+
19+
impl NonEmptySlice {
20+
/// This creates a NonEmptySlice from a byte slice.
21+
///
22+
/// Note that the cast operation keep the the fat pointer structure, so we need to manually
23+
/// create the expected pointer with correct len. For the NonEmptySlice, this looks like:
24+
/// ```rust
25+
/// struct NonEmptySliceRef {
26+
/// ptr: *const u8,
27+
/// unsize_len: usize,
28+
/// }
29+
/// ```
30+
///
31+
/// I.e.: The length is only relative to the unsized part of the structure.
32+
/// The total size of the object is the `size_of(sized_members) + (unsize_len * size_of::<u8>())`
33+
///
34+
fn new(bytes: &mut [u8]) -> &Self {
35+
assert!(bytes.len() > 0, "This requires at least one element");
36+
let unsized_len = bytes.len() - 1;
37+
unsafe {
38+
let ptr = std::ptr::slice_from_raw_parts_mut(bytes.as_mut_ptr(), unsized_len);
39+
unsafe { &*(ptr as *mut Self) }
40+
}
41+
}
42+
43+
/// This function does a naive transmute in the slice which generates a corrupt structure.
44+
/// See the documentation of `NonEmptySliceRef::new()` for more details.
45+
fn naive_new(bytes: &mut [u8]) -> &Self {
46+
assert!(bytes.len() > 0, "This requires at least one element");
47+
unsafe { std::mem::transmute(bytes) }
48+
}
49+
}
50+
51+
impl RawSlice {
52+
fn new(bytes: &mut [u8]) -> &Self {
53+
unsafe { std::mem::transmute(bytes) }
54+
}
55+
}
56+
57+
#[kani::proof]
58+
fn check_non_empty_raw() {
59+
let mut vector = vec![1u8, 2u8, 3u8, 4u8];
60+
let raw = RawSlice::new(vector.as_mut_slice());
61+
assert_eq!(mem::size_of_val(raw), 4);
62+
assert_eq!(raw.inner.len(), 4);
63+
assert_eq!(raw.inner[0], 1);
64+
}
65+
66+
#[kani::proof]
67+
fn check_empty_raw() {
68+
let mut vector = vec![];
69+
let raw = RawSlice::new(vector.as_mut_slice());
70+
assert_eq!(mem::size_of_val(raw), 0);
71+
assert_eq!(raw.inner.len(), 0);
72+
}
73+
74+
#[kani::proof]
75+
fn check_non_empty_slice() {
76+
let mut vector = vec![1u8, 5u8];
77+
let slice = NonEmptySlice::new(vector.as_mut_slice());
78+
assert_eq!(mem::size_of_val(slice), 2);
79+
assert_eq!(slice.others.len(), 1);
80+
assert_eq!(slice.first, 1);
81+
assert_eq!(slice.others[0], 5);
82+
}
83+
84+
#[kani::proof]
85+
#[kani::unwind(3)]
86+
fn check_naive_iterator_should_fail() {
87+
let mut bytes = vec![1u8, 5u8];
88+
assert_eq!(bytes.len(), 2);
89+
90+
let first = bytes[0];
91+
let second = bytes[1];
92+
93+
let slice = NonEmptySlice::naive_new(&mut bytes);
94+
assert_eq!(slice.others.len(), 2, "Naive new should have the wrong slice len");
95+
assert_eq!(slice.first, first);
96+
assert_eq!(slice.others[0], second);
97+
let mut sum = 0u8;
98+
for e in &slice.others {
99+
// This should trigger out-of-bounds.
100+
sum = sum.wrapping_add(*e);
101+
}
102+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
Checking harness check_non_empty_raw...
2+
3+
Status: SUCCESS\
4+
Description: "assertion failed: mem::size_of_val(raw) == 4"\
5+
in function check_non_empty_raw
6+
7+
Status: SUCCESS\
8+
Description: "assertion failed: raw.inner.len() == 4"\
9+
in function check_non_empty_raw
10+
11+
Status: SUCCESS\
12+
Description: "assertion failed: raw.inner[0] == 1"\
13+
in function check_non_empty_raw
14+
15+
VERIFICATION:- SUCCESSFUL
16+
17+
Checking harness check_empty_raw...
18+
19+
Status: SUCCESS\
20+
Description: "assertion failed: mem::size_of_val(raw) == 0"\
21+
in function check_empty_raw
22+
23+
Status: SUCCESS\
24+
Description: "assertion failed: raw.inner.len() == 0"\
25+
in function check_empty_raw
26+
27+
VERIFICATION:- SUCCESSFUL
28+
29+
Checking harness check_non_empty_slice...
30+
31+
Status: SUCCESS\
32+
Description: "assertion failed: mem::size_of_val(slice) == 2"\
33+
in function check_non_empty_slice
34+
35+
Status: SUCCESS\
36+
Description: "assertion failed: slice.others.len() == 1"\
37+
in function check_non_empty_slice
38+
39+
Status: SUCCESS\
40+
Description: "assertion failed: slice.first == 1"\
41+
in function check_non_empty_slice
42+
43+
Status: SUCCESS\
44+
Description: "assertion failed: slice.others[0] == 5"\
45+
in function check_non_empty_slice
46+
47+
VERIFICATION:- SUCCESSFUL
48+
49+
50+
Checking harness check_naive_iterator_should_fail...
51+
52+
Status: SUCCESS\
53+
Description: ""Naive new should have the wrong slice len""\
54+
in function check_naive_iterator_should_fail
55+
56+
Status: SUCCESS\
57+
Description: "assertion failed: slice.first == first"\
58+
in function check_naive_iterator_should_fail
59+
60+
Status: SUCCESS\
61+
Description: "assertion failed: slice.others[0] == second"\
62+
in function check_naive_iterator_should_fail
63+
64+
Status: FAILURE\
65+
Description: "dereference failure: pointer outside object bounds"\
66+
in function check_naive_iterator_should_fail
67+
68+
VERIFICATION:- FAILED
69+
70+
71+
Summary:\
72+
Verification failed for - check_naive_iterator_should_fail\
73+
Complete - 3 successfully verified harnesses, 1 failures, 4 total.

0 commit comments

Comments
 (0)