⬢ DragonFlyBSD Kernel Audit
DF-3045 / fix_validation.log
← back to finding ↓ download raw
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.