Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider alignment when computing into_raw #138

Merged
merged 2 commits into from
Dec 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}