# DF-3776 — jme_stop()/jme_miibus_statchg() vs jme_tick() AB-BA deadlock (callout subsystem vs driver serializer)

**File:** `sys/dev/netif/jme/if_jme.c` (pass 2, GLM 5.3 re-audit)
**Class:** DF-3715 (em) / DF-3738 (emx) / DF-3760 (alc) family — independent jme(4) instance
**Severity:** Medium · **CWE:** CWE-833 (Deadlock) / CWE-667 · **Confidence:** certain (source), mechanism PROVEN on guest

## What

`jme_tick` is a self-re-arming 1 Hz callout (pinned to CPU0 via
`JME_TICK_CPUID`, if_jme.c:77/:2881) whose callback **blocks** in
`lwkt_serialize_enter(&sc->jme_serialize)` (if_jme.c:2571). `jme_stop()`
calls the **synchronous** `callout_stop(&sc->jme_tick_ch)`
(if_jme.c:2907) while holding **all** driver serializers
(`ASSERT_IFNET_SERIALIZED_ALL`, if_jme.c:2898). On this kernel
`callout_stop()` → `_callout_cancel_or_stop(..., sync=1)` ssleeps on
`"costp"` (sys/kern/kern_timeout.c:920) until an in-progress callback
finishes — so a tick that fired and parked on the serializer held by the
stopper deadlocks both threads **permanently**, and the wedged CPU0
softclock thread stops delivering every callout queued on CPU0.

## Trigger paths (all hold all serializers)

* `ifconfig jme0 down` / `up` / `mtu` / `media` → `jme_ioctl`
  (if_jme.c:1954/:1939/:1965) → `jme_stop`/`jme_init`→`jme_stop` (:2672)
* `jme_miibus_statchg` `callout_stop` at :409 (link state change)
* `jme_detach` (:1092-1093), `jme_suspend` (:1589-1590)
* `jme_watchdog` → `jme_init` (:1885/:1900)
* `kldunload`/devd detach, system suspend — all root-triggered; window
  widens arbitrarily when the stopper blocks acquiring the ring
  serializers behind a busy RX interrupt handler.

## Reproduce (guest, no jme hardware needed — protocol replica)

```
scp findings/poc/DF-3776/{Makefile,jme_callout_replica.c} dfbsd:/root/kmod/
vm.sh run_root 'cd /root/kmod && make && kldload ./jme_callout_replica.ko'
vm.sh run_root 'sysctl kern.jmereplica.arm=1; sleep 2; sysctl kern.jmereplica.stop_try=1'
vm.sh run_root '(sysctl kern.jmereplica.stop_blk=1 &) ; sleep 6; dmesg | grep "jmereplica\[blk\]" | tail -2; ps -o pid,stat,wchan,command -ax | grep stop_blk | grep -v grep'
#   -> "IF THIS IS THE LAST jmereplica LINE, THE if_jme.c AB-BA DEADLOCK IS PROVEN"
#   -> sysctl thread D5 wchan "costp"; callout_stop NEVER returned.
#   Guest is wedged: vm.sh reset with-src to recover.
```

**Expected (observed 2026-09-16, see run.log):** fix-shape control
returns with `pending=0`; shipped-shape `callout_stop()` never returns;
`ps` shows the caller asleep on `costp` (kern_timeout.c:920).

## Fix

`fix.diff` — try-enter + 1-tick retry in `jme_tick` (family fix, mirrors
DF-3715/DF-3738/DF-3760). Compile-validated: `nativekernel` rebuild of
X86_64_GENERIC with the fix → RC=0, no warnings (fix_build.log).
Mechanism validated by the replica's `stop_try` control.
