2.9 KiB
2.9 KiB
id, original_id, level, impact, clippy
| id | original_id | level | impact | clippy |
|---|---|---|---|---|
| safety-01 | P.UNS.SAS.01 | P | CRITICAL | 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
// 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
// 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
- Update bookkeeping after operations: Increment length only after writing
- Use panic guards: RAII types that clean up on panic
- 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 issuessafety-02: Verify safety invariants