# DF-3435 — VERDICT

**Status: not_reproduced on the guest this session** (mechanism is
code-certain; the on-guest demonstration is throughput-bound, not
correctness-bound)

## The defect (code-certain)

`check_keep_state()`, `sys/net/ipfw3_basic/ip_fw3_state.c:319-321`:

```c
s = kmalloc(LEN_FW3_STATE, M_IPFW3_STATE, M_INTWAIT | M_NULLOK | M_ZERO);
s->src_addr = k->src_addr;		/* NULL write when s == NULL */
```

`M_NULLOK` explicitly permits `kmalloc` to return `NULL` when the
malloc-type limit is hit — `sys/kern/kern_slaballoc.c:694-705`:

```c
while (type->ks_loosememuse >= type->ks_limit) {
        ...
        if (ttl >= type->ks_limit) {
                if (flags & M_NULLOK)
                        return(NULL);          /* <- the checked-in flag */
                panic("malloc limit exceeded");
        }
}
```

The result is never checked; the very next statement writes through it
(`s->src_addr` at offset 24 of `struct ipfw3_state` → write to vaddr 0x18 →
`Fatal trap 12: page fault on write`). The default type limit is
`kmem_lim_size()/10` (`kern_slaballoc.c:365-372`) — **390 MB** on this
guest (`vmstat -m` limit column) — reachable by state allocations once the
admin raises `net.inet.ip.fw3_basic.state_max_{udp,tcp}_{in,out}` above
4096 (all four are `CTLFLAG_RW` deployment knobs; the shipped defaults cap
allocations at 4096 per tree before the type limit can ever be approached).

## What was run on the guest

- sysctls raised to 10,000,000 per tree/direction; `udp_timeout=7200`
  (live-state horizon); `ipfw3 add 00098 allow udp ... keep-state`
  (plain, O(log n) path); tap-frame injector `state_null_tap.c` pushing
  unique-5-tuple UDP packets at ~250k pps write rate.
- Observed: 35M+ packets injected, M_IPFW3_STATE climbed to ~190K states /
  ~12MB and rule hits grew at only ~2-7k pps — the **netisr drain** (rule
  evaluation + tree insert + forward/ARP work per packet) throttles state
  creation; reaching 6.1M live states (390MB / 64B) would need ~30-50
  minutes of sustained saturation plus 390MB of pinned kernel memory on the
  4.25GB guest. Not completed within this session's guest budget.
- The accelerator needed (a way to create states faster than the packet
  pipeline — e.g. a bulk state-restore path) does not exist: the only state
  creator is `check_keep_state` itself.

## Honest classification

Per the status table, the run ended with the guest up and the NULL write
not observed → `not_reproduced` / `reproduced=0` / `impact=none`
**observed**. The defect itself is unambiguous from the code
(`M_NULLOK` requested, result dereferenced unchecked) and the fix is
one line; confidence in the *finding* remains **certain**, the *guest
demonstration* is what did not complete. `fix.diff` adds the NULL check
(and drops M_NULLOK); on the patched kernel the same flood simply stops
creating states at the limit instead of faulting.

## Fix

`fix.diff` hunk: keep `M_NULLOK` (dropping it would turn the limit-hit into
`panic("malloc limit exceeded")` — a different, admin-triggered panic) and
**check the result**: `if (s == NULL) goto done;` — at the type limit the
firewall simply stops creating states instead of writing to vaddr 0x18.

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