{
  "finding_id": "DF-2252",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "hexncpy() (sys/libkern/hexncpy.c:41-64) never validates inlen > 0 on entry; the :52 KKASSERT checks only outb/inb/outlen. With inlen <= 0 and outlen >= 3 the loop body at :56 never runs, outb stays at addr, and the unconditional terminator *--outb = 0 at :62 writes NUL one byte BEFORE the caller buffer. Proven on the kernel-compiled REAL function (runtime kldload GPFs this guest): destination &region[16] of a 0xAA-prefilled 32-byte kmalloc region, outlen=12; inlen=0 and inlen=-1 both flip the canary byte region[15] from 0xaa to 0x00 while region[16] is untouched (run.log, underflow_canary_hits=2); the inlen=4 control leaves it intact. The cited caller m_print() (sys/kern/uipc_mbuf.c:2527-2544) calls hexncpy(m2->m_data, m2->m_len, hexstr, HEX_NCPYLEN(m2->m_len), -) at :2537 with no zero guard; HEX_NCPYLEN(s)=(s*3) (sys/sys/libkern.h:67), so a legal m_len==0 mbuf passes outlen=0 - on this INVARIANTS guest the harness (real m_gethdr chain, real m_print) panics with assertion outlen >= MIN_OUTBLEN failed in hexncpy at hexncpy.c:52, backtrace hexncpy <- m_print, guest wedged in ddb (panic.txt); on production kernels KKASSERT compiles out (sys/sys/systm.h:116-119) and the same call executes the *--outb = 0 underflow into the byte before the kmalloc(3*len, M_TEMP) buffer - exactly the C2/C3 write. REACHABILITY (team directive): full caller sweep shows m_print has exactly one in-tree caller, the musycc T1/E1 PCI card RX-debug path (sys/dev/misc/musycc/musycc.c:754, clamps m_len to 16 but does not guard 0); ddb has no m_print command, netstat -m uses sysctl counters only, no syscall/ioctl path exists, and every other variable-inlen caller pairs outlen=HEX_NCPYLEN(inlen) so zero length means outlen=0 (same panic/underflow split, device- or boot-controlled lengths only; sppp guards with if(len), if_spppsubr.c:5289). privesc_candidate=false: the 1-byte NUL underflow primitive is real and deterministic but NO unprivileged-reachable caller passes inlen<=0 in the current tree - it is a landmine for any future zero-length-tolerant caller. fix.diff (drain sep?3:2, terminate at addr[0] when nothing converted, plus m_print skip-empty-mbuf hunk) validated A/B on a rebuilt kernel: C2/C3 canary stays 0xaa with buf0=0x00, C1 sep!=NULL output byte-identical (de-ad-be-ef), m_print mode-2 returns without panic, guest up.",
  "exploit_chain": "",
  "evidence": "run.log (baseline matrix: C2/C3 canary 0xaa->0x00 = the underflow, C1 control clean, underflow_canary_hits=2); panic.txt (baseline m_print zero-m_len: panic assertion outlen >= MIN_OUTBLEN failed in hexncpy at hexncpy.c:52, backtrace hexncpy <- m_print <- sysctl_df_hexncpy, db> prompt); fix_run.log (patched kernel #2 16:28:39: canary intact, buf0=0x00, de-ad-be-ef identical, m_print returns, guest up); fix_build.log (nativekernel BUILD2_RC=0 with hexncpy.c+uipc_mbuf.c recompiled -Werror); harness_hexncpy.c sha256 27dccb374f1ca3665e01527d75dd6de76e28209674e811845f9ffdea24778a75",
  "kernel_refs": [
    "sys/libkern/hexncpy.c:52",
    "sys/libkern/hexncpy.c:56",
    "sys/libkern/hexncpy.c:62",
    "sys/kern/uipc_mbuf.c:2533",
    "sys/kern/uipc_mbuf.c:2537",
    "sys/sys/libkern.h:67",
    "sys/sys/systm.h:116",
    "sys/dev/misc/musycc/musycc.c:754",
    "sys/net/sppp/if_spppsubr.c:5289"
  ],
  "poc_changes": "Authored fresh (no seeded pack on disk). Per guest quirk (runtime kldload GPFs) the rig is compiled into the kernel: harness_hexncpy.c appended to the guest copy of sys/libkern/hexncpy.c so it links the production function in the same TU, driven by sysctl kern.df_hexncpy; pseudo-device tap added to X86_64_GENERIC for the DF-2253 end-to-end. Guest source verified byte-identical to the audit tree (sha256) before patching.",
  "attempts": 3,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly v6.5.0.1712.g89e6a-DEVELOPMENT #1: Sat Oct 17 16:14:33 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (baseline, INVARIANTS); #2: Sat Oct 17 16:28:39 UTC 2026 (single-fix kernel)",
  "runtime_sec": 1900,
  "guest_dirty": 0,
  "build_cmd": "sh build.sh (guest: cat harness >> /usr/src/sys/libkern/hexncpy.c; pseudo-device tap; cd /usr/src && make -j6 nativekernel KERNCONF=X86_64_GENERIC && make installkernel; reboot)",
  "run_cmd": "sh run.sh (guest: sysctl kern.df_hexncpy=1; sysctl kern.df_hexncpy=2 -> panic)",
  "code_hash": "27dccb374f1ca3665e01527d75dd6de76e28209674e811845f9ffdea24778a75",
  "notes": "Impact recorded as panic: that is the strongest behavior actually reachable and demonstrated through a real caller on this INVARIANTS guest (KKASSERT at hexncpy.c:52 fires before the underflow because every zero-length caller also passes outlen=0). On production kernels the same inputs perform the silent 1-byte NUL heap underflow proven at function level by the canary matrix. Unprivileged reachability closed negative: musycc-only caller, no ddb/netstat/syscall path; see VERDICT.md Reachability. Guest reset with-src after validation.",
  "recommended_fix": "Guard the terminator against the nothing-converted case and fix sep==NULL accounting: drain (sep ? 3 : 2) per iteration and write *outb = 0 when sep == NULL or no char was written (see fix.diff, plus m_print skip-empty-mbuf companion hunk).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "fix.diff applied to guest /usr/src (patch -p1, hunks at hexncpy.c:53 and uipc_mbuf.c:2534), incremental make nativekernel (BUILD2_RC=0, -Werror), rebooted into kernel #2 Sat Oct 17 16:28:39 UTC 2026. Identical harness and inputs: baseline had canary 0xaa->0x00 on C2/C3 (underflow) and an m_print KKASSERT panic wedging the guest in ddb; patched kernel keeps the canary at 0xaa with buf0=0x00 (terminator in-bounds), C1 sep!=NULL output de-ad-be-ef byte-identical to stock, m_print with the zero-m_len chain prints the 8-byte mbuf and returns, guest stays up. fix_patched_reproduced=false means the BAD behavior is gone, which is the success criterion.",
  "fix_evidence": "fix_run.log (patched matrix + mode-2 return); fix_build.log (BUILD2_RC=0, uipc_mbuf.c/hexncpy.c recompile lines); fix.diff sha256 745fdae526ade23b4c34f118af06eeec2be0f312dd087f8859fce09270bfacd3 (git apply --check clean against sys/); panic.txt + run.log (baseline A side)"
}
