burn-store: Unsound clone_unsafely visitor memcpy causes double-free/UB, and deserializer panics on unsupported types
Describe the bug
While auditing burn-store, I identified a critical memory-safety vulnerability in the nested deserializer alongside several process-aborting panic paths.
- Unsound unsafe clone in
clone_unsafely(Critical / UB)Deserializer::deserialize_enumduplicates the caller's genericVisitorusing raw bitwise copying (ptr::copy_nonoverlapping) without requiring aCopybound:
fn clone_unsafely<T>(thing: &T) -> T {
unsafe {
let mut clone = std::mem::MaybeUninit::<T>::uninit();
ptr::copy_nonoverlapping(thing as *const T, clone.as_mut_ptr(), 1);
clone.assume_init()
}
}Impact: Because Visitor is not Copy, any non-ZST visitor carrying heap allocations (e.g., a Vec or String) aliases the same memory address across two owners, leading to a double-free / use-after-free when both instances drop.
Miri Verification: Reproduced under Miri (nested_de_miri.rs), which flags an immediate dangling reference/use-after-free error.
- Recoverable Errors Treated as Process Panics (High)
The nested deserializer turns valid data errors into hard process aborts via
unimplemented!():
deserialize_any()panics on untagged enums,#[serde(flatten)], or dynamic values.deserialize_i8()anddeserialize_u32()panic due to incomplete primitive dispatch symmetry.tuple_variant()/struct_variant()panic on data-carrying enum variants.
To Reproduce Steps to reproduce the behavior:
- Implement a custom non-ZST Serde
Visitorowning a heap allocation (e.g., aVec<String>). - Pass it to
Deserializer::deserialize_enumwithinburn-store. - Run under Miri:
cargo +nightly miri test -p burn-store --test nested_de_miri. - See use-after-free / double-free UB error.
Expected behavior
Deserializer should safely handle enum variants and visitors without unsafe memory aliasing, and unsupported types or variant paths should return a recoverable serde::de::Error rather than aborting the process with a panic.
Desktop :
- OS: Fedora Linux
- Rust Version: Nightly / Stable
Proposed Solution:
- Remove
clone_unsafelyentirely fromde.rsand refactordeserialize_enumto evaluate variant access safely without cloning the visitor. - Replace
unimplemented!()paths in primitives and enum dispatchers with properErr(serde::de::Error::custom(...))returns.
Source: tracel-ai/burn