# DF-3577 — VERDICT

**Status: reproduced (parser logic exercised verbatim at userland, fault
deterministic).** Impact ceiling on its own: none demonstrated beyond the
OOB read (Low). Confidence: certain.

## Root cause

`iwn_read_firmware_tlv()` walks TLVs with the single generic bound check

```c
ptr += sizeof (*tlv);
if (ptr + len > end)          /* if_iwn.c:8280 — accepts len == 0..3 */
        return EINVAL;
```

and then dispatches on `tlv->type`. The `IWN_FW_TLV_PHY_CALIB` case
(if_iwn.c:8315-8320) executes `tmp = le32toh(*ptr)` **without checking
`len >= 4`**, so a TLV whose payload is shorter than 4 bytes — or has zero
length and terminates the image — makes the 4-byte load run past `end`
(the end of the firmware image resident in a kernel heap buffer obtained
from `firmware_get()`, if_iwn.c:8375-8385). The `IWN_FW_TLV_FLAGS` case
(if_iwn.c:8327-8331) proves the author knew the pattern: it rejects
`len < sizeof(uint32_t)` and `len % sizeof(uint32_t) != 0`.

The parser runs at every `ifconfig wlan0 up` (iwn_init_locked →
iwn_read_firmware → iwn_read_firmware_tlv, if_iwn.c:8933→8362→8397).

## Reproduction

Harness `iwn_tlv_oob.c` copies the parsing loop line-for-line (structs from
if_iwnreg.h:1721-1754). Crafted 96-byte image: valid TLV header (signature
"IWL\n") + one TLV {type=PHY_CALIB(15), alt=0, len=0} as the final 8 bytes.

- baseline: image mapped to end flush at a `PROT_NONE` guard page →
  `tmp = le32toh(*ptr)` reads at `end` → **SIGSEGV, exit 139** — 4/4 runs
  (run.log, run.2.log stress 3/3).
- ASAN variant exists; the guest's gcc 8.3 lacks libasan
  (`ld.gold: cannot find -lasan`), so guard-page mode is the recorded
  evidence. (build.log)
- fixed variant (`iwn_tlv_oob_fixed.c`, one added guard): same image parsed,
  **returns cleanly, no fault** — the fix eliminates the OOB at harness
  level. Kernel-level fix validation is impossible on this guest (no iwn
  hardware to load a patched kernel for), hence fix_status=not_testable with
  harness-level baseline/patched evidence recorded.

## Why this is faithful

The vulnerable code is pure parsing over a kernel-resident buffer; the
harness executes the identical statements on an identical input. The only
difference is the allocator (kmalloc vs malloc/mmap), which affects only
*whether* the OOB hits unmapped memory — the read past `end` is the defect,
and it is unconditional.

## Exploit chain

None developed: the 4 adjacent-heap bytes only influence calibration command
codes sent to the radio. No userspace-visible disclosure path exists in the
driver. Reported as a memory-safety defect (CWE-125) with Low impact.

## Fix

`fix.diff`: mirror the FLAGS-case guard:

```diff
 	case IWN_FW_TLV_PHY_CALIB:
+		if (len < sizeof (uint32_t))
+			break;
 		tmp = le32toh(*ptr);
```

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