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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5564 Β· 14 files
FileTypeDescriptionSize
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):

(SMB_WAIT == IIC_WAIT == 0x1, sys/bus/smbus/smbconf.h:41-44.)

Consequences:

  1. 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) from iicbus_release_bus() (iiconf.c:145).
  2. Lost wakeup on a FREE bus β€” iicbus_release_bus()'s EACCES path returns before wakeup(sc) (iiconf.c:137-140 early-return vs :141-145), and the requester's check-then-sleep in iicbus_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

fixed
baseline reproduced→ patch + rebuild →patched clean

With 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)
↓ fix.diffper-fix-DF-5564

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.