DF-3045 / fix_validation.log
DF-3045 fix validation (A/B, same harness, same tests, only bootp_subr.c changed)
================================================================================
Kernel A (baseline): bootp_subr.c sha256 ada7d13f... (verbatim gate OK)
Kernel B (fixed): baseline + fix.diff (2 hunks: selection requires
0 < sdl_alen <= sizeof(call.chaddr) at :1048-1062 --
restructured so `sdl` is only set when ALL predicates
pass; hexncpy outlen bound sizeof(hexstr) at :721),
sha256 52154f24f8d0f6fd8964d8cab366e81a377c5c10c622a7b3531c293d33be0148
NOTE ON THE FIRST FIX ATTEMPT (honesty): the initial version of the
selection hunk only appended predicates to the existing condition:
sdl_type == IFT_ETHER && sdl_alen > 0 && sdl_alen <= 16
That is INSUFFICIENT: `(sdl = ((struct sockaddr_dl *) ifa->ifa_addr)) != NULL`
assigns sdl for EVERY AF_LINK ifaddr, so when the appended predicates reject
the address, sdl remains NON-NULL pointing at the rejected ifaddr; the
post-loop NULL check passes and the unbounded alen is still used. The A/B
run caught this on the first fixed kernel (test 3 still reported
"selection ACCEPTED sdl_alen=17" and the bcopy overrun). The corrected
hunk (this fix.diff) only assigns `sdl` from a local `sdl2` when every
predicate holds. This is exactly what fix validation is for.
Test 3 -- sdl_alen=17 fake IFT_ETHER interface through verbatim
bootpc_fakeup_interface -> bootpc_compose_query -> bootpc_call
baseline: selection ACCEPTED sdl_alen=17 (>16, never checked)
call.hlen=17 (call.hlen=sdl_alen at :1192)
bcopy at :1197 overran chaddr[16] by 1 byte:
call.sname[0]=0x50 == MAC[16]=0x50
matcher bcmp len=17 -> 1 byte past chaddr[15] on BOTH sides
full OFFER/ACK exchange completes (bootpc_call error=0)
fixed: selection REJECTED; verbatim fail-loud path fires:
panic: bootpc: Unable to find HW address for fak0
bootpc_fakeup_interface <- df_test3 <- sysctl_df_test <-
sysctl_root <- userland_sysctl (run.fixed.log)
No hlen>16, no bcopy overrun, no bcmp overread, no hexncpy
overlong write -- the primitive is dead upstream. A boot-time
panic on a machine whose ONLY interface has a bogus driver is
the intended loud failure instead of silent memory corruption.
Test 50 -- in-situ sdl_alen=255 (full magnitude; baseline only)
baseline: hexncpy at :718-720 writes 765 bytes from hexstr[64] inside
bootpc_call's own frame -> Fatal trap 9 (GPF) at
bootpc_call+0x83d: ret with frame pointer 0x3a37363a36363a35
(0x3a = ':' -- the hexncpy separator byte) (panic.txt)
fixed: not reachable: selection rejects sdl_alen=255 before any of the
cited uses (same mechanism as test 3 fixed).
Test 5 -- hexncpy call-form canaries (contained; both kernels)
T5a alen=22 as-shipped form (HEX_NCPYLEN=66): 2 bytes past the 64-byte
buffer (canary offsets 0..1), pre-canary intact
T5b alen=106 as-shipped form (318): 254 bytes past
(canary offsets 0..253, next byte intact; scaling:
alen=255 -> 701 bytes past the 64-byte buffer)
T5c fixed form (outlen=sizeof(hexstr)=64): 0 bytes past, pre intact
(T5a/T5b exercise libkern hexncpy semantics, unchanged by the fix; the fix
bounds the production CALL SITE, proven by the test-3 A/B above.)
Verdict: with the corrected selection hunk, none of the cited uses can see
sdl_alen > 16, and the hexncpy call is independently bounded; the fix
converts silent boot-time stack corruption into a loud boot failure.