Panic-safety unsoundness in Map::into_iter, and an uninitialised Arc in SharedIncin::clear

Two independent soundness problems in lockfree 0.5.1. Both are reachable from safe Rust.

1. Panic-safety unsoundness in Map::into_iter

Map::into_iter has a panic-safety unsoundness. It manually drops builder (the caller-provided hasher H) and incin, then commits the ownership transfer with mem::forget(self). H::drop is user-controlled and can panic; if it does, mem::forget(self) is skipped and the still-live Map is dropped during unwinding, whose field drop glue drops builder a second time. For a builder that owns an allocation, that's a double-free (CWE-415) / use-after-free (CWE-416) reachable from safe Rust. An empty map is enough — no entries or concurrency needed.

src/map/mod.rs:

fn into_iter(mut self) -> Self::IntoIter {
    let raw = self.top.raw();
    unsafe {
        (&mut self.builder as *mut H).drop_in_place();   // H::drop() may panic
        (&mut self.incin as *mut SharedIncin<K, V>).drop_in_place();
        mem::forget(self);                                // skipped on panic
        IntoIter::new(OwnedAlloc::from_raw(raw))
    }
}

Map implements Drop, and after it runs, field drop glue destroys builder again. So any panic before mem::forget(self) leaves builder dropped twice.

Map::with_hasher + into_iter with a custom BuildHasher whose Drop panics (valid safe Rust) triggers it. With a builder that owns a heap Vec followed by a one-shot panicking field, the PoC reports attempting double-free under AddressSanitizer.

Suggested fix: wrap self in mem::ManuallyDrop at the start instead of relying on mem::forget(self) after the fallible teardown. Then a panic in builder's Drop leaks the remaining fields rather than dropping builder twice.

2. SharedIncin::clear creates an uninitialised Arc

make_shared_incin! expands clear() into this (src/incin.rs:324):

let arc = unsafe {
    replace(&mut self.inner, uninitialized())
};

Arc has a validity invariant, so mem::uninitialized::<Arc<_>>() is UB at the point of creation. No panic, no concurrency, no particular element type:

fn main() {
    let mut incin = lockfree::stack::SharedIncin::<String>::new();
    incin.clear();
}
error: Undefined Behavior: reading memory at alloc4679[0x0..0x8], but memory is
uninitialized at [0x0..0x8], and this operation requires initialized memory
   --> src/incin.rs:332:50
    |
332 |                           replace(&mut self.inner, uninitialized())
    |                                                    ^^^^^^^^^^^^^^^

There's a second problem in the same window. Between that replace and the repairing one at line 338, self.inner holds garbage while self is still a live, droppable value. incin.clear() runs T::drop for every pending item and Arc::new(incin) allocates, so either can unwind — and then SharedIncin::drop decrements a refcount through an uninitialised pointer.

The macro is instantiated five times, so this covers queue, stack, map, channel::spmc and channel::mpmc.

Suggested fix: make the field Option<Arc<..>> and use Option::take, which leaves a valid None in the slot. The comment says Arc::get_mut was avoided because it "locks stuff" — that isn't the case; it does atomic refcount checks, not locking. Using it would remove the unsafe here entirely.

Confirmed on 0.5.1.

Edited by SonJuHyung