Skip to content

Commit

Permalink
Consider alignment when computing into_raw (#138)
Browse files Browse the repository at this point in the history
This commit fixes #137 by considering alignment when converting the raw
Arc pointer to and raw its raw representation.

Signed-off-by: John Nunley <dev@notgull.net>
  • Loading branch information
notgull authored Dec 16, 2023
1 parent 0a6a603 commit ac2e68c
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 5 deletions.
19 changes: 14 additions & 5 deletions portable-atomic-util/src/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use alloc::boxed::Box;

use core::{
borrow::Borrow,
fmt,
cmp, fmt,
hash::Hash,
isize,
marker::PhantomData,
Expand Down Expand Up @@ -332,12 +332,13 @@ impl<T: ?Sized> Arc<T> {
/// ```
/// use portable_atomic_util::Arc;
///
/// let five = Arc::new(5);
/// let five = Arc::new(5u8);
/// let five_ptr = Arc::into_raw(five);
///
/// // We should now free the pointer.
/// // SAFETY: The pointer is valid.
/// unsafe { Arc::from_raw(five_ptr) };
/// let five = unsafe { Arc::from_raw(five_ptr) };
/// assert_eq!(&*five, &5u8);
/// ```
#[must_use]
pub fn into_raw(self) -> *const T {
Expand All @@ -359,11 +360,15 @@ impl<T: ?Sized> Arc<T> {
/// ```
#[must_use]
pub fn as_ptr(&self) -> *const T {
// Get the alignment of `T`.
let alignment = mem::align_of_val(&**self);

// Get the raw pointer.
let ptr = self.shared.as_ptr() as *mut u8;

// Add the size of the header so that it points to the value.
let new_ptr = strict::map_addr(ptr, |addr| addr + mem::size_of::<Header>());
let distance = cmp::max(alignment, mem::size_of::<Header>());
let new_ptr = strict::map_addr(ptr, |addr| addr + distance);

// Cast the pointer to the correct type.
strict::with_metadata_of(new_ptr, self.shared.as_ptr() as *mut T)
Expand Down Expand Up @@ -391,11 +396,15 @@ impl<T: ?Sized> Arc<T> {
pub unsafe fn from_raw(ptr: *const T) -> Self {
// SAFETY: The caller must ensure that the pointer is valid.
unsafe {
// Get the alignment of `T`.
let alignment = mem::align_of_val(&*ptr);

// Get the raw pointer.
let raw_ptr = ptr as *mut u8;

// Subtract the size of the header so that it points to the Shared allocation.
let new_ptr = strict::map_addr(raw_ptr, |addr| addr - mem::size_of::<Header>());
let distance = cmp::max(alignment, mem::size_of::<Header>());
let new_ptr = strict::map_addr(raw_ptr, |addr| addr - distance);

// Cast the pointer to the correct type.
let shared = strict::with_metadata_of(new_ptr, ptr as *mut Shared<T>);
Expand Down
19 changes: 19 additions & 0 deletions portable-atomic-util/tests/arc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT

#![cfg(any(feature = "std", feature = "alloc"))]

use portable_atomic_util::Arc;

#[test]
fn over_aligned() {
#[repr(align(128))]
struct Aligned(u32);

let value = Arc::new(Aligned(128));
let ptr = Arc::into_raw(value);
// SAFETY: `ptr` should always be a valid `Aligned`.
assert_eq!(unsafe { (&*ptr).0 }, 128);
// SAFETY: `ptr` is a valid reference to an `Arc<Aligned>`.
let value = unsafe { Arc::from_raw(ptr) };
assert_eq!(value.0, 128);
}

0 comments on commit ac2e68c

Please sign in to comment.