Files
Sprimo/skills/unsafe-checker/rules/mem-03-no-auto-drop-foreign.md
2026-02-12 22:58:33 +08:00

128 lines
3.2 KiB
Markdown

---
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
```rust
// 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
```rust
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
| Allocator | Can use Rust Vec/String/Box? |
|-----------|------------------------------|
| Rust global allocator | Yes |
| C malloc | No - use wrapper with C free |
| C++ new | No - use wrapper with C++ delete |
| Custom allocator | No - use allocator_api |
| mmap/shared memory | No - use munmap |
| Stack/static | No - 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