IIC_NOINTR is 0, so WAIT-only requesters sleep without PCATCH and without timeout β unkillable indefinite park (ipmi_ssif/cyapa via SMB_WAIT passthrough), compounded by a lost-wakeup path that can strand sleepers on a free bus forever
Summary
IIC_NOINTR==0 collapses the WAIT|NOINTR case onto every WAIT-only caller: tsleep(sc, 0, 'iicreq', 0) - no PCATCH, no timeout; smbus forwards how verbatim so ipmi_ssif/cyapa park unkillably whenever another child owns the bus. The EACCES path of release_bus also returns before wakeup(sc) and the requester's check-then-sleep is unordered against release's clear-then-wakeup -> a missed wakeup strands the sleeper forever on a free bus. Rig: parked D1, SIGKILL SURVIVED, non-owner release EACCES with STILL_PARKED, real wakeup clears. PCATCH+hz-bounded fix validated (1s slices, SIGKILL terminates, self-heals lost wakeups).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5564 Β· 14 files| File | Type | Description | Size | |
|---|---|---|---|---|
| df5563_rig.c | β | 12.7 KB | view raw | |
| iicbb_reg.c | β | 491 B | view raw | |
| park.c | β | 3.2 KB | view raw | |
| Makefile.rig | β | 202 B | β download | |
| Makefile.reg | β | 189 B | β download | |
| build.sh | β | 111 B | view raw | |
| build.log | β | 13.5 KB | view raw | |
| run.sh | β | 181 B | view raw | |
| run.log | β | 770 B | view raw | |
| fix_run.log | β | 1.1 KB | view raw | |
| env.txt | β | 400 B | view raw | |
| fix.diff | β | 5.2 KB | view raw | |
| README.md | β | 3.2 KB | β raw | |
| verdict.json | β | 3.3 KB | view raw |
DF-5564 β IIC_WAIT-only requesters sleep without PCATCH and without timeout: unkillable indefinite park (+ lost-wakeup on the free bus)
- File under audit:
sys/bus/iicbus/iiconf.c(pass 2) - Class: local/kernel-thread DoS (unkillable sleep)
- Severity: Low (root-gated / kthread-only in-tree callers)
- Verdict: REPRODUCED (non-destructive), fix validated
The bug
iiconf.h:43-45 defines:
#define IIC_DONTWAIT 0
#define IIC_NOINTR 0 /* <-- zero! */
#define IIC_WAIT 0x1
so IIC_WAIT | IIC_NOINTR collapses to plain IIC_WAIT (0x1) β the
case at iiconf.c:66-68 matches every WAIT-only caller:
case IIC_WAIT | IIC_NOINTR:
error = tsleep(sc, 0, "iicreq", 0); /* no PCATCH, no timeout */
In-tree WAIT-only callers reach it through the smbus passthrough, which
forwards how verbatim (smbus_request_bus, sys/bus/smbus/smbconf.c:112-146
β iicsmb_callback, sys/bus/iicbus/iicsmb.c:250-267):
sys/dev/misc/ipmi/ipmi_ssif.c:81,181βsmbus_request_bus(smbus, dev, SMB_WAIT)sys/dev/smbus/cyapa/cyapa.c:1176,1624βsmbus_request_bus(bus, sc->dev, SMB_WAIT)
(SMB_WAIT == IIC_WAIT == 0x1, sys/bus/smbus/smbconf.h:41-44.)
Consequences:
- Unkillable park β no PCATCH means SIGKILL is ignored while the
iicbus is owned by another child or the bridge reports busy. An IPMI
SSIF poller or cyapa thread that loses the bus can never be signaled
out; the only exit is a legitimate
wakeup(sc)fromiicbus_release_bus()(iiconf.c:145). - Lost wakeup on a FREE bus β
iicbus_release_bus()'s EACCES path returns beforewakeup(sc)(iiconf.c:137-140early-return vs:141-145), and the requester's check-then-sleep iniicbus_request_bus(:99-108) is not ordered against release's clear-then-wakeup. A sleeper that parks just after an owner releases never sees the wakeup and sleeps forever on a free bus (the smbus twin wakes unconditionally,smbusconf.c:180). With timeout 0 there is no self-healing.
Rig
Same rig as DF-5563 (df5563_rig.c): real iicbus.ko/iicbb.ko under a
fake GPIO bridge; hw.df5563.busy=1 makes the bridge callback report
busy; ./park wait issues iicbus_request_bus(bus, dev, IIC_WAIT) β the
ipmi_ssif/cyapa contract.
Reproduce (non-destructive)
sh build.sh && (load chain per run.sh) sysctl hw.df5563.busy=1 ./park wait & # parks at wchan 'iicreq' kill -9 $! ; sleep 3 # process SURVIVES ./park rel # EACCES -- and does NOT wake the sleeper sysctl hw.df5563.busy=0 ./park intr # request+release cycle -> wakeup(sc) frees it
Expected output (baseline)
PID STAT WCHAN COMMAND 1981 D1 iicreq ./park wait --- kill -9 --- SURVIVED_SIGKILL=YES <-- unkillable [park 1986] ioctl: returned -1 (errno=13) <-- non-owner release: EACCES, no wakeup STILL_PARKED_AFTER_REL=YES GONE_AFTER_WAKEUP=YES <-- only wakeup(sc) frees it
Fix
Covered by DF-5563's fix.diff: iicbus_poll() always PCATCH and bounded
(hz), timeout treated as spurious wakeup. Validation (fix_run.log):
the WAIT-only sleeper parks in 1s slices (S1 iicreq) and SIGKILL
terminates it (F2V_PASS); the sanity round trip still passes.
Fix verification
fixedWith fix.diff the WAIT-only sleeper parks in 1s slices and SIGKILL terminates it (F2V_PASS in fix_run.log); the bounded timeout also self-heals the lost-wakeup case within one second.
findings/poc/DF-5564/fix_run.log (F2v section); fix.diff (shared with DF-5563)
Confirmed kernel references
Detail
Exploit chain
root: hold /dev/iic0 mid-ioctl (or any busy bridge) while an ipmi_ssif/cyapa kthread requests with SMB_WAIT -> kthread parks at iiconf.c:67 with no PCATCH -> SIGKILL impossible, timeout 0 means no self-heal even after the bus frees (lost wakeup) -> IPMI/cyapa interface permanently wedged. Userland demonstration via the rig's /dev/df5563 with the identical (bus, dev, IIC_WAIT) call contract.
Evidence (decisive lines)
run.log: 'SURVIVED_SIGKILL=YES', 'ioctl: returned -1 (errno=13: Permission denied)' + 'STILL_PARKED_AFTER_REL=YES', then 'GONE_AFTER_WAKEUP=YES' only after ./park intr cycled the bus. fix_run.log: 'F2V_PASS: SIGKILL terminated the WAIT-only sleeper' with the patched iicbus.ko (PCATCH + hz-bounded, timeout-as-spurious-wakeup).
PoC changes
Shared rig with DF-5563; ./park wait drives iicbus_request_bus(bus, dev, IIC_WAIT) -- the exact how value the smbus passthrough delivers for ipmi_ssif/cyapa. No source changes beyond the DF-5563 rig fixes.
Verified recommended fix
Always PCATCH + bounded (hz) tsleep in iicbus_poll with timeout treated as spurious wakeup (DF-5563 fix.diff).
Verdict
IIC_NOINTR is defined as 0 (iiconf.h:44), so case IIC_WAIT | IIC_NOINTR at iiconf.c:66-68 matches every WAIT-only caller and tsleeps WITHOUT PCATCH and with timeout 0; the smbus layer forwards how verbatim, so ipmi_ssif (ipmi_ssif.c:81,181) and cyapa (cyapa.c:1176,1624) callers of smbus_request_bus(..., SMB_WAIT==IIC_WAIT) park unkillably whenever another iicbus child holds the bus. Demonstrated on the guest: a WAIT-only requester parked at wchan 'iicreq' ignored SIGKILL (SURVIVED_SIGKILL=YES), a non-owner release returned EACCES without waking it (iicbus_release_bus's EACCES path early-returns before wakeup, iiconf.c:137-145 -- the lost-wakeup twin), and only a genuine wakeup(sc) from another child cycling the bus freed it.
No comments yet.