DF-5702 / verdict.json
{ "finding_id": "DF-5702", "status": "reproduced", "reproduced": true, "impact": "none", "confidence": "certain", "verdict": "Kernel strtoq/strtouq consume the 0x/0X prefix without verifying a hex digit follows (strtoq.c:72-77, strtouq.c:70-75), 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 (strtoq==strtoll via _strtol.h:83-91, strtouq==strtoull via _strtoul.h:86-94, both gated on isxdigit(s[1])) parse 0 and advance endptr past the 0. Proven on the DragonFly guest with a differential harness compiling the untouched guest kernel sources against guest libc: 28 ENDPTR divergences, exactly and only this class (bases 0/16, both families), deterministic across 3 runs. VALUE divergences are 0 across the whole corpus - the kernel is strictly fail-closed (never accepts or advances beyond libc), saturation values are bit-identical to libc, and there is no memory-safety impact (all reads NUL-terminated, only write is *endptr). Net effect: kernel/userland parser desync on the shared ASCII-integer surface (ng_parse ng_int64_parse EINVAL vs libc-validated accept; kgetenv_quad; scanf %i insulated) - Low severity hardening/interop defect, machine-proven.", "exploit_chain": "", "evidence": "findings/poc/DF-5702/run.log (and run2.log/run.1.log, identical md5): rows ENDPTR-DIFF s=\"0x\" b=0 kernel=0 (off 0) libc=0 (off 1) x28; SUMMARY block: VALUE 0 / ENDPTR 28 / invalid-base 258 / ERANGE-ONLY 79 (DF-2230 class, not re-reported) / patched residual 0 / patched regressions 0. build_full.log shows cksum-identical guest /usr/src kernel sources and the sed-generated k_*.c copies. fix.diff git-apply-checked.", "kernel_refs": [ "sys/libkern/strtoq.c:72-77", "sys/libkern/strtoq.c:103-121", "sys/libkern/strtoq.c:126-127", "sys/libkern/strtouq.c:70-75", "sys/libkern/strtouq.c:104-105", "lib/libc/stdlib/_strtol.h:83-91", "lib/libc/stdlib/_strtoul.h:86-94", "sys/netgraph/netgraph/ng_parse.c:620-628", "sys/netgraph7/netgraph/ng_parse.c:640-648", "sys/kern/kern_environment.c:432-470", "sys/kern/subr_scanf.c:186-234", "sys/libprop/prop_number.c:518-540" ], "poc_changes": "No seed PoC existed. Built the differential rig from scratch: kernel_strtoq_shim.h (swaps the four kernel includes for userland equivalents; QUAD_* macros already provided by userland sys/limits.h, NULL via stddef.h), build.sh sed-generates userland-compilable k_strtoq.c/k_strtouq.c plus fix-only k_*_fix.c variants from the untouched guest /usr/src copies (cksum-verified against the repo), diffharness.c drives 43 inputs x 9 bases x {signed,unsigned} comparing kernel vs libc VALUE / endptr offset / ERANGE, with invalid-base rows classified separately so the DF-5702 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": 96, "guest_dirty": 0, "build_cmd": "scp -F dfbsd-qemu/config kernel_strtoq_shim.h diffharness.c build.sh run.sh dfbsd-maxx:/tmp/df5702/ ; vm.sh run_user cd /tmp/df5702 && ./build.sh", "run_cmd": "vm.sh run_user cd /tmp/df5702 && ./run.sh (harness exit 0)", "code_hash": "b1402743551d0360f6212917a52fea5fa9a59030e9061490d97fdac8069bc1d1", "notes": "Do-not-re-report respected: the kernel-has-no-errno / silent-saturation property (79 ERANGE-ONLY rows) is DF-2230s claimed root cause and was NOT re-filed; strtouq negative-sign acceptance is C99 7.20.1.4p5-conformant (not a bug); no in-tree caller passes an invalid base (all pass 0/8/10/16), so the 258 invalid-base diffs are a documented negative note. Memory-safety surface of both files verified clean: NUL-terminated reads (the s[1] peek at strtoq.c:73-74 is gated on *s==x so it reads at most the terminator), sole write is *endptr, all arithmetic in u_quad_t with the cutoff/cutlim idiom verified at the exact -2^63 / 2^64-1 boundaries.", "recommended_fix": "Add the same isxdigit((unsigned char)s[1]) guard the userland template has (_strtol.h:86 / _strtoul.h:89) to the 0x-prefix test in sys/libkern/strtoq.c:72-77 and sys/libkern/strtouq.c:70-75.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff (git-apply-checked in a scratch 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-5702/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_full.log contains the unified diff of k_strtoq.c vs k_strtoq_fix.c / k_strtouq.c vs k_strtouq_fix.c showing the guard as the only change." } |