Skip to content

Commit 96e5797

Browse files
authored
A few follow ups for the wasmtime_internal_error crate (#12198)
* A few follow ups for the `wasmtime_internal_error` crate * address review comments
1 parent 0ffd4d8 commit 96e5797

File tree

4 files changed

+46
-36
lines changed

4 files changed

+46
-36
lines changed

crates/error/src/backtrace.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
44
static ENABLED: AtomicBool = AtomicBool::new(true);
55

66
fn enabled() -> bool {
7-
ENABLED.load(Ordering::Acquire)
7+
ENABLED.load(Ordering::Relaxed)
88
}
99

1010
/// Forcibly disable capturing backtraces dynamically.
@@ -15,7 +15,7 @@ fn enabled() -> bool {
1515
/// `backtrace` cargo feature.
1616
#[doc(hidden)]
1717
pub fn disable_backtrace() {
18-
ENABLED.store(false, Ordering::Release)
18+
ENABLED.store(false, Ordering::Relaxed)
1919
}
2020

2121
#[track_caller]

crates/error/src/boxed.rs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::{OutOfMemory, Result};
22
use alloc::boxed::Box;
33
use core::alloc::Layout;
4+
use core::mem::MaybeUninit;
45
use core::ptr::NonNull;
56

67
/// Try to allocate a block of memory that fits the given layout, or return an
@@ -24,27 +25,19 @@ pub(crate) unsafe fn try_alloc(layout: Layout) -> Result<NonNull<u8>, OutOfMemor
2425

2526
/// Create a `Box<T>`, or return an `OutOfMemory` error.
2627
#[inline]
27-
pub(crate) fn try_box<T>(value: T) -> Result<Box<T>, OutOfMemory> {
28-
let layout = alloc::alloc::Layout::new::<T>();
28+
pub(crate) fn try_new_uninit_box<T>() -> Result<Box<MaybeUninit<T>>, OutOfMemory> {
29+
let layout = alloc::alloc::Layout::new::<MaybeUninit<T>>();
2930

3031
if layout.size() == 0 {
31-
// Safety: `Box` explicitly allows construction from dangling pointers
32-
// (which are guaranteed non-null and aligned) for zero-sized types.
33-
return Ok(unsafe { Box::from_raw(core::ptr::dangling::<T>().cast_mut()) });
32+
// NB: no actual allocation takes place when boxing zero-sized types.
33+
return Ok(Box::new(MaybeUninit::uninit()));
3434
}
3535

3636
// Safety: layout size is non-zero.
3737
let ptr = unsafe { try_alloc(layout)? };
3838

39-
let ptr = ptr.cast::<T>();
39+
let ptr = ptr.cast::<MaybeUninit<T>>();
4040

41-
// Safety: The allocation succeeded, and it has `T`'s layout, so the pointer
42-
// is valid for writing a `T`.
43-
unsafe {
44-
ptr.write(value);
45-
}
46-
47-
// Safety: The pointer's memory block was allocated by the global allocator,
48-
// is valid for `T`, and is initialized.
41+
// Safety: The pointer's memory block was allocated by the global allocator.
4942
Ok(unsafe { Box::from_raw(ptr.as_ptr()) })
5043
}

crates/error/src/error.rs

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::boxed::try_box;
1+
use super::boxed::try_new_uninit_box;
22
use super::context::ContextError;
33
use super::ptr::{MutPtr, OwnedPtr, SharedPtr};
44
use super::vtable::Vtable;
@@ -18,8 +18,10 @@ use std::backtrace::{Backtrace, BacktraceStatus};
1818
///
1919
/// # Safety
2020
///
21-
/// Implementations must correctly report their type (or a type `T` where `*mut
22-
/// Self` can be cast to `*mut T` and safely accessed) in `ext_is`.
21+
/// Safety relies on `ext_is` being implemented correctly. Implementations must
22+
/// not lie about whether they are or are not an instance of the given type id's
23+
/// associated type (or a newtype wrapper around that type). Violations will
24+
/// lead to unsafety.
2325
pub(crate) unsafe trait ErrorExt: core::error::Error + Send + Sync + 'static {
2426
/// Get a shared borrow of the next error in the chain.
2527
fn ext_source(&self) -> Option<OomOrDynErrorRef<'_>>;
@@ -30,7 +32,8 @@ pub(crate) unsafe trait ErrorExt: core::error::Error + Send + Sync + 'static {
3032
/// Take ownership of the next error in the chain.
3133
fn ext_take_source(&mut self) -> Option<OomOrDynError>;
3234

33-
/// Is this error an instance of `T`, where `type_id == TypeId::of::<T>()`?
35+
/// Is this error an instance of `T`, where `type_id == TypeId::of::<T>()`
36+
/// or a newtype wrapper around that type?
3437
///
3538
/// # Safety
3639
///
@@ -137,12 +140,16 @@ impl BoxedDynError {
137140
None => crate::backtrace::capture(),
138141
};
139142

140-
let error = try_box(ConcreteError {
141-
vtable: Vtable::of::<E>(),
142-
#[cfg(feature = "backtrace")]
143-
backtrace: Some(backtrace),
144-
error,
145-
})?;
143+
let boxed = try_new_uninit_box()?;
144+
let error = Box::write(
145+
boxed,
146+
ConcreteError {
147+
vtable: Vtable::of::<E>(),
148+
#[cfg(feature = "backtrace")]
149+
backtrace: Some(backtrace),
150+
error,
151+
},
152+
);
146153

147154
// We are going to pun the `ConcreteError<E>` pointer into a `DynError`
148155
// pointer. Debug assert that their layouts are compatible first.
@@ -179,7 +186,8 @@ impl BoxedDynError {
179186
// Safety: `Box::into_raw` always returns a non-null pointer.
180187
let ptr = unsafe { NonNull::new_unchecked(ptr) };
181188
let ptr = OwnedPtr::new(ptr);
182-
Ok(Self::from_owned_ptr(ptr))
189+
// Safety: points to a valid `DynError`.
190+
Ok(unsafe { Self::from_owned_ptr(ptr) })
183191
}
184192

185193
fn into_owned_ptr(self) -> OwnedPtr<DynError> {
@@ -188,7 +196,12 @@ impl BoxedDynError {
188196
ptr
189197
}
190198

191-
fn from_owned_ptr(inner: OwnedPtr<DynError>) -> Self {
199+
/// # Safety
200+
///
201+
/// The given pointer must be a valid `DynError` pointer: punning a
202+
/// `ConcreteError<?>` and is safe to drop and deallocate with its
203+
/// `DynError::vtable` methods.
204+
unsafe fn from_owned_ptr(inner: OwnedPtr<DynError>) -> Self {
192205
BoxedDynError { inner }
193206
}
194207
}
@@ -398,6 +411,7 @@ impl BoxedDynError {
398411
/// assert_eq!(error.to_string(), "oops I ate worms");
399412
/// # }
400413
/// ```
414+
#[repr(transparent)]
401415
pub struct Error {
402416
pub(crate) inner: OomOrDynError,
403417
}
@@ -1500,6 +1514,7 @@ impl<'a> OomOrDynErrorMut<'a> {
15001514

15011515
/// Bit packed version of `enum { BoxedDynError, OutOfMemory }` that relies on
15021516
/// implicit pointer tagging and `OutOfMemory` being zero-sized.
1517+
#[repr(transparent)]
15031518
pub(crate) struct OomOrDynError {
15041519
// Safety: this must always be the casted-to-`u8` version of either (a)
15051520
// `0x1`, or (b) a valid, owned `DynError` pointer. (Note that these cases
@@ -1526,7 +1541,8 @@ impl Drop for OomOrDynError {
15261541
if self.is_boxed_dyn_error() {
15271542
let inner = self.inner.cast::<DynError>();
15281543
let inner = OwnedPtr::new(inner);
1529-
let _ = BoxedDynError::from_owned_ptr(inner);
1544+
// Safety: the pointer is a valid `DynError` pointer.
1545+
let _ = unsafe { BoxedDynError::from_owned_ptr(inner) };
15301546
} else {
15311547
debug_assert!(self.is_oom());
15321548
}
@@ -1640,11 +1656,9 @@ impl OomOrDynError {
16401656
self,
16411657
) -> Box<dyn core::error::Error + Send + Sync + 'static> {
16421658
let box_dyn_error_of_oom = || {
1643-
let ptr = NonNull::<OutOfMemory>::dangling().as_ptr();
1644-
// Safety: it is always safe to call `Box::<T>::from_raw` on `T`'s
1645-
// dangling pointer if `T` is a unit type.
1646-
let boxed = unsafe { Box::from_raw(ptr) };
1647-
boxed as _
1659+
// NB: `Box::new` will never actually allocate for zero-sized types
1660+
// like `OutOfMemory`.
1661+
Box::new(OutOfMemory::new()) as _
16481662
};
16491663

16501664
if self.is_oom() {

crates/error/src/vtable.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{ConcreteError, DynError, ErrorExt, OomOrDynErrorMut, OomOrDynErrorRef, OutOfMemory};
2-
use crate::boxed::try_box;
2+
use crate::boxed::try_new_uninit_box;
33
use crate::ptr::{MutPtr, OwnedPtr, SharedPtr};
44
use alloc::boxed::Box;
55
use core::{any::TypeId, fmt, ptr::NonNull};
@@ -137,7 +137,10 @@ where
137137
let error = error.cast::<ConcreteError<E>>();
138138
// Safety: implied by all vtable functions' safety contract.
139139
let error = unsafe { error.into_box() };
140-
let error = try_box(error.error)?;
140+
141+
let boxed = try_new_uninit_box()?;
142+
let error = Box::write(boxed, error.error);
143+
141144
Ok(error as _)
142145
}
143146

0 commit comments

Comments
 (0)