# DF-3991 VERDICT — mechanism PROVEN on the guest; ale trigger hardware-gated (inconclusive per family)

## Root cause (path:line)

`ale_tick` — if_ale.c:2257-2272 — is a self-re-arming NON-MP callout whose
handler takes the driver serializer by BLOCKING:

```c
455:  callout_init(&sc->ale_tick_ch);              /* NON-MP */
2263: lwkt_serialize_enter(ifp->if_serializer);    /* BLOCKING enter */
2269: callout_reset(&sc->ale_tick_ch, hz, ale_tick, sc);   /* self re-arm */
2271: lwkt_serialize_exit(ifp->if_serializer);
```

`ale_stop` tears it down while HOLDING that same serializer:

```c
2544: ale_stop(struct ale_softc *sc)
2551: ASSERT_SERIALIZED(ifp->if_serializer);
2560: callout_stop(&sc->ale_tick_ch);
```

Every ale_stop caller holds the serializer: ale_ioctl SIOCSIFFLAGS-down
:1762-1764, MTU-change ale_init :1745-1748, ale_init :2319 (also reached
from the ale_intr error path :1981-1985 — i.e. from a serialized interrupt
thread), ale_detach :660-662, ale_suspend :1455.

DragonFly `callout_stop` is synchronous (sys/kern/kern_timeout.c:1091-1094
→ `_callout_cancel_or_stop(..., sync=1)` → `ssleep(c, &c->spin, 0,
"costp", 0)` at :923). A tick callback dequeued and parked inside
`lwkt_serialize_enter` deadlocks against the stopper:

  stopper:   holds serializer → sleeps in callout_stop waiting for the
             INPROG callback ('costp')
  callback:  INPROG → blocked in lwkt_serialize_enter waiting for the
             serializer the stopper holds

Permanent wedge: the interface serializer is never released, the affected
CPU's softclock thread stops delivering callouts. `ifconfig ale0 down`,
MTU change, detach, suspend, or a RX-seq-error interrupt during the 1 Hz
window triggers it.

## Guest PROOF (deterministic replica, run.log / build.log)

`ale_replica.c` replicates if_ale.c's exact protocol (non-MP
`callout_init`, blocking `lwkt_serialize_enter`, self re-arm, stop under
serializer). Executed on the audit guest (DragonFly 6.5-DEVELOPMENT
#0 Thu Jul 2 06:02:54 UTC 2026):

    ale_replica: loaded (hz=100)
    ale_replica: armed ale_tick replica (non-mp callout, ticks hz/10)
    ale_replica: tick handler BLOCKED on our serializer (fired=73 ran=72) - entering callout_stop()
    ale_replica: IF THIS IS THE LAST ale_replica LINE, AB-BA DEADLOCK PROVEN (DF-3715 family / ale instance)

    $ ps -ax -o pid,stat,wchan,comm | grep costp
    1159  D2  costp  sysctl            <- stopper wedged in callout_stop sync wait
    (still wedged 30 s later: STILL_WEDGED_AFTER_30S)

That is the exact DF-3715/DF-3935 mechanism signature (D-state, wchan
'costp', kern_timeout.c:923). Guest was reset with `vm.sh reset with-src`
afterwards.

The ale-specific end-to-end trigger (real if_ale device) is hardware-gated
on this guest: zero 0x1969 PCI devices, no hw.ale sysctl, if_ale compiled
into the kernel but never attached. Hence family-convention status
**inconclusive**, mechanism proven. Guest src md5 ae824d7a4429b82f0ad92a72b7c91da8
== audited tree.

## Impact

`ifconfig ale0 down/up` (or MTU change / detach / suspend / RX-error
interrupt) racing the 1 Hz tick → permanent kernel wedge + softclock
blackout on the callback's CPU. Unprivileged-local-triggerable on any
machine with an AR8121/8113/8114 NIC (ifconfig is unprivileged for
down/MTU? — down requires operator/interface privileges; MTU likewise; but
detach/suspend paths are root; the RX-seq-error path (:1981-1985 → ale_init
→ ale_stop) is device-triggerable). Medium DoS, matching DF-3760/DF-3935.

## Fix

fix.diff: `lwkt_serialize_try()` + early re-arm in ale_tick (the callback
can always complete; callout_stop can always return) — identical to the
validated DF-3760/DF-3935 family fix. git-apply clean. Runtime fix
validation not possible (no ale hardware); the replica demonstrates the
mechanism the fix breaks (with try-enter the callback cannot park on the
serializer).

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