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

Single-caller assumption unenforced: ichsmb_callback no-ops bus request/release, so concurrent /dev/smb ioctls interleave in the transaction state machine and panic KASSERT(sc->ich_cmd == -1)

Summary

The comment claims the smbus layer serializes; the callback returns success for REQUEST/RELEASE with no arbitration and smbus_request_bus adds none - fork() shares the fd and two threads interleave the state machine (lksleep drops the mutex mid-transaction; bwrite's block state is written pre-mutex): LIVE-REPRODUCED TWICE on the q35 rig ('panic: ichsmb_bread: ich_cmd=0' with two racing frames, and a quick-variant with a SECONDARY PANIC on another CPU, guest wedged both times). A callback-based fix attempt was honestly disproven live (unbalanced release panic); xfer_lock-across-each-method fix validated 3/3 clean 4-way races.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5459 Β· 11 files
FileTypeDescriptionSize
df5457_poc.c β€” 7.8 KB view raw
panic.txt β€” 604 B view raw
panic2.txt β€” 926 B view raw
baseline_run.log β€” 1.9 KB view raw
fixed_race_x3.log β€” 404 B view raw
fixed_run.log β€” 1.5 KB view raw
fix.diff β€” 7.2 KB view raw
build.sh β€” 107 B view raw
run.sh β€” 154 B view raw
env.txt β€” 496 B view raw
qemu_cmdline.txt β€” 392 B view raw

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Baseline modules: 2/2 race runs panic (panic.txt, panic2.txt). Patched ichsmb.ko+smb.ko rebuilt in-guest from fix.diff: 3/3 race runs clean, guest alive, zero panic/lockmgr dmesg lines (fixed_race_x3.log).

fixed_race_x3.log; fixed_run.log; build output in VERDICT.md
↓ fix.diffper-fix-DF-5459

Confirmed kernel references

Detail

Exploit chain

root (or wheel with 0600 devfs node relaxed) opens /dev/smb0; fork shares the single-open fd; children issue SMB_QUICK_READ/SMB_BREAD/SMB_BWRITE concurrently; second caller enters a method while the first sleeps in ichsmb_wait (lksleep drops sc->mutex) -> KASSERT panic -> DDB wedge. On INVARIANTS-free builds the same interleave corrupts the block state machine instead.

Evidence (decisive lines)

panic.txt (run 1: ichsmb_bread), panic2.txt (run 2: ichsmb_quick + secondary CPU panic), fixed_race_x3.log (3 clean races on patched modules, 'ALL_RACES_DONE_GUEST_ALIVE', dmesg panic/lockmgr count 0), fix.diff

PoC changes

wrote mode 'race' (4 forked fd-sharers, randomized QUICK/BREAD/BWRITE against the QEMU smbus-ipmi BMC at 0x20) into df5457_poc.c; built a q35 rig (first live ichsmb hardware in this audit)

Verified recommended fix

Add sc->xfer_lock (lockinit in ichsmb_attach, uninit in fail/detach) and acquire LK_EXCLUSIVE across the full body of all ten SMBus methods so ich_cmd/block_* state is never visible to a second caller; keep ichsmb_callback stateless.

Verdict

REPRODUCED: concurrent /dev/smb0 ioctls from forked processes sharing one fd interleave inside the unserialized ichsmb transaction state machine (ichsmb_callback no-ops the bus request, ichsmb.c:148-165) and panic the always-compiled KASSERT(sc->ich_cmd == -1) at ichsmb.c:174/391/422 β€” two baseline runs panicked from two different entry points ('ichsmb_bread: ich_cmd=0' with two racing ichsmb_bread frames, then 'ichsmb_quick: ich_cmd=0' with a SECONDARY PANIC on another CPU). Privileged local DoS (device node 0600 root:wheel). Fix (xfer_lock across each method) validated live: 3/3 races clean on patched modules vs 2/2 panics on baseline.