# DF-3045 — VERDICT

**Status: reproduced** (mechanically: primitive-level canary smash + in-situ
fatal kernel stack corruption through the verbatim production call sites),
**impact on this guest: panic** (fatal GPF with control-flow-corrupting
frame overwrite), **real-world reachability: driver-gated** — exactly the
gate the finding describes; on a stock system with only `ether_ifattach`
interfaces (sdl_alen=6, if_ethersubr.c:620,647) none of the cited uses can
see alen > 16, so unprivileged-local and crafted-network reach is nil.

## Chain, line-accurate

1. `bootpc_fakeup_interface` :1046-1053 selects any AF_LINK sockaddr with
   `sdl_type == IFT_ETHER` — `sdl_alen` is never validated. Proven on the
   guest with a fabricated alen=17 / alen=255 sockaddr_dl on a fake ifnet
   walked by the verbatim TAILQ_FOREACH.
2. `bootpc_compose_query` :1192 `call.hlen = ifctx->sdl->sdl_alen` and
   :1197 `bcopy(LLADDR(sdl), &ifctx->call.chaddr, sdl_alen)` — chaddr is
   `unsigned char chaddr[16]` (:102). Observed: alen=17 puts MAC byte 17
   (0x50) into `call.sname[0]`; alen=255 spills 239 bytes into
   sname/file/vend of the outgoing query (in-struct; the vend prefix is
   then legitimately rewritten by the cookie/options writes at :1199+).
3. Reply matcher :846-848 `bcmp(reply.chaddr, ifctx->call.chaddr,
   ifctx->call.hlen)` with hlen=17/255 reads past the 16-byte chaddr
   member on both sides (interposed bcmp logged len and over-read count);
   the verbatim exchange completes (`error=0`), i.e. an attacker reply
   crafted with the matching tail is accepted.
4. `bootpc_call` :718-720 `hexncpy((u_char *)LLADDR(ifctx->sdl),
   sdl_alen, hexstr, HEX_NCPYLEN(sdl_alen), ":")` with
   `char hexstr[64]` (:590) and `HEX_NCPYLEN(s) = (s * 3)`
   (sys/sys/libkern.h:67). libkern hexncpy (libkern/hexncpy.c:56-62)
   writes exactly 3 bytes per input byte while `outlen >= 3`, then a NUL:
   outlen = alen*3 is trusted, so the footprint is 3*alen bytes from a
   64-byte buffer. Contained canary runs: alen=22 → 2 bytes past; alen=106
   → 254 bytes past (offsets 0..253 of the post-canary, next byte intact).
5. **In-situ (test 50, alen=255)**: 765-byte write from `hexstr[64]` inside
   `bootpc_call`'s own stack frame → saved RBP/return address overwritten
   with hex-digit/':' bytes → `Fatal trap 9: general protection fault`,
   `Stopped at bootpc_call+0x83d: ret`, frame pointer
   `0x3a37363a36363a35` (0x3a = ':'). Kernel built with
   `-fno-stack-protector` (stock flags, see build.log), so nothing caught
   it before control flow used the smashed words.

## Privesc assessment (standing directive)

The primitive is a linear, fully attacker-length-controlled stack overwrite
(up to 701 bytes past a 64-byte buffer) whose content is a printable
hex-with-colons encoding of the interface MAC — i.e. controllable in length,
constrained in alphabet (0-9a-f and ':'), landing on saved registers/return
address in a boot-time kernel thread. On an unhardened target this is
control-flow corruption; but the WRITE occurs only when a driver presents
IFT_ETHER with alen>16, which no in-tree driver does and which an
unprivileged user cannot cause (no ioctl path sets sdl_alen for an existing
ether interface: net/if.c:2885 refuses length changes). Therefore: real
kernel-stack corruption class, hardware/driver-gated trigger, no
unprivileged-local route to uid=0 on this guest — consistent with the
finding's Low/speculative rating, now with the primitive proven rather than
inferred.

## Fix validation

First attempt (append predicates to the existing condition) was **caught
broken by the A/B run**: `(sdl = ...)` in :1050 assigns on every AF_LINK
ifaddr, so a rejected address still leaves sdl non-NULL and the clamp never
engaged (test 3 still accepted alen=17). Corrected `fix.diff` assigns a
local `sdl2` only when `sdl_type == IFT_ETHER && sdl_alen > 0 && sdl_alen
<= sizeof(ifctx->call.chaddr)`, and bounds the hexncpy outlen with
`sizeof(hexstr)`. Fixed-kernel results: selection rejects alen=17/255 →
verbatim `panic("bootpc: Unable to find HW address for %s")` (:1056-1057)
— a loud boot failure instead of silent corruption; call-form canary run
with the bounded outlen writes 0 bytes past the buffer. No cited use can
observe alen > 16 anymore.

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