Handle unwinding from Box::new (#837)

This commit is contained in:
Alice Ryhl
2026-07-07 15:33:07 +02:00
committed by GitHub
parent 91402cee60
commit 924c82bf00
2 changed files with 57 additions and 30 deletions
+36 -23
View File
@@ -1,4 +1,4 @@
use core::mem::{self, ManuallyDrop}; use core::mem::{self, ManuallyDrop, MaybeUninit};
use core::ops::{Deref, RangeBounds}; use core::ops::{Deref, RangeBounds};
use core::ptr::NonNull; use core::ptr::NonNull;
use core::{cmp, fmt, hash, ptr, slice}; use core::{cmp, fmt, hash, ptr, slice};
@@ -946,24 +946,26 @@ impl From<&'static str> for Bytes {
impl From<Vec<u8>> for Bytes { impl From<Vec<u8>> for Bytes {
fn from(vec: Vec<u8>) -> Bytes { fn from(vec: Vec<u8>) -> Bytes {
// Avoid an extra allocation if possible.
if vec.len() == vec.capacity() {
return Bytes::from(vec.into_boxed_slice());
}
let shared = Box::new(MaybeUninit::<Shared>::uninit());
let mut vec = ManuallyDrop::new(vec); let mut vec = ManuallyDrop::new(vec);
let ptr = vec.as_mut_ptr(); let ptr = vec.as_mut_ptr();
let len = vec.len(); let len = vec.len();
let cap = vec.capacity(); let cap = vec.capacity();
// Avoid an extra allocation if possible. let shared = Shared::init_to_raw(
if len == cap { shared,
let vec = ManuallyDrop::into_inner(vec); Shared {
return Bytes::from(vec.into_boxed_slice()); buf: ptr,
} cap,
ref_cnt: AtomicUsize::new(1),
},
);
let shared = Box::new(Shared {
buf: ptr,
cap,
ref_cnt: AtomicUsize::new(1),
});
let shared = Box::into_raw(shared);
// The pointer should be aligned, so this assert should // The pointer should be aligned, so this assert should
// always succeed. // always succeed.
debug_assert!( debug_assert!(
@@ -1327,6 +1329,15 @@ struct Shared {
ref_cnt: AtomicUsize, ref_cnt: AtomicUsize,
} }
impl Shared {
fn init_to_raw(b: Box<MaybeUninit<Self>>, v: Self) -> *mut Self {
let shared = Box::into_raw(b).cast::<Self>();
// SAFETY: The Box has the right layout.
unsafe { shared.write(v) };
shared
}
}
impl Drop for Shared { impl Drop for Shared {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { dealloc(self.buf, Layout::from_size_align(self.cap, 1).unwrap()) } unsafe { dealloc(self.buf, Layout::from_size_align(self.cap, 1).unwrap()) }
@@ -1472,16 +1483,18 @@ unsafe fn shallow_clone_vec(
// updated and since the buffer hasn't been promoted to an // updated and since the buffer hasn't been promoted to an
// `Arc`, those three fields still are the components of the // `Arc`, those three fields still are the components of the
// vector. // vector.
let shared = Box::new(Shared { let shared = Box::new(MaybeUninit::<Shared>::uninit());
buf, let shared = Shared::init_to_raw(
cap: offset.offset_from(buf) as usize + len, shared,
// Initialize refcount to 2. One for this reference, and one Shared {
// for the new clone that will be returned from buf,
// `shallow_clone`. cap: offset.offset_from(buf) as usize + len,
ref_cnt: AtomicUsize::new(2), // Initialize refcount to 2. One for this reference, and one
}); // for the new clone that will be returned from
// `shallow_clone`.
let shared = Box::into_raw(shared); ref_cnt: AtomicUsize::new(2),
},
);
// The pointer should be aligned, so this assert should // The pointer should be aligned, so this assert should
// always succeed. // always succeed.
+21 -7
View File
@@ -79,6 +79,15 @@ struct Shared {
ref_count: AtomicUsize, ref_count: AtomicUsize,
} }
impl Shared {
fn init_to_raw(b: Box<MaybeUninit<Self>>, v: Self) -> *mut Self {
let shared = Box::into_raw(b).cast::<Self>();
// SAFETY: The Box has the right layout.
unsafe { shared.write(v) };
shared
}
}
// Assert that the alignment of `Shared` is divisible by 2. // Assert that the alignment of `Shared` is divisible by 2.
// This is a necessary invariant since we depend on allocating `Shared` a // This is a necessary invariant since we depend on allocating `Shared` a
// shared object to implicitly carry the `KIND_ARC` flag in its pointer. // shared object to implicitly carry the `KIND_ARC` flag in its pointer.
@@ -1112,13 +1121,18 @@ impl BytesMut {
// updated and since the buffer hasn't been promoted to an // updated and since the buffer hasn't been promoted to an
// `Arc`, those three fields still are the components of the // `Arc`, those three fields still are the components of the
// vector. // vector.
let shared = Box::new(Shared { //
vec: rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off), // Explicitly allocate before invoking rebuild_vec() so that
original_capacity_repr, // the vector is not dropped if Box::new() panics.
ref_count: AtomicUsize::new(ref_cnt), let shared = Box::new(MaybeUninit::<Shared>::uninit());
}); let shared = Shared::init_to_raw(
shared,
let shared = Box::into_raw(shared); Shared {
vec: rebuild_vec(self.ptr.as_ptr(), self.len, self.cap, off),
original_capacity_repr,
ref_count: AtomicUsize::new(ref_cnt),
},
);
// The pointer should be aligned, so this assert should // The pointer should be aligned, so this assert should
// always succeed. // always succeed.