{
  "finding_id": "DF-5703",
  "status": "reproduced",
  "reproduced": true,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Kernel strtol/strtoul consume the 0x/0X prefix without verifying a hex digit follows (strtol.c:70-75, strtoul.c:68-73), so inputs whose C99 subject sequence is exactly \"0\" (e.g. \"0x\", \"0xg\", \"0xz\") are reported as NO conversion (*endptr=nptr, return 0) where C99 7.20.1.4p7 and DragonFlys own userland libc (strtol via _strtol.h:86, strtoul via _strtoul.h:86, both gated on isxdigit_l(s[1])) parse 0 and advance endptr past the 0. A type-normalized diff proves strtol.c is the same Chris Torek body as strtoq.c modulo type width: the int-width twins SHARE the DF-5702 defect, which DF-5702s fix left unfixed in these two files. Proven on the DragonFly guest with a differential harness compiling the untouched guest kernel sources (cksum-identical to the repo) against guest libc: 28 ENDPTR divergences, exactly and only this class (bases 0/16, both families), md5-identical across 3 runs. VALUE divergences are 0 across the whole corpus including every 32-bit and 64-bit long boundary row - the int-width cutoff idiom is value-exact, the kernel is strictly fail-closed (never accepts or advances beyond libc), and there is no memory-safety impact (all reads NUL-terminated; the s+=2 past-NUL pointer formed on input exactly \"0x\" is never dereferenced because the pre-fetched c==\\0 breaks the loop first; sole write is *endptr). Net effect: kernel/userland parser desync on the shared ASCII-integer surface (ng_parse ng4/ng7 base-0 int32/hex parse EINVAL vs libc-validated accept; boot kenv consumers; hammer PFS name parsing is base 10 and unaffected) - Low severity hardening/interop defect, machine-proven, twin of DF-5702.",
  "exploit_chain": "",
  "evidence": "findings/poc/DF-5703/run.log (and run.2.log/run.3.log, all md5 f7431ebd02350460d632303c275f7f4d): rows ENDPTR-DIFF s=\"0x\" b=0 kernel=0 (off 0) libc=0 (off 1) x28; SUMMARY block: VALUE 0 / ENDPTR 28 / invalid-base 306 / ERANGE-ONLY 65 (DF-2230 class, not re-reported) / patched residual 0 / patched regressions 0. build.log shows cksum-identical guest /usr/src kernel sources (370024243 4183 strtol.c, 3544205135 3234 strtoul.c) and the sed-generated k_*.c copies with the fix variants differing by only the isxdigit guard. fix.diff git-apply-checked against the repo tree.",
  "kernel_refs": [
    "sys/libkern/strtol.c:70-75",
    "sys/libkern/strtol.c:96-98",
    "sys/libkern/strtol.c:99-117",
    "sys/libkern/strtol.c:122-123",
    "sys/libkern/strtoul.c:68-73",
    "lib/libc/stdlib/_strtol.h:86",
    "lib/libc/stdlib/_strtoul.h:86",
    "sys/conf/files:2233-2236",
    "sys/netgraph/netgraph/ng_parse.c:339",
    "sys/netgraph7/netgraph/ng_parse.c:341",
    "sys/kern/subr_bus.c:2311",
    "sys/vfs/hammer/hammer_vnops.c:1822",
    "sys/vfs/hammer/hammer_subs.c:1073"
  ],
  "poc_changes": "No seed PoC existed. Rig adapted from DF-5702s (directly reusable as flagged by the orchestrator): kernel_strtol_shim.h swaps the four kernel includes for userland equivalents (LONG_*/ULONG_* already provided by userland limits.h); build.sh sed-generates userland-compilable k_strtol.c/k_strtoul.c plus fix-only k_*_fix.c variants from the untouched guest /usr/src copies (cksum-verified against the repo); diffharness.c drives DF-5702s 43-input corpus PLUS the int-width boundary rows (32-bit long 2147483647/2147483648/-2147483648/-2147483649/4294967295/4294967296 + hex forms, and the live 64-bit long boundaries) x 9 bases x {signed,unsigned} comparing kernel vs libc VALUE / endptr offset / ERANGE, with invalid-base rows classified separately so the DF-5703 signal is unambiguous.",
  "attempts": 3,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026     root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "runtime_sec": 105,
  "guest_dirty": 0,
  "build_cmd": "scp -F dfbsd-qemu/config kernel_strtol_shim.h diffharness.c build.sh run.sh dfbsd-maxx:/tmp/df5703/ ; vm.sh run_user cd /tmp/df5703 && sh ./build.sh",
  "run_cmd": "vm.sh run_user cd /tmp/df5703 && sh ./run.sh   (harness exit 0)",
  "code_hash": "cb8af41ade9cc929eceae4b1cbd32ab71cfd6f71d633768badae2a6c3223de04",
  "notes": "Do-not-re-report respected: ERANGE-only saturation rows are DF-2230s claimed root cause (65 rows, counted, not filed); the quad twins strtoq.c/strtouq.c are DF-5702 (filed + fix) and were only diffed to prove the shared-body claim, not re-derived as findings. Consumer census (int variants): ng_parse ng4/ng7 base-0 endptr-validated parse is the desync surface (root-only configuration); subr_bus/blist/alist/usb_quirk/drm/acpi/hdaa are boot/root-trusted kenv+module strings; the only unprivileged-reachable sites (hammer PFS name forms, hammer_vnops.c:1822 hammer_subs.c:1073) are base 10 with NULL/length-checked endptr and unaffected by the 0x class; no kernel-side prop_dictionary_recv consumer exists for libprops base-0 strtoul (prop_data.c:572). Memory-safety surface verified clean: NUL-terminated reads; s+=2 past-NUL pointer never dereferenced (break on pre-fetched c at strtol.c:100-107 first); sole write *endptr; cutlim fits int for base<=36; invalid bases (1/37/-1) unreachable from any in-tree caller (306 rows documented as negative note). Stale 32-bit-era comment block strtol.c:84-90 on a 64-bit-long build is cosmetic.",
  "recommended_fix": "Add the same isxdigit((unsigned char)s[1]) guard the userland template has (_strtol.h:86 / _strtoul.h:86) to the 0x-prefix test in sys/libkern/strtol.c:70-75 and sys/libkern/strtoul.c:68-73 (see fix.diff).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff (git-apply-checked against the repo) adds only the isxdigit((unsigned char)s[1]) guard to both files; recompiled through the identical rig, all 28 ENDPTR divergences vanish and no value changes on any valid base (SUMMARY: patched residual 0, regressions 0). Pure-function semantic fix - the compiled fix variant is byte-for-byte the kernel body except the guard - so no kernel rebuild was required for validation (non-memory-corruption class).",
  "fix_evidence": "findings/poc/DF-5703/run.log SUMMARY lines: patched-kernel residual ENDPTR diffs : 0 (fix validation; must be 0) and patched-kernel VALUE regressions : 0 (must be 0); build.log contains the unified diff of k_strtol.c vs k_strtol_fix.c / k_strtoul.c vs k_strtoul_fix.c showing the guard as the only change."
}