# DF-3777 — VERDICT

**Status: source-level certain (loop math) and demonstrated on the guest
kernel by a verbatim-loop replica (200M-iteration guard fully consumed,
zero reposts, consumer unchanged); jme-specific runtime hardware-gated
(no JMicron NIC on the guest, and the trigger additionally requires a
faulty/malicious controller) → inconclusive per family convention,
reproduced=0, impact=none-on-this-guest. Class of impact on faulty
hardware: unbounded RX interrupt-handler spin holding the ring
serializer (interface RX halt + one CPU softlock).**

## Root cause (path:line)

1. `sys/dev/netif/jme/if_jme.c:2511-2518` — the only validation of the
   controller-DMA'd RX descriptor is
   `nsegs != howmany(pktlen, MCLBYTES)`. `JME_RX_NSEGS` is a 7-bit
   field (sys/dev/netif/jme/if_jmereg.h:985-986) and `JME_RX_BYTES`
   a 16-bit field (:995). The pair **(0, 0) passes** the check because
   `howmany(0, 2048) == 0`.
2. `sys/dev/netif/jme/if_jme.c:2304-2486` — `jme_rxpkt()` with
   nsegs == 0: the segment loop `for (count = 0; count < nsegs;
   count++)` (:2361) never runs → **neither `jme_newbuf()` nor
   `jme_discard_rxbufs()` reposts the descriptor**; the error path is
   not taken either (JME_RX_ERR_STAT clear). `rdata->jme_rx_cons +=
   nsegs` (:2484) advances by 0.
3. ⇒ the enclosing `for (;;)` in `jme_rxeof` (:2494) re-reads the same
   OWN-clear (:2500), VALID-set (:2502) descriptor forever. Callers
   pass count = -1 (unbounded) from `jme_msix_rx` (:3845),
   `jme_rx_intr` (:3500) and `jme_rx_restart` (:3882): the interrupt
   thread spins in-kernel holding `rdata->jme_rx_serialize`, RX halts,
   and with legacy/MSI interrupts `sc->jme_serialize` (main) is also
   held for the whole spin.
4. Also note the **positive** result of the pass-2 RX checklist: the
   :2513 cross-check *does* close the DF-3604/3639/3663/3679/3713/3759
   heap-OOB-read family for jme — pktlen is bound to nsegs ≤ 32 posted
   2048-byte clusters (pktlen ≤ 65535 ⇒ howmany ≤ 32), and the chain
   trimming at :2403-2413 keeps every m_len within its cluster. Only
   the degenerate (0,0) pair and the (benign) pktlen<10 negative-length
   edge survive: the negative m_len is dropped by `ether_input`
   (`m->m_len < sizeof(struct ether_header)`,
   sys/net/if_ethersubr.c:1501) — defensive depth only, no memory
   unsafety.

## Threat model

Compliant JMicron silicon always reports nsegs ≥ 1 for a completed
frame (and runt/error frames carry JME_RX_ERR_STAT → discarded at
:2348-2357). The trigger requires a faulty or malicious controller
writing crafted descriptor state via DMA — the same threat model the
audit applied to the DF-3604 family. No unprivileged-local or remote
software trigger exists.

## Verification performed (guest, DragonFly 6.5-DEVELOPMENT x86_64)

Presence probe (run.log): no JMicron PCI device (`vtnet0 lo0` only);
driver compiled into the running kernel (`kldstat -v`: `103 pci/if_jme`)
→ runtime hardware-gated.

Loop math, demonstrated by `jme_callout_replica.ko` (the module shared
with DF-3776; `repl_rxeof_spin()` is the verbatim :2494-2562 loop logic
against a simulated 16-descriptor ring whose desc[0] is OWN-clear,
VALID-set, nsegs=0, pktlen=0):

* `sysctl kern.jmereplica.rxspin=1` (broken shape):
  `jmereplica[rxspin]: guard=-1 iterations=200000000 reposts=0
   rx_cons 0 -> 0` — the entire 200,000,000-iteration guard was
  consumed (the guard is the ONLY thing that ended the loop; the real
  code has none), zero reposts performed, consumer pointer unchanged —
  i.e. the descriptor is provably never re-armed and the loop provably
  never terminates.
* `sysctl kern.jmereplica.rxspin_fix=1` (fix.diff shape):
  `jmereplica[rxspin_fix]: iterations=2 reposts=1 rx_cons 0 -> 1
   (terminates; degenerate desc reposted)` — the fixed logic exits
  after reposting and advancing past the degenerate descriptor.

## Fix validation

* `fix.diff` (git-apply clean): `if (nsegs < 1)` before the howmany
  check → repost the single degenerate descriptor
  (`jme_discard_rxbufs(rdata, rdata->jme_rx_cons, 1)`) and advance
  (`JME_DESC_INC`), then `continue`.
* Mechanism validated live by the `rxspin_fix` replica control
  (terminates in 2 iterations, 1 repost).
* Compile-validated on the guest: all three jme fixes (DF-3776/3777/3778)
  applied together, incremental `nativekernel` X86_64_GENERIC build →
  RC=0, zero warnings (fix_build.log).
* Driver-level runtime validation impossible without (faulty) hardware →
  fix_status: not_testable.

## Bottom line

Robustness hole in the RX descriptor validation: the consistency check
admits (nsegs=0, pktlen=0), which makes the handler neither repost nor
advance past the descriptor — an unbounded in-handler spin on faulty
hardware. The surrounding length handling is otherwise sound (the
DF-3604 family OOB does not apply to jme). One-line-class fix,
compile-clean, mechanism-proven on the guest.

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