β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4381

Duplicate transaction DELETE double-dispatches xdisk state callbacks: kernel panic (KKASSERT on stock kernels; NULL deref at xaio_rcvdmsg:538 / xa_sync_completion:1127 otherwise)

Summary

txcmd's DELETE bit is only set by the writer thread; a dmsg peer that pipelines a second LNK_SPAN|DELETE while our terminating reply to the first is still queued passes every 'state reused' guard (they test only CREATE) and reaches the callback a second time. Stock kernels panic at the KKASSERT; non-KKASSERT builds dispatch into xdisk again with msg->state->any.xa_sc == NULL - lockmgr on NULL, or kdmsg_state_reply(NULL) on the BLK_OPEN variant. REPRODUCED LIVE twice from fresh resets with a purpose-built untrusted-peer harness (deterministic via a 100x PING reply backlog). Remote DoS by the cluster peer after root attaches. Core+guards fix validated on a rebuilt kernel (300-attempt PoC survived, duplicates dropped).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4381 Β· 8 files
FileTypeDescriptionSize
xdpeer.c β€” 15.0 KB view raw
panic.txt β€” 1.7 KB view raw
run.patched.log β€” 6.5 KB view raw
fix_run.log β€” 6.5 KB view raw
fix.diff β€” 2.3 KB view raw
fix_build.log β€” 25.2 KB view raw
fix_install.log β€” 80.4 KB view raw
env.txt β€” 145 B view raw

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Single-fix kernel (nativekernel rebuild) + fixed module: the exact PoC that panicked the stock kernel completed 300 attempts with no crash; dmesg shows the new duplicate-DELETE-dropped path firing 253 times and the pre-existing no-state-for-DELETE drop 47 times.

run.patched.log / fix_run.log; fix_build.log; fix_install.log; uname #1 Sat Sep 19 21:08:17 UTC 2026
↓ fix.diffper-fix-DF-4381

Confirmed kernel references

Detail

Exploit chain

untrusted dmsg peer -> LNK_SPAN|CREATE -> [100x LNK_PING|CREATE + 2x LNK_SPAN|DELETE in one write] -> duplicate DELETE double-dispatch -> kernel panic (remote DoS). No memory-corruption primitive; uid0 N/A.

Evidence (decisive lines)

panic.txt (stock-kernel serial panic + backtrace); run.patched.log / fix_run.log (300 attempts survived post-fix, 253 duplicate-DELETE-dropped); fix.diff (git-apply-able)

PoC changes

authored from scratch (pass 2, no seed). Bring-up fixes: replies must invert REVTRANS|REVCIRC routing bits; backlog must use PING|CREATE transactions (one-off pings get no reply); peer must keep draining to avoid INTERLOCK deadlock instead of double-dispatch.

Verified recommended fix

kern_dmsg.c: drop duplicate DELETE with EALREADY when rxcmd already has DELETE; xdisk: NULL-guard xa_sc in span-delete and tag->state in xa_sync_completion

Verdict

Duplicate transaction DELETEs from a dmsg peer double-dispatch xdisk state callbacks: stock kernels panic at the kern_dmsg.c:1076 KKASSERT (reproduced twice from fresh resets, serial capture in panic.txt); kernels built without KKASSERT NULL-deref in xaio_rcvdmsg (xdisk.c:538, xa_sc==NULL) or xa_sync_completion (xdisk.c:1127, tag->state==NULL). Root cause: txcmd DELETE is set only by the writer thread (kern_dmsg.c:1672), so a pipelined duplicate DELETE passes all state-reused guards. Deterministic via a PING|CREATE backlog keeping the writer >100 messages behind.