From b8aba11de3422b5b0ea7be727c2da2b856db3076 Mon Sep 17 00:00:00 2001 From: Pointerbender Date: Wed, 26 May 2021 21:30:43 +0200 Subject: [PATCH 1/3] regression tests for pointer invalidation in core library slice methods --- rust-version | 2 +- tests/run-pass/slices.rs | 54 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 089b4f55511..7b2fc1f00d6 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -d9feaaa548ce380159a1de68f4f6e605db9a9fc5 +d9feaaa548ce380159a1de68f4f6e605db9a9fc5 \ No newline at end of file diff --git a/tests/run-pass/slices.rs b/tests/run-pass/slices.rs index 41e7b2d36c7..113f3faa9ad 100644 --- a/tests/run-pass/slices.rs +++ b/tests/run-pass/slices.rs @@ -1,4 +1,6 @@ #![feature(new_uninit)] +#![feature(slice_as_chunks)] +#![feature(slice_partition_dedup)] use std::slice; @@ -186,8 +188,60 @@ fn uninit_slice() { assert_eq!(values.iter().map(|x| **x).collect::>(), vec![1, 2, 3]) } +/// Regression tests for slice methods in the Rust core library where raw pointers are obtained +/// from mutable references. +fn test_for_invalidated_pointers() { + let mut buffer = [0usize; 64]; + let len = buffer.len(); + + // These regression tests (indirectly) call every slice method which contains a `buffer.as_mut_ptr()`. + // `<[T]>::as_mut_ptr(&mut self)` takes a mutable reference (tagged Unique), which will invalidate all + // the other pointers that were previously derived from it according to the Stacked Borrows model. + // An example of where this could go wrong is a prior bug inside `<[T]>::copy_within`: + // + // unsafe { + // core::ptr::copy(self.as_ptr().add(src_start), self.as_mut_ptr().add(dest), count); + // } + // + // The arguments to `core::ptr::copy` are evaluated from left to right. `self.as_ptr()` creates + // an immutable reference (which is tagged as `SharedReadOnly` by Stacked Borrows) to the array + // and derives a valid `*const` pointer from it. When jumping to the next argument, + // `self.as_mut_ptr()` creates a mutable reference (tagged as `Unique`) to the array, which + // invalidates the existing `SharedReadOnly` reference and any pointers derived from it. + // The invalidated `*const` pointer (the first argument to `core::ptr::copy`) is then used + // after the fact when `core::ptr::copy` is called, which triggers undefined behavior. + + unsafe { assert_eq!(0, *buffer.as_mut_ptr_range().start ); } + // Check that the pointer range is in-bounds, while we're at it + let range = buffer.as_mut_ptr_range(); + unsafe { assert_eq!(*range.start, *range.end.sub(len)); } + + buffer.reverse(); + + // Calls `fn as_chunks_unchecked_mut` internally (requires unstable `#![feature(slice_as_chunks)]`): + assert_eq!(2, buffer.as_chunks_mut::<32>().0.len()); + + // Calls `fn split_at_mut_unchecked` internally: + let split_mut = buffer.split_at_mut(32); + assert_eq!(split_mut.0, split_mut.1); + + // Calls `fn partition_dedup_by` internally (requires unstable `#![feature(slice_partition_dedup)]`): + assert_eq!(1, buffer.partition_dedup().0.len()); + + buffer.rotate_left(8); + buffer.rotate_right(16); + + buffer.copy_from_slice(&[1usize; 64]); + buffer.swap_with_slice(&mut [2usize; 64]); + + assert_eq!(0, unsafe { buffer.align_to_mut::().1[1] }); + + buffer.copy_within(1.., 0); +} + fn main() { slice_of_zst(); test_iter_ref_consistency(); uninit_slice(); + test_for_invalidated_pointers(); } From c6dbe5cdcabaa3eb677519a7193b0d03254aa3b9 Mon Sep 17 00:00:00 2001 From: Pointerbender Date: Wed, 2 Jun 2021 15:31:50 +0200 Subject: [PATCH 2/3] use references so that potential aliasing bugs are triggered during regression test --- tests/run-pass/slices.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/run-pass/slices.rs b/tests/run-pass/slices.rs index 113f3faa9ad..83d9ff11518 100644 --- a/tests/run-pass/slices.rs +++ b/tests/run-pass/slices.rs @@ -1,3 +1,4 @@ +// compile-flags: -Zmiri-track-raw-pointers #![feature(new_uninit)] #![feature(slice_as_chunks)] #![feature(slice_partition_dedup)] @@ -220,13 +221,23 @@ fn test_for_invalidated_pointers() { // Calls `fn as_chunks_unchecked_mut` internally (requires unstable `#![feature(slice_as_chunks)]`): assert_eq!(2, buffer.as_chunks_mut::<32>().0.len()); + for chunk in buffer.as_chunks_mut::<32>().0 { + for elem in chunk { + *elem += 1; + } + } // Calls `fn split_at_mut_unchecked` internally: let split_mut = buffer.split_at_mut(32); assert_eq!(split_mut.0, split_mut.1); // Calls `fn partition_dedup_by` internally (requires unstable `#![feature(slice_partition_dedup)]`): - assert_eq!(1, buffer.partition_dedup().0.len()); + let partition_dedup = buffer.partition_dedup(); + assert_eq!(1, partition_dedup.0.len()); + partition_dedup.0[0] += 1; + for elem in partition_dedup.1 { + *elem += 1; + } buffer.rotate_left(8); buffer.rotate_right(16); From e21dae71c8f331cc2c1cd29f90447508fb1caa2b Mon Sep 17 00:00:00 2001 From: Pointerbender Date: Wed, 2 Jun 2021 15:38:12 +0200 Subject: [PATCH 3/3] removed unintentional file change due to whitespace --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 7b2fc1f00d6..089b4f55511 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -d9feaaa548ce380159a1de68f4f6e605db9a9fc5 \ No newline at end of file +d9feaaa548ce380159a1de68f4f6e605db9a9fc5