Files
Sprimo/skills/unsafe-checker/rules/safety-01-panic-safety.md
2026-02-12 22:58:33 +08:00

114 lines
2.9 KiB
Markdown

---
id: safety-01
original_id: P.UNS.SAS.01
level: P
impact: CRITICAL
clippy: panic_in_result_fn
---
# Be Aware of Memory Safety Issues from Panics
## Summary
Panics in unsafe code can leave data structures in an inconsistent state, leading to undefined behavior when the panic is caught.
## Rationale
When a panic occurs, Rust unwinds the stack and runs destructors. If unsafe code has partially modified data, the destructors may observe invalid state.
## Bad Example
```rust
// DON'T: Panic can leave Vec in invalid state
impl<T> MyVec<T> {
pub fn push(&mut self, value: T) {
if self.len == self.cap {
self.grow(); // Might panic during allocation
}
unsafe {
// If Clone::clone() panics after incrementing len,
// drop will try to drop uninitialized memory
self.len += 1;
ptr::write(self.ptr.add(self.len - 1), value.clone());
}
}
}
```
## Good Example
```rust
// DO: Ensure panic safety by ordering operations correctly
impl<T> MyVec<T> {
pub fn push(&mut self, value: T) {
if self.len == self.cap {
self.grow();
}
unsafe {
// Write first, then increment len
// If write somehow panics, len is still valid
ptr::write(self.ptr.add(self.len), value);
self.len += 1; // Only increment after successful write
}
}
}
// DO: Use guards for complex operations
impl<T: Clone> MyVec<T> {
pub fn extend_from_slice(&mut self, slice: &[T]) {
self.reserve(slice.len());
let mut guard = PanicGuard {
vec: self,
initialized: 0,
};
for item in slice {
unsafe {
ptr::write(guard.vec.ptr.add(guard.vec.len + guard.initialized), item.clone());
guard.initialized += 1;
}
}
// Success - update len and forget guard
self.len += guard.initialized;
std::mem::forget(guard);
}
}
struct PanicGuard<'a, T> {
vec: &'a mut MyVec<T>,
initialized: usize,
}
impl<T> Drop for PanicGuard<'_, T> {
fn drop(&mut self) {
// Clean up partially initialized elements on panic
unsafe {
for i in 0..self.initialized {
ptr::drop_in_place(self.vec.ptr.add(self.vec.len + i));
}
}
}
}
```
## Key Patterns
1. **Update bookkeeping after operations**: Increment length only after writing
2. **Use panic guards**: RAII types that clean up on panic
3. **Order operations carefully**: Ensure invariants hold if panic occurs at any point
## Checklist
- [ ] What happens if this code panics at each line?
- [ ] Are all invariants maintained if we unwind from here?
- [ ] Do I need a panic guard for cleanup?
## Related Rules
- `safety-04`: Avoid double-free from panic safety issues
- `safety-02`: Verify safety invariants