Unsafe review: secretmangle::MangledBoxArbitrary

โš“ Rust    ๐Ÿ“… 2025-09-22    ๐Ÿ‘ค surdeus    ๐Ÿ‘๏ธ 9      

surdeus

Warning

This post was published 39 days ago. The information described in this article may have changed.

The crate part I'm presenting is secretmangle/src/arbitrary.rs at master ยท ProgramCrafter/secretmangle ยท GitHub.

/// Utility for masking a structure in program's heap with a random key,
/// supporting an arbitrary content type.
///
/// This version is written using assembly (not even common Unsafe Rust).
/// If your data is [`bytemuck::NoUninit`] (that is, Copy and has no padding), you can
/// also use [`crate::MangledBox`].
///
/// It is recommended to use [`std::clone::CloneToUninit`] to initialize
/// the contents of the box rather than constructing it on stack, since the
/// latter option might leave some trace of value being masked.
pub struct MangledBoxArbitrary<T> {
    /// Heap allocation with bytes mangled by XORing with `key`.
    data: Box<MaybeUninit<T>>,

    /// T-sized buffer containing a cryptographically secure random key.
    key: MaybeUninit<T>,
}

impl<T> MangledBoxArbitrary<T> {
    // ...
    
    /// Unmangles the contents and invokes the provided closure on it.
    /// Whether the closure panics or returns normally, the contents
    /// are remangled.
    pub fn with_unmangled<F, R>(&mut self, f: F) -> R
    where
        F: FnOnce(NonNull<T>) -> R,
    {
        let data_ptr = Box::as_mut_ptr(&mut self.data).cast::<u8>();
        let key_ptr = self.key.as_ptr().cast::<u8>();
        let data_nn: NonNull<u8> = NonNull::new(data_ptr).unwrap();

        // # Safety
        // 1. Both pointers point to some `MaybeUninit<T>`, so aligned
        // 2. [`data_ptr`] and [`key_ptr`] point to an allocation of at least
        //    `size_of::<T>()` bytes because they are obtained from references
        //    to `MaybeUninit<T>`.
        // 3. [`data_ptr`] points to heap allocation and [`key_ptr`] to
        //    stack, therefore they do not overlap.
        unsafe {
            xor_chunks::<T>(data_ptr, key_ptr);
        }
        
        // ... another safety comment here ...
        let _guard = RemangleGuard::<T> {
            data: data_ptr,
            key: key_ptr,
            token: PhantomData,
        };

        f(data_nn.cast())
    }
}

xor_chunks::<T> is then calling xor_intrinsic::xor_chunks_intrinsic_baseline::<T>(data, key) which is written in assembly code and mangles the T-sized buffer behind first pointer using the key behind second one.

(To be continued)

3 posts - 1 participant

Read full topic

๐Ÿท๏ธ Rust_feed