DF-5321 / verdict.json
{ "finding_id": "DF-5321", "status": "reproduced", "reproduced": true, "impact": "panic", "confidence": "certain", "verdict": "Reproduced on the QEMU guest with the production firewire.c compiled into a KLD harness: the split-transaction watchdog firewire_xfer_timeout() and the fw_rcv() response path (plus the FW_ASYREQ waiters fw_xfer_free_buf/fw_tl_free) race with no cross-CPU serialization, panicking the kernel on the first attempt (tlabel use-after-free read inside firewire_xfer_timeout, movq 0x8(%rax),%rax, fault 0x8; same for the production fw_asy_callback_free double-free variant). Demonstrated impact: reliable kernel panic; characterized primitive: UAF read of a freed 16-byte tlabel (wild-pointer deref when not reused zeroed), double fw_xfer_done -> double kfree of fw_xfer, and stale wire-data stores into a freed/re-used fw_xfer while its owner frees it. uid=0 chain not built (hardware-gated trigger); fix validated: panics and leak gone with fix.diff applied to /usr/src.", "exploit_chain": "open /dev/fwN.M (operator group) -> FW_ASYREQ split transaction to a node whose response lands at the 200 ms split timeout -> fw_rcv() and firewire_xfer_timeout() both complete the xfer / the waiter frees xfer+tlabel under the scanning watchdog -> freed struct tlabel dereferenced (or fw_xfer double-freed / stale wire-data written into re-used slab object) -> kernel panic; with slab grooming on the permissive guest the double-free/UAF-write is a path to controlled corruption (not built: hardware-gated).", "evidence": "panic.txt and run_mode1_console.log (Fatal trap 12, Stopped at firewire_xfer_timeout+0xbc, pri 12 CRIT), run.log (both modes panic on attempt #1, baseline), fix_run.log (fixed: mode1 3000 attempts 0 double-completions / mode2 SURVIVED 5000 / leak 0), fix_build.log, fix.diff", "kernel_refs": [ "sys/bus/firewire/firewire.c:320", "sys/bus/firewire/firewire.c:336", "sys/bus/firewire/firewire.c:341", "sys/bus/firewire/firewire.c:348", "sys/bus/firewire/firewire.c:349", "sys/bus/firewire/firewire.c:890", "sys/bus/firewire/firewire.c:910", "sys/bus/firewire/firewire.c:1757", "sys/bus/firewire/firewire.c:1779", "sys/bus/firewire/firewire.c:1786", "sys/bus/firewire/firewire.c:1789", "sys/bus/firewire/firewire.c:1078", "sys/bus/firewire/firewire.c:2169", "sys/bus/firewire/firewirereg.h:130" ], "poc_changes": "Harness written fresh for this file (DF-5230/DF-5302 KLD pattern): compiles the production firewire.c in via #include with -D-renamed globals (incl. -Dfirewire, -D_hw_firewire, MALLOC_DEFINE types, and a MODULE_VERSION no-op shim because the #name stringify defeats -D renames and would hit interface firewire.1 already present); three lwkt threads model the receiving interrupt, the 10 Hz watchdog callout, and the fwdev FW_ASYREQ waiter; response packet hand-built per LE bitfield layout; tlabel/xfer zones prewarmed so the known DF-1021 M_WAITOK-in-crit site cannot hijack the result; fixed during bring-up: g_stop release at receiver exit, xfer->sc publish before tlabel insertion, 90 s wall-clock deadline, paced watchdog.", "attempts": 4, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 61, "guest_dirty": 0, "build_cmd": "sh build.sh (cd /root/df5321 && make; compiles production /sys/bus/firewire/firewire.c into df5321.ko)", "run_cmd": "sh run_mode1.sh (sysctl kern.df5321_mode=1; kern.df5321_iters=3000; kern.df5321_fire=1) / sh run.sh (mode 2)", "code_hash": "858fff8bd4cba49ad2c2a3d25388b80ca2b18eee26b30dc02c1ef27f16d7122d", "notes": "Baseline panics were captured on two separate fresh vm.sh reset boots (mode 1 and mode 2), identical signature. Mode-1 instrumented counters never got to print because the tlabel-UAF panic dominates on the first race hit; the counters are exercised on the FIXED kernel (0/0/0 over 3000 attempts), which also proves the fix removes every manifestation. The fw_get_tlabel restructuring in fix.diff additionally removes the known DF-1021 sleep-in-crit for this function (not claimed as DF-1021s fix).", "recommended_fix": "Serialize the tlabel lists with fc->tlabel_lock and claim xfer completion atomically with the lookup (CAS FWXF_SENT->FWXF_RCVD); kfree the tlabel unlinked by firewire_xfer_timeout; run fw_xfer_done outside the lock (fix.diff).", "fix_status": "fixed", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "Baseline: panic attempt #1 in both race modes + 5000-object tlabel leak. Patched (fix.diff on /usr/src, module rebuilt, identical triggers): mode1 completes 3000 attempts with double-completions=0 / act.hand-after-free=0 / stale-stores=0; mode2 SURVIVED 5000 attempts; leak delta 0; guest stays up.", "fix_evidence": "fix_run.log, fix_build.log, fix.diff" } |