Files
punktfunk/crates/punktfunk-core/src
enricobuehler bcfb833ff7 fix(abi): the panic boundary was documented as universal and wasn't
`abi.rs`'s header states "panics never cross the boundary: every entry point is wrapped in
`catch_unwind`". Of its 78 `extern "C"` entry points, 16 were not. Ten of those are fine and were
always fine — `punktfunk_abi_version` returns a constant, and the nine `punktfunk_connect*` shims
forward every argument unchanged into a guarded implementation — but five ran real code bare:
`session_free`, `connection_close`, `connection_disconnect_quit`, `reanchor_gate_free`,
`reanchor_gate_arm`.

The three `*_free`/`close` ones are the point. They run `Drop` for an entire `Session` or
`Connection` — transports, threads, mutexes — and a `Drop` impl that unwraps a poisoned lock panics.
Since Rust 1.81 that unwind is a hard abort rather than undefined behaviour, so this is not a
soundness hole; it is worse-behaved than it looks. Aborting the CALLER'S process because one of our
teardown paths hit a poisoned mutex is not an acceptable failure mode for a library, and it would
present as "the app died in punktfunk_session_free" with no Rust backtrace to explain it.

Adds `guard_void`, the sibling of `guard` for entry points with no status to report through: it
catches, logs, and returns — right for teardown, where the object is going away regardless. The five
are wrapped in it.

The header now says what is actually true, including WHICH entry points are deliberately bare and
why, so the next reader can check the claim instead of trusting it. That is the same failure this
sweep found in `MappedView`'s `Sync` proof: a stated invariant that a reviewer would rely on and that
had quietly stopped holding.

Verified: Linux .21 fmt + both CI clippy steps rc=0, and the C ABI harness — which exercises the
`*_free`/`close` paths it touches — still passes, 4 frames byte-exact through lossy loopback.
2026-07-29 08:48:42 +02:00
..