Skip to content

Commit b8aba11

Browse files
committed
regression tests for pointer invalidation in core library slice methods
1 parent 453affa commit b8aba11

File tree

2 files changed

+55
-1
lines changed

2 files changed

+55
-1
lines changed

rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
d9feaaa548ce380159a1de68f4f6e605db9a9fc5
1+
d9feaaa548ce380159a1de68f4f6e605db9a9fc5

tests/run-pass/slices.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#![feature(new_uninit)]
2+
#![feature(slice_as_chunks)]
3+
#![feature(slice_partition_dedup)]
24

35
use std::slice;
46

@@ -186,8 +188,60 @@ fn uninit_slice() {
186188
assert_eq!(values.iter().map(|x| **x).collect::<Vec<_>>(), vec![1, 2, 3])
187189
}
188190

191+
/// Regression tests for slice methods in the Rust core library where raw pointers are obtained
192+
/// from mutable references.
193+
fn test_for_invalidated_pointers() {
194+
let mut buffer = [0usize; 64];
195+
let len = buffer.len();
196+
197+
// These regression tests (indirectly) call every slice method which contains a `buffer.as_mut_ptr()`.
198+
// `<[T]>::as_mut_ptr(&mut self)` takes a mutable reference (tagged Unique), which will invalidate all
199+
// the other pointers that were previously derived from it according to the Stacked Borrows model.
200+
// An example of where this could go wrong is a prior bug inside `<[T]>::copy_within`:
201+
//
202+
// unsafe {
203+
// core::ptr::copy(self.as_ptr().add(src_start), self.as_mut_ptr().add(dest), count);
204+
// }
205+
//
206+
// The arguments to `core::ptr::copy` are evaluated from left to right. `self.as_ptr()` creates
207+
// an immutable reference (which is tagged as `SharedReadOnly` by Stacked Borrows) to the array
208+
// and derives a valid `*const` pointer from it. When jumping to the next argument,
209+
// `self.as_mut_ptr()` creates a mutable reference (tagged as `Unique`) to the array, which
210+
// invalidates the existing `SharedReadOnly` reference and any pointers derived from it.
211+
// The invalidated `*const` pointer (the first argument to `core::ptr::copy`) is then used
212+
// after the fact when `core::ptr::copy` is called, which triggers undefined behavior.
213+
214+
unsafe { assert_eq!(0, *buffer.as_mut_ptr_range().start ); }
215+
// Check that the pointer range is in-bounds, while we're at it
216+
let range = buffer.as_mut_ptr_range();
217+
unsafe { assert_eq!(*range.start, *range.end.sub(len)); }
218+
219+
buffer.reverse();
220+
221+
// Calls `fn as_chunks_unchecked_mut` internally (requires unstable `#![feature(slice_as_chunks)]`):
222+
assert_eq!(2, buffer.as_chunks_mut::<32>().0.len());
223+
224+
// Calls `fn split_at_mut_unchecked` internally:
225+
let split_mut = buffer.split_at_mut(32);
226+
assert_eq!(split_mut.0, split_mut.1);
227+
228+
// Calls `fn partition_dedup_by` internally (requires unstable `#![feature(slice_partition_dedup)]`):
229+
assert_eq!(1, buffer.partition_dedup().0.len());
230+
231+
buffer.rotate_left(8);
232+
buffer.rotate_right(16);
233+
234+
buffer.copy_from_slice(&[1usize; 64]);
235+
buffer.swap_with_slice(&mut [2usize; 64]);
236+
237+
assert_eq!(0, unsafe { buffer.align_to_mut::<u8>().1[1] });
238+
239+
buffer.copy_within(1.., 0);
240+
}
241+
189242
fn main() {
190243
slice_of_zst();
191244
test_iter_ref_consistency();
192245
uninit_slice();
246+
test_for_invalidated_pointers();
193247
}

0 commit comments

Comments
 (0)