# 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:

```c
#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**:

```c
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:

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.
