# DF-3982 VERDICT — blocking callout_stop() in sbp's hard-interrupt path

**Status: inconclusive end-to-end (sbp trigger hardware-gated), mechanism
PROVEN live on the shipped guest kernel** (blocked caller in `D5 costp`
inside a critical section + INPROG softclock thread in `B2 bcb` + cascade
to an unrelated `wkdelay` sleep). Source-level certainty: certain.

## Root cause

1. `sbp_recv1` runs in the fwohci **hard-interrupt** path under
   `crit_enter()` — chain: fwohci_intr (fwohci.c:2009) → fwohci_intr_body
   (fwohci.c:2038, FWOHCI_TASKQUEUE=0 per fwohcivar.h:37) → fwohci_arcv
   `crit_enter()` (fwohci.c:2688) → fw_rcv (fwohci.c:2833) → fw_xfer_done →
   `act.hand` (firewire.c:991) → sbp_recv `crit_enter()` (sbp.c:1823).

2. On that path the driver calls the **synchronous** `callout_stop()` at
   - sbp.c:1600 (mgm_ocb_timeout, on mgm-ORB status match),
   - sbp.c:2590 (CCB timeout, in sbp_dequeue_ocb — called at sbp.c:1605),
   - sbp.c:2705 (CCB timeout, in sbp_abort_ocb via sbp_abort_all_ocbs from
     sbp_recv1's dead-agent branch sbp.c:1663-1669 → sbp_agent_reset
     sbp.c:1080, and from sbp_probe_target sbp.c:688).

3. DragonFly's current `callout_stop()` (kern_timeout.c:1091 →
   `_callout_cancel_or_stop(cc, CALLOUT_STOP, sync=1)`, kern_timeout.c:858)
   sleeps in `ssleep(..., "costp", 0)` (kern_timeout.c:923) while the
   callback is queued or INPROG on any CPU — i.e. the fwohci interrupt
   thread is put to sleep, with a critical section held, for the full
   duration of `sbp_mgm_timeout`'s body (sbp.c:2126-2146: kprintf,
   `sbp_free_ocb`, `sbp_reset_start` → `sbp_write_cmd` → `fw_asyreq`, which
   allocates — see DF-1021 for the M_WAITOK-in-crit tlabel path) or of
   `sbp_timeout`'s body (xpt_freeze_devq, abort-all). The old-BSD-callout
   semantics this 2003-era driver was written for (stop = async cancel) no
   longer hold.

4. Cascade (observed): the softclock thread blocked INPROG inside the
   callback cannot service that CPU's wheel, so unrelated `tsleep`-timeouts
   starve — a third thread stuck in `D2 wkdelay` was captured live while
   trying to release the experiment.

## What was run on the guest (DF 6.5-DEVELOPMENT, X86_64_GENERIC)

Mechanism replica B (`sbp_replica.c`, this pack): a 1-tick
`mgm_ocb_timeout`-shaped callout whose body blocks until released
(the unbounded-body shape of `sbp_mgm_timeout`), plus a sysctl handler that
performs `crit_enter(); callout_stop(&c);` — the exact state of
sbp.c:1600 — from a detached process:

```
 1237 D5     costp    sysctl      <- callout_stop caller: D-state, wchan 'costp', crit held
   -1 12 B2  bcb      softclock 2 <- target callout: INPROG, blocked in body
 1256 D2     wkdelay  sysctl      <- cascade: tsleep timeout starved (wedged wheel)
```

Negative control (RUN 1): `callout_stop()` on a *non-INPROG* callout
returned in 9 ms — the block is specifically the INPROG/queued wait of
kern_timeout.c:888-923. Both behaviors match the source.

Why not `reproduced` for the finding itself: the sbp trigger needs
FireWire hardware plus an attacker-timed status/timeout collision; the guest
has no OHCI-1394 controller (env.txt). The blocking mechanism itself was
demonstrated on the very kernel the audit targets.

## Not-a-duplicate check
DF-3715/DF-3738 (em/emx) and DF-3760 (alc) prove the same kernel-wide
callout_stop semantics in *serializer* deadlocks of net drivers; this is
the **sbp.c instance**: different driver, different context (hard-IRQ
receive path, crit-only, no serializer), different victim (fwohci IRQ
thread + softclock wheel starvation). No prior sbp.c finding covers the
callout sites.

## Fix
`fix.diff`: `callout_stop_async()` at sbp.c:1600/2590/2705 (interrupt-path
semantics of the era the driver was written for) + `sbp_mgm_timeout`
re-validating `target->mgm_ocb_cur == ocb` under crit before acting, so the
now-unstoppable callback cannot race the recv1 completion into a double
`sbp_free_ocb` of the same ocb. A complete fix is a lock/timeout redesign of
the mgm queue (flagged in the finding as follow-up).

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