# DF-3046 — VERDICT

**Status: reproduced** (the unvalidated read demonstrably happens, with
planted-byte attribution at every production shape), **impact: none**
(bounded, in-allocation, no leak/corruption/crash — exactly what the Info
finding claims), **confidence: certain**.

## Line-accurate mechanics

`bootpc_tag_helper` (bootp_subr.c:1263-1304) walks options with the guard
`if (j >= ej || j + *j + 1 > ej) { badopt = 1; return; }` (:1283-1286),
consumes `len = *j++` (:1287), and for `code == TAG_OVERLOAD` executes
`tctx->overload = *j` (:1299-1300) with **no check that len == 1**.

* **len == 0, length byte is the final byte of the region**: the guard
  admits it (`j + *j + 1 == ej`, not `> ej`), `len = *j++` leaves j == ej,
  and `*j` reads one byte past the region end. Reproduced at every
  production shape:
  * bare region (guard byte 0xa5 adopted);
  * vend walk of a full-size reply (replylen == sizeof(struct
    bootp_packet) == 1460 — note the struct carries 2 bytes of tail
    padding): reads `bp + sizeof` — past the struct (0x03 adopted);
  * vend walk of a 1458-byte reply: reads the tail padding just past the
    `vend[1222]` member (0xee adopted);
  * sname walk (overload=SNAME from a valid option): reads `bp->file[0]`,
    the adjacent in-struct field (0x5c / 0x77 tracked on repeat);
  * file walk: reads `bp->vend[0]` (the cookie byte, 99) — and because 99
    has OVERLOAD_SNAME set, that OOB value then triggers a further sname
    walk which adopts `bp->file[0]` — a chained double-OOB adoption.
* **len != 1 (e.g. 3)**: the first value byte is taken as the overload
  flag regardless of length (0x02 adopted from {2,7,9}).

## Impact analysis (why none)

* The adopted value only selects which **fixed-size, in-struct** areas
  (file[128], sname[64]) are subsequently walked by the same bounded loop.
* For the vend walk at replylen == sizeof, the byte past the region is the
  struct tail padding or, at exactly sizeof, the first byte past the
  struct — which in both production contexts (`struct bootpc_globalcontext`
  and `struct bootpc_ifcontext`, both kmalloc'd) is another member of the
  same allocation (`replylen`). Never out of the allocation; no page fault,
  no info leak to userspace, no corruption. Ran on the INVARIANTS kernel
  with zero side effects across repeated runs.
* Not attacker-reachable beyond what a DHCP reply already controls: the
  value read is packet-adjacent bytes the attacker usually also authored.

## Fix validation

One-hunk `fix.diff` (require `len == 1` for TAG_OVERLOAD, else `badopt=1`
and abort the walk — with len==1 the existing :1283 guard already proves
`*j` is in-region). Fixed-kernel run (`run.fixed.log` / fix_validation.log):
every planted byte rejected (`badopt=1`, overload stays 0 or the legitimate
len==1 value, constant across planted-byte changes); no behavior change for
well-formed replies.

Residual note: the chained-propagation shape (T1f) disappears entirely
because the malformed overload never sets the re-walk flags.

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