From 61535f72d8cd9a3fd0dd96473c54d8de596141b0 Mon Sep 17 00:00:00 2001 From: PB <37089506+pbower@users.noreply.github.com> Date: Mon, 23 Mar 2026 23:57:57 +0000 Subject: [PATCH] Drop bugfix for shared buffer --- src/structs/shared_buffer/internal/owned.rs | 19 ++++++++++++++++++- src/structs/shared_buffer/mod.rs | 4 ++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/structs/shared_buffer/internal/owned.rs b/src/structs/shared_buffer/internal/owned.rs index b230551..26006d9 100644 --- a/src/structs/shared_buffer/internal/owned.rs +++ b/src/structs/shared_buffer/internal/owned.rs @@ -21,12 +21,18 @@ use crate::structs::shared_buffer::internal::vtable::Vtable; /// /// Enables SharedBuffer to manage any container implementing AsRef<[u8]> /// with atomic reference counting for safe sharing. +/// +/// The `drop_fn` field stores a type-erased destructor so that +/// `owned_drop` can properly clean up the concrete `Owned` without +/// knowing T at the vtable level. #[repr(C)] pub(crate) struct Owned + Send + Sync + 'static> { pub(crate) ref_cnt: AtomicUsize, + pub(crate) drop_fn: unsafe fn(*mut ()), pub(crate) owner: T, } + /// Clones owned buffer by incrementing reference count. unsafe fn owned_clone(h: &AtomicPtr<()>, p: *const u8, l: usize) -> SharedBuffer { let raw = h.load(Ordering::Acquire); @@ -43,6 +49,9 @@ unsafe fn owned_clone(h: &AtomicPtr<()>, p: *const u8, l: usize) -> SharedBuffer } /// Decrements reference count, deallocating if last reference. +/// +/// Reads the type-erased destructor stored in the `Owned` header +/// to properly drop the concrete `Owned` and run T's destructor. unsafe fn owned_drop(h: &mut AtomicPtr<()>, _p: *const u8, _l: usize) { let raw = h.load(Ordering::Acquire); if raw.is_null() { @@ -50,7 +59,15 @@ unsafe fn owned_drop(h: &mut AtomicPtr<()>, _p: *const u8, _l: usize) { } let ref_cnt = unsafe { &*(raw as *const AtomicUsize) }; if ref_cnt.fetch_sub(1, Ordering::AcqRel) == 1 { - drop(unsafe { Box::from_raw(raw) }); + // Read the drop function stored after ref_cnt in the Owned header. + // This is safe because Owned is #[repr(C)] with ref_cnt first, + // drop_fn second - the layout is the same for all Owned. + let drop_fn_ptr = unsafe { + (raw as *const u8).add(std::mem::size_of::()) + as *const unsafe fn(*mut ()) + }; + let drop_fn = unsafe { *drop_fn_ptr }; + drop_fn(raw); } } diff --git a/src/structs/shared_buffer/mod.rs b/src/structs/shared_buffer/mod.rs index 88c2215..563be5f 100644 --- a/src/structs/shared_buffer/mod.rs +++ b/src/structs/shared_buffer/mod.rs @@ -154,8 +154,12 @@ impl SharedBuffer { where T: AsRef<[u8]> + Send + Sync + 'static, { + unsafe fn drop_typed + Send + Sync + 'static>(ptr: *mut ()) { + unsafe { drop(Box::from_raw(ptr as *mut Owned)); } + } let raw: *mut Owned = Box::into_raw(Box::new(Owned { ref_cnt: AtomicUsize::new(1), + drop_fn: drop_typed::, owner, })); let buf = unsafe { (*raw).owner.as_ref() };