Do Not Let String/Vec Auto-Drop Other Process's Memory

2026-08-30 浏览 (3)


id: mem-03 original_id: P.UNS.MEM.03 level: P impact: CRITICAL

Do Not Let String/Vec Auto-Drop Other Process's Memory

Summary

Never create String, Vec, or Box from memory allocated outside Rust's allocator. They will try to free the memory with the wrong deallocator.

Rationale

String, Vec, and Box assume memory was allocated by Rust's global allocator. When dropped, they call dealloc. If the memory came from C's malloc, a different allocator, or shared memory, this causes undefined behavior.

Bad Example

// DON'T: Create String from C-allocated memory
extern "C" {
    fn c_get_string() -> *mut std::os::raw::c_char;
}

fn bad_string() -> String {
    unsafe {
        let ptr = c_get_string();
        // BAD: String will try to free with Rust allocator
        String::from_raw_parts(ptr as *mut u8, len, cap)
    }
}

// DON'T: Create Vec from foreign memory
fn bad_vec(ptr: *mut u8, len: usize) -> Vec<u8> {
    // BAD: Vec will free this memory incorrectly
    unsafe { Vec::from_raw_parts(ptr, len, len) }
}

// DON'T: Wrap shared memory in Box
fn bad_box(shared_ptr: *mut Data) -> Box<Data> {
    // BAD: Box will try to deallocate shared memory!
    unsafe { Box::from_raw(shared_ptr) }
}

Good Example

use std::ffi::CStr;

extern "C" {
    fn c_get_string() -> *mut std::os::raw::c_char;
    fn c_free_string(s: *mut std::os::raw::c_char);
}

// DO: Copy data into Rust-owned allocation
fn good_string() -> String {
    unsafe {
        let ptr = c_get_string();
        let cstr = CStr::from_ptr(ptr);
        let result = cstr.to_string_lossy().into_owned();
        c_free_string(ptr);  // Free with correct deallocator
        result
    }
}

// DO: Use wrapper that calls correct deallocator
struct CString {
    ptr: *mut std::os::raw::c_char,
}

impl Drop for CString {
    fn drop(&mut self) {
        unsafe { c_free_string(self.ptr); }
    }
}

// DO: Use slice for borrowed view, don't take ownership
fn good_slice(ptr: *const u8, len: usize) -> &'static [u8] {
    // Only borrow, don't own
    unsafe { std::slice::from_raw_parts(ptr, len) }
}

// DO: For shared memory, use raw pointers or custom wrapper
struct SharedBuffer {
    ptr: *mut u8,
    len: usize,
}

impl SharedBuffer {
    fn as_slice(&self) -> &[u8] {
        unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
    }
}

impl Drop for SharedBuffer {
    fn drop(&mut self) {
        // Unmap shared memory, don't deallocate
        // munmap(self.ptr, self.len);
    }
}

Memory Allocation Compatibility

AllocatorCan use Rust Vec/String/Box?
Rust global allocatorYes
C mallocNo - use wrapper with C free
C++ newNo - use wrapper with C++ delete
Custom allocatorNo - use allocator_api
mmap/shared memoryNo - use munmap
Stack/staticNo - never "free"

Checklist

  • Who allocated this memory?
  • Is it from Rust's global allocator?
  • If not, do I have a custom Drop that frees correctly?
  • Am I copying data or taking ownership?

Related Rules

  • mem-02: Don't modify other process's memory
  • ffi-03: Implement Drop for wrapped C pointers
  • ffi-07: Don't implement Drop for types passed to external code

你可能感兴趣的文章

Do Not Expose Raw Pointers in Public APIs

Unsafe Code Authors Must Verify Safety Invariants

Ensure Consistent Data Layout for Custom Types

Add Safety Section in Docs for Public Unsafe Functions

Do Not Use Union Variants Across Different Lifetimes

Use MaybeUninit for Uninitialized Memory

Ensure C-ABI Compatibility for Strings Between Rust and C

Do Not Dereference Pointers Cast to Misaligned Types

Use Dedicated Opaque Type Pointers Instead of c_void for C Opaque Types

Choose Appropriate Data Layout for Struct/Tuple/Enum

  • 所属分类: AI
  • 本文标签: rust
  • 版权声明: 本文链接 https://seaxiang.com/blog/D22FwuSk