From d325e2c94576f6806508751f945ba5985661b721 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Mon, 28 Nov 2022 18:29:58 -0500 Subject: [PATCH] Use ManuallyDrop with bumpalo's Box instead of mem::forget Miri now reports UB with some uses of bumpalo's Box where it did not before: https://github.com/rust-lang/miri/issues/2704 The previous behavior of Miri was a false negative. rustc applies noalias to newtype wrappers around &mut, so Miri has to retag &mut when passed by value to a function even if it is in a wrapper struct, such as bumpalo's Box. mem::forget is a common aliasing footgun, because for a unique-owning wrapper like Box, leaking an RAII handle re-asserts the uniqueness of the handle as it is sent to the void. Ouch. ManuallyDrop solves this problem. --- src/boxed.rs | 15 +++++++-------- tests/all/boxed.rs | 14 ++++++++++++++ tests/all/main.rs | 1 + 3 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 tests/all/boxed.rs diff --git a/src/boxed.rs b/src/boxed.rs index cf9f4d6..af0737c 100644 --- a/src/boxed.rs +++ b/src/boxed.rs @@ -130,7 +130,7 @@ use { future::Future, hash::{Hash, Hasher}, iter::FusedIterator, - mem, + mem::ManuallyDrop, ops::{Deref, DerefMut}, pin::Pin, task::{Context, Poll}, @@ -280,9 +280,8 @@ impl<'a, T: ?Sized> Box<'a, T> { /// ``` #[inline] pub fn into_raw(b: Box<'a, T>) -> *mut T { - let ptr = b.0 as *mut T; - mem::forget(b); - ptr + let mut b = ManuallyDrop::new(b); + b.deref_mut().0 as *mut T } /// Consumes and leaks the `Box`, returning a mutable reference, @@ -662,9 +661,9 @@ impl<'a, F: ?Sized + Future + Unpin> Future for Box<'a, F> { /// This impl replaces unsize coercion. impl<'a, T, const N: usize> From> for Box<'a, [T]> { - fn from(mut arr: Box<'a, [T; N]>) -> Box<'a, [T]> { + fn from(arr: Box<'a, [T; N]>) -> Box<'a, [T]> { + let mut arr = ManuallyDrop::new(arr); let ptr = core::ptr::slice_from_raw_parts_mut(arr.as_mut_ptr(), N); - mem::forget(arr); unsafe { Box::from_raw(ptr) } } } @@ -672,10 +671,10 @@ impl<'a, T, const N: usize> From> for Box<'a, [T]> { /// This impl replaces unsize coercion. impl<'a, T, const N: usize> TryFrom> for Box<'a, [T; N]> { type Error = Box<'a, [T]>; - fn try_from(mut slice: Box<'a, [T]>) -> Result, Box<'a, [T]>> { + fn try_from(slice: Box<'a, [T]>) -> Result, Box<'a, [T]>> { if slice.len() == N { + let mut slice = ManuallyDrop::new(slice); let ptr = slice.as_mut_ptr() as *mut [T; N]; - mem::forget(slice); Ok(unsafe { Box::from_raw(ptr) }) } else { Err(slice) diff --git a/tests/all/boxed.rs b/tests/all/boxed.rs new file mode 100644 index 0000000..54458fb --- /dev/null +++ b/tests/all/boxed.rs @@ -0,0 +1,14 @@ +#![cfg(feature = "boxed")] + +use bumpalo::Bump; +use bumpalo::boxed::Box; + +#[test] +fn into_raw_aliasing() { + let bump = Bump::new(); + let boxed = Box::new_in(1, &bump); + let raw = Box::into_raw(boxed); + + let mut_ref = unsafe { &mut *raw }; + dbg!(mut_ref); +} diff --git a/tests/all/main.rs b/tests/all/main.rs index 716abc6..00de4f3 100644 --- a/tests/all/main.rs +++ b/tests/all/main.rs @@ -13,5 +13,6 @@ mod tests; mod try_alloc_try_with; mod try_alloc_with; mod vec; +mod boxed; fn main() {}