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

SMB_WAIT-only requesters park unkillably in smbreq: tsleep without PCATCH and without timeout (DF-5563/5564 twins at the smbus layer)

Summary

SMB_NOINTR is 0 so plain SMB_WAIT - exactly what ipmi_ssif and cyapa pass - selects tsleep(sc,0,"smbreq",0): no PCATCH, no timeout. A user process reaching this sleep sits D-state and survives kill -9 until something releases the bus; the check-then-sleep window can strand a waiter on a free bus forever, and no detach drain makes kldunload-under-a-parked-waiter the DF-5636-class freed-text resume. Reproduced (D4 smbreq before/after kill -9; unwedged only by unpark); PCATCH+hz-bound fix validated (kill -9 works immediately, self-heals lost wakeups).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5638 Β· 14 files
FileTypeDescriptionSize
df5636_rig.c β€” 22.5 KB view raw
df5636ctl.c β€” 1.5 KB view raw
iicbb_reg.c β€” 491 B view raw
Makefile.rig β€” 279 B ↓ download
Makefile.reg β€” 228 B ↓ download
build.sh β€” 82 B view raw
run.sh β€” 204 B view raw
build.log β€” 1.1 KB view raw
run_baseline.log β€” 5.2 KB view raw
run_fixed.log β€” 2.9 KB view raw
env.txt β€” 512 B view raw
fix.diff β€” 2.2 KB view raw
VERDICT.md β€” 2.4 KB ↓ raw
verdict.json β€” 3.1 KB view raw
VERDICT.md
↓ download raw

DF-5638 VERDICT β€” REPRODUCED

Root cause: smbus_poll() (sys/bus/smbus/smbconf.c:82-102) sleeps tsleep(sc, 0, "smbreq", 0) for SMB_WAIT|SMB_NOINTR (smbconf.c:92-94). SMB_NOINTR is 0 (smbconf.h:42), so every caller passing plain SMB_WAIT β€” in-tree: ipmi_ssif.c:81 and :181, cyapa.c:1176 and :1624 β€” parks with no PCATCH and no timeout. If the smbus is held (by another child, by a wedged owner β€” see DF-5637's permanent wedge β€” or via the check-then-sleep lost-wakeup window in smbconf.c:120-124 where a release's wakeup fires between the failed callback and the tsleep), the calling thread sleeps indefinitely and cannot be killed.

Reproduced (stock kernel, DF-5636 rig): ./df5636ctl park holds the bus with devA and calls smbus_request_bus(smbus, devB, SMB_WAIT=0x1) from a user process: ps shows the process in state D4 on wchan smbreq; kill -9 has no effect; the SIGKILL is delivered only after ./df5636ctl unpark releases the bus (and the wake path additionally hands the parker the DF-5636 phantom grant, <<< phantom grant on wake).

Fixed kernel (fix.diff, kernel #3): smbus_poll always sleeps with PCATCH and an hz bound (mirroring the DF-5564 iiconf fix). The parker now shows S2 smbreq and kill -9 kills it immediately β€” verified in the same session (kill -9 WORKED: parker died). The hz bound also converts the lost-wakeup strand into a 1-second self-healing retry.

Residual (documented, not in this fix): no detach drain β€” kldunload of the smbus/iicsmb modules under a now bounded waiter is still not explicitly serialized; with PCATCH+hz the waiter is killable and re-checks within a second, which defuses the DF-5563-class freed-text resume practically, but a proper drain flag (cf. the DF-5563 fix's dying/waiters/spinlock) belongs to a family-wide rework of both iiconf and smbconf.

Classification: status=reproduced, reproduced=1, impact=dos, confidence certain. Severity Low (DF-5563/5564 twin band): unkillable-state local DoS reachable from root-only device entry points or kernel threads.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Patched kernel: parker state S2 (killable) vs D4 (unkillable) on stock; kill -9 terminates the parked process immediately; the 1s poll bound self-heals lost wakeups.

run_fixed.log PARK killability section vs run_baseline.log PARK section; fix.diff sha256 346fe6893056aecf467e64d12285a0de337461aee150737964c0c3f5f06fbc2a
↓ fix.diffper-fix-DF-5638

Confirmed kernel references

Detail

Evidence (decisive lines)

run_baseline.log PARK section: 'D4 smbreq' before and after kill -9, unwedge via unpark, '<<< phantom grant on wake'; run_fixed.log: 'S2 smbreq' + 'kill -9 WORKED: parker died (PCATCH fix effective)'

PoC changes

shared rig with DF-5636; the PARK/UNPARK ioctls park a real user process (not a kthread) so unkillability is observable via ps/kill

Verified recommended fix

Make smbus_poll always sleep with PCATCH and an hz-bounded timeout (and treat the timeout as a re-check, per DF-5636's loop fix); see pack fix.diff, shared with DF-5636.

Verdict

smbus_poll (sys/bus/smbus/smbconf.c:92-94) parks SMB_WAIT|SMB_NOINTR callers in tsleep(sc, 0, "smbreq", 0) -- no PCATCH, no timeout (SMB_NOINTR is 0 per smbconf.h:42; in-tree plain-SMB_WAIT callers: ipmi_ssif.c:81,181 and cyapa.c:1176,1624). Reproduced on the stock kernel with the DF-5636 rig: a user process calling smbus_request_bus(devB, SMB_WAIT) while another device held the bus entered state D4 on wchan smbreq and SURVIVED kill -9; the pending SIGKILL was delivered only after the bus was released (and the wake path handed it a phantom grant besides). The check-then-sleep window in the first do-while (smbconf.c:120-124) can additionally strand a waiter on a free bus forever. fix.diff makes smbus_poll always PCATCH with an hz bound: on the rebuilt kernel the parker sits in S2 smbreq and kill -9 kills it immediately, and the timeout turns any lost wakeup into a 1-second retry. Residual: no detach drain (noted in VERDICT.md).