diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index d734ae6a2cf61..4b695ad7c7933 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -22,7 +22,7 @@ use core::borrow; use core::fmt; use core::cmp::Ordering; use core::intrinsics::abort; -use core::mem::{self, size_of_val, uninitialized}; +use core::mem::{self, align_of_val, size_of_val, uninitialized}; use core::ops::Deref; use core::ops::CoerceUnsized; use core::ptr::{self, Shared}; @@ -324,7 +324,9 @@ impl Arc { Ok(elem) } } +} +impl Arc { /// Consumes the `Arc`, returning the wrapped pointer. /// /// To avoid a memory leak the pointer must be converted back to an `Arc` using @@ -378,16 +380,21 @@ impl Arc { /// ``` #[stable(feature = "rc_raw", since = "1.17.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { - // To find the corresponding pointer to the `ArcInner` we need to subtract the offset of the - // `data` field from the pointer. - let ptr = (ptr as *const u8).offset(-offset_of!(ArcInner, data)); + // Align the unsized value to the end of the ArcInner. + // Because it is ?Sized, it will always be the last field in memory. + let align = align_of_val(&*ptr); + let layout = Layout::new::>(); + let offset = (layout.size() + layout.padding_needed_for(align)) as isize; + + // Reverse the offset to find the original ArcInner. + let fake_ptr = ptr as *mut ArcInner; + let arc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); + Arc { - ptr: Shared::new_unchecked(ptr as *mut u8 as *mut _), + ptr: Shared::new_unchecked(arc_ptr), } } -} -impl Arc { /// Creates a new [`Weak`][weak] pointer to this value. /// /// [weak]: struct.Weak.html @@ -1491,6 +1498,28 @@ mod tests { } } + #[test] + fn test_into_from_raw_unsized() { + use std::fmt::Display; + use std::string::ToString; + + let arc: Arc = Arc::from("foo"); + + let ptr = Arc::into_raw(arc.clone()); + let arc2 = unsafe { Arc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }, "foo"); + assert_eq!(arc, arc2); + + let arc: Arc = Arc::new(123); + + let ptr = Arc::into_raw(arc.clone()); + let arc2 = unsafe { Arc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }.to_string(), "123"); + assert_eq!(arc2.to_string(), "123"); + } + #[test] fn test_cowarc_clone_make_mut() { let mut cow0 = Arc::new(75); diff --git a/src/liballoc/macros.rs b/src/liballoc/macros.rs index 43ebaa4fbdb3f..c2a3019515f10 100644 --- a/src/liballoc/macros.rs +++ b/src/liballoc/macros.rs @@ -105,22 +105,3 @@ macro_rules! vec { macro_rules! format { ($($arg:tt)*) => ($crate::fmt::format(format_args!($($arg)*))) } - -// Private macro to get the offset of a struct field in bytes from the address of the struct. -macro_rules! offset_of { - ($container:path, $field:ident) => {{ - // Make sure the field actually exists. This line ensures that a compile-time error is - // generated if $field is accessed through a Deref impl. - let $container { $field : _, .. }; - - // Create an (invalid) instance of the container and calculate the offset to its - // field. Using a null pointer might be UB if `&(*(0 as *const T)).field` is interpreted to - // be nullptr deref. - let invalid: $container = ::core::mem::uninitialized(); - let offset = &invalid.$field as *const _ as usize - &invalid as *const _ as usize; - - // Do not run destructors on the made up invalid instance. - ::core::mem::forget(invalid); - offset as isize - }}; -} diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 58c589697f41c..553980d463fc5 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -253,7 +253,7 @@ use core::hash::{Hash, Hasher}; use core::intrinsics::abort; use core::marker; use core::marker::Unsize; -use core::mem::{self, forget, size_of_val, uninitialized}; +use core::mem::{self, align_of_val, forget, size_of_val, uninitialized}; use core::ops::Deref; use core::ops::CoerceUnsized; use core::ptr::{self, Shared}; @@ -359,7 +359,9 @@ impl Rc { Err(this) } } +} +impl Rc { /// Consumes the `Rc`, returning the wrapped pointer. /// /// To avoid a memory leak the pointer must be converted back to an `Rc` using @@ -413,17 +415,21 @@ impl Rc { /// ``` #[stable(feature = "rc_raw", since = "1.17.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { - // To find the corresponding pointer to the `RcBox` we need to subtract the offset of the - // `value` field from the pointer. + // Align the unsized value to the end of the RcBox. + // Because it is ?Sized, it will always be the last field in memory. + let align = align_of_val(&*ptr); + let layout = Layout::new::>(); + let offset = (layout.size() + layout.padding_needed_for(align)) as isize; + + // Reverse the offset to find the original RcBox. + let fake_ptr = ptr as *mut RcBox; + let rc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); - let ptr = (ptr as *const u8).offset(-offset_of!(RcBox, value)); Rc { - ptr: Shared::new_unchecked(ptr as *mut u8 as *mut _) + ptr: Shared::new_unchecked(rc_ptr), } } -} -impl Rc { /// Creates a new [`Weak`][weak] pointer to this value. /// /// [weak]: struct.Weak.html @@ -1522,6 +1528,28 @@ mod tests { } } + #[test] + fn test_into_from_raw_unsized() { + use std::fmt::Display; + use std::string::ToString; + + let rc: Rc = Rc::from("foo"); + + let ptr = Rc::into_raw(rc.clone()); + let rc2 = unsafe { Rc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }, "foo"); + assert_eq!(rc, rc2); + + let rc: Rc = Rc::new(123); + + let ptr = Rc::into_raw(rc.clone()); + let rc2 = unsafe { Rc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }.to_string(), "123"); + assert_eq!(rc2.to_string(), "123"); + } + #[test] fn get_mut() { let mut x = Rc::new(3);