# DF-3369 — VERDICT

**Status: reproduced** (non-terminating kernel loop proven in-kernel with the
exact predicted arithmetic; impact: unkillable NIOCRXSYNC holding the kring
q_lock exclusively — local DoS).

## What was proven, on the guest

Verbatim `bdg_netmap_rxsync` (netmap_vale.c:1485-1535) in a KLD on stock
kernel #0, driven with the exact ring state an mmap()ed user thread writes:

* control (reserved=4): terminates in 1 iteration (`run.log`).
* attack (`cur=5, reserved=0xFFFFFFFF, avail=1`, 1024-slot ring):
  - the only sanity check `resvd + ring->avail >= lim + 1`
    (netmap_vale.c:1503) prints `0x00000000 >= 1024 ? FALSE (wrap defeats
    check)` — 0xFFFFFFFF+1 wrapped to 0;
  - `k = 5 + 1024 + 1 = 1030`, reported by the module as
    `k = 1030 is OUTSIDE [0..1023]`;
  - the verbatim release loop (`for (n = 0; j != k; n++) … j = nm_next(j, lim)`,
    netmap_vale.c:1515) ran **1,000,000 iterations without j reaching k** —
    j only ever cycles [0..1023]. The module's bail-out (INSTRUMENTATION,
    marked) is the only reason it returned; the in-tree code has none.

On the real subsystem the spinning thread holds `kring->q_lock`
LK_EXCLUSIVE (netmap_vale.c:1493), never returns to user mode (unkillable),
and the port's ring is locked forever.

## Root cause, path:line

netmap_vale.c:1502-1508: `ring->reserved` (user-writable shared memory) is
only validated by the wrap-able expression `resvd + ring->avail >= lim + 1`;
a reserved value ≥ ring size must be rejected outright.  The derived `k`
then leaves the ring index space and the `j != k` loop cannot terminate.
(Same *class* as DF-3332 — unvalidated ring->reserved in netmap.c's
netmap_grab_packets — but a different function, file, and failure mode.)

## Why DoS and not more

The loop body only clears `NS_BUF_CHANGED` in slot flags and advances j —
no write primitive beyond the (already user-writable) shared ring. Impact
ceiling = permanent kernel-thread wedge + ring lock, i.e. local DoS.

## PoC changes vs the seeded sketch

None (authored in this pass). Bail-out counter and the mode sysctl are the
marked INSTRUMENTATION additions; loop control flow untouched.

## Fix validation (A/B, same module)

`-DDD3369_FIXED` adds `resvd >= lim + 1 ||` to the check (tree patch in
fix.diff): attack input now prints the `XXX invalid reserve/avail 4294967295 1`
diagnostic, resets reserved, and **terminates in 5 iterations**
(`run.log`). Full-subsystem kernel build impossible on this platform
(netmap build rot — platform note), so verbatim A/B is the maximal
validation.

**Kernel refs:** sys/net/netmap/netmap_vale.c:1485-1493 (lock), :1502-1508
(wrap + k computation), :1510-1528 (release loop), :1551 (nm_rxsync wiring),
:914-918 (num_slots bound at port creation); reachability:
NIOCRXSYNC → nm_rxsync on any vale port.

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