GitHub Security Advisory: GHSA-xphw-cqx3-667j
Package: thin-vec (RUST)
Severity: HIGH (CVSS 7.3)
CWE: CWE-415: Double Free, CWE-416: Use After Free
Vulnerable versions: < 0.2.16
Patched in: 0.2.16

Summary: thin-vec: Use-After-Free and Double Free in IntoIter::drop When Element Drop Panics

Details:
### Summary

A **Double Free / Use-After-Free (UAF)** vulnerability has been identified in the `IntoIter::drop` and `ThinVec::clear` implementations of the `thin_vec` crate.
Both vulnerabilities share the same root cause and can trigger memory corruption using only safe Rust code — no `unsafe` blocks required.
Undefined Behavior has been confirmed via **Miri** and **AddressSanitizer (ASAN)**.

---

### Details

Both vulnerabilities share the same root cause. When a **panic occurs** during sequential element deallocation, the subsequent length cleanup code (`set_len(0)`) is never executed. During stack unwinding, the container is dropped again, causing already-freed memory to be re-freed (Double Free / UAF).

#### Vulnerability 1 — `IntoIter::drop`

**Location:** `thin-vec/src/lib.rs` L.2308~2314

`IntoIter::drop` transfers ownership of the internal buffer via `mem::replace`, then sequentially frees elements via `ptr::drop_in_place`.
If a panic occurs during element deallocation, `set_len_non_singleton(0)` is never reached. During unwinding, `vec` is dropped again, re-freeing already-freed elements.
The standard library's `std::vec::IntoIter` prevents this with a **DropGuard pattern**, but thin-vec lacks this defense.

```rust
// Problematic structure (conceptual representation)
impl<T> Drop for IntoIter<T> {
    fn drop(&mut self) {
        let mut vec = mem::replace(&mut self.vec, ThinVec::new());
        unsafe {
            ptr::drop_in_place(vec.remaining_slice_mut()); // ← panic may occur here
            vec.set_len_non_singleton(0);                  // ← unreachable on panic
        }
        // During unwinding, vec is dropped again → Double Free
    }
}
```

#### Vulnerability 2 — `ThinVec::clear`

`clear()` calls `ptr::drop_in_place(&mut self[..])` followed by `self.set_len(0)` to reset the length.
If a panic occurs during element deallocation, `set_len(0)` is never executed. When the `ThinVec` itself is subsequently dropped, already-freed elements are freed again.

```rust
// Problematic structure (conceptual representation)
pub fn clear(&mut self) {
    unsafe {
        ptr::drop_in_place(&mut self[..]); // ← panic may occur here
        self.set_len(0);                   // ← unreachable on panic
    }
    // ThinVec drop later → Double Free
}
```

#### Recommended Fix

Both vulnerabilities can be resolved with the same pattern:

- **DropGuard pattern:** Insert an RAII guard before `drop_in_place` to guarantee `set_len(0)` is called regardless of panic
- **Pre-zeroing approach:** Set the length to 0 before calling `drop_in_place`

---

### PoC

**Requirements:** Rust nightly toolchain, `thin-vec = "0.2.14"`

```bash
# Miri
cargo +nightly miri run

# ASAN
RUSTFLAGS="-Z sanitizer=address" cargo +nightly run --release
```

#### PoC-1: `IntoIter::drop`

```rust
use thin_vec::ThinVec;

struct PanicBomb(String);

impl Drop for PanicBomb {
    fn drop(&mut self) {
        if self.0 == "panic" {
            panic!("panic!");
        }