# DF-4317 — lge_jref check-then-increment runs without the jslot serializer (DF-1384 nge twin)

**What**: `lge_jref()` (if_lge.c:801-814) validates `entry->lge_inuse != 0`
(:810) and then does `atomic_add_int(&entry->lge_inuse, 1)` (:813) **without
holding `sc->lge_jslot_serializer`**, while `lge_jfree()`
(:819-841) performs its decrement-and-recycle **under** that serializer
(:833-839). This is byte-for-byte the unlocked check-then-act that DF-1384
found in nge(4) (if_nge.c nge_jref).

**Race** (needs two CPUs):
1. RX jumbo mbuf `m` (ext_arg = jslot E) is shared, e.g. by an
   `m_copym`/BPF-style copy, so the cluster refcount path calls
   `lge_jref(E)` (m_ext.ext_ref, wired at if_lge.c:688).
2. CPU0 (lge_jref): reads `E->lge_inuse == 1` → check passes.
3. CPU1 (lge_jfree on the original mbuf): takes the serializer,
   `atomic_subtract_int(&E->lge_inuse, 1)` → 0, `SLIST_INSERT_HEAD(free_list,
   E)` (:836-837), releases.
4. CPU0 (lge_jref): `atomic_add_int(&E->lge_inuse, 1)` → E is now
   `inuse == 1` **while sitting on the free list**.
5. `lge_jalloc()` (:777-794, serialized) pops E and hands the *same 9 KB DMA
   buffer* to a fresh RX descriptor (lge_newbuf :661-729) while the shared
   mbuf still references it → NIC DMA lands in a buffer the network stack is
   reading → packet data corruption; eventual double-free of E either trips
   `panic("lge_jfree: buffer already free!")` (:831) or corrupts the jslot
   freelist.

**Trigger**: any concurrent share (ext_ref) and free of an RX jumbo cluster.
Reachability of ext_ref: DragonFly mbuf code invokes `ext_ref` whenever an
external cluster is shared (m_copym of the full buffer, m_dup_pkthdr, BPF
copies). Concurrent free comes from the normal RX path on another CPU.
Hardware-gated: requires an lge(4) NIC passing traffic (guest has none:
`ifconfig -l` → `vtnet0 lo0`).

## Reproduce / verification status

Source-certain by inspection (the code path is driver-local, deterministic to
read); the runtime race is hardware-gated — the audit QEMU guest has no
Level-One LXT1001 NIC, so no live PoC is possible. Status `untested`,
confidence `likely` (identical code shape to DF-1384, which was verified as a
class; the DF-1384 fix is directly transferable).

Cross-check the twin: `sys/dev/netif/nge/if_nge.c` nge_jref/nge_jfree
(DF-1384), and note `lge_jalloc` (:782-792) is correctly serialized — only
`lge_jref` is the unlocked actor.

## Fix

`fix.diff` — take `sc->lge_jslot_serializer` around the check+increment in
`lge_jref()` (mirrors the DF-1384 fix): the serializer is per-softc and
never taken in an order that can recurse here (jref is called from mbuf
code with no driver locks held).

## Files
- `README.md` (this), `VERDICT.md`, `fix.diff`, `manifest.json`, `verdict.json`
