DF-2884 / verdict.json
{ "finding_id": "DF-2884", "status": "reproduced", "reproduced": 1, "impact": "none", "confidence": "certain", "verdict": "kvsscanf's conversion switch (sys/kern/subr_scanf.c:148-253) has no default case, so an unsupported specifier (%f/%e/%g/%a/%z/typo) silently falls through: leading whitespace is consumed as a side effect, NO varargument is taken, and every subsequent conversion binds to a shifted argument -- %d then stores through the pointer meant for %f, and a following %s would write a string through a scalar pointer (caller-object overflow hazard). Proven in-kernel on the stock kernel via kvsscanf (E5: ksscanf-style '5' with '%f%d' returns ret=1, a=5, b=-1 -- %d wrote through &a, &b untouched); cc's own __scanflike(2,3) annotation on ksscanf rejects such formats at compile time (the PoC had to bypass it), confirming the specifier is unsanctioned, but runtime-built formats and unannotated wrappers reach the defect. No in-tree caller uses an unsupported specifier (21 sites surveyed) -- Info/hardening. fix.diff hunk 1 (default: goto match_failure, mirroring userland vfscanf) validated on kernel #1: ret=0, nothing written.", "exploit_chain": "none in-tree; for a hypothetical caller with a runtime-built fmt containing an unsupported specifier, later %s writes a NUL-terminated string through a non-buffer argument pointer", "evidence": [ "run.log: \"dfpoc: E5 '5' %f%d: ret=1 a=5 b=-1\" + RESULT varargument list desynchronized REPRODUCED", "run.2.log: identical on second load", "run_patched.log: kernel #1 with fix.diff hunk 1 -- \"E5 '5' %f%d: ret=0 a=-1 b=-1\"", "build attempt record: cc rejects ksscanf(..., \"%f%d\", int*, int*) via __scanflike -- %f is not a supported kernel conversion" ], "kernel_refs": [ "sys/kern/subr_scanf.c:148", "sys/kern/subr_scanf.c:253", "sys/kern/subr_scanf.c:258", "sys/kern/subr_scanf.c:283", "sys/sys/systm.h:240" ], "poc_changes": "authored fresh (no seed): experiment E5 of the shared dfpoc.ko KLD harness; called through an unannotated kvsscanf wrapper because the __scanflike(2,3) annotation on ksscanf rejects '%f' at compile time", "attempts": 2, "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": 2400, "guest_dirty": 0, "build_cmd": "cd /root/dfscanf && make (fix validation: cd /usr/src && make -j6 nativekernel KERNCONF=X86_64_GENERIC && make installkernel KERNCONF=X86_64_GENERIC)", "run_cmd": "kldload /root/dfscanf/dfpoc.ko && sleep 1 && dmesg | tail -4", "code_hash": "0c3b123fc9ff001261befd22c3cdba2bce64a21495883ef913e4ab55413b5b3e", "notes": "hardening finding; userland vfscanf rejects unknown conversions (default: goto match_failure) -- hunk 1 aligns the kernel engine. In-tree callers all use supported specifiers only.", "recommended_fix": "add 'default: goto match_failure;' to the conversion switch at sys/kern/subr_scanf.c:252 (fix.diff hunk 1)", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Wed Sep 2 21:17:17 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff hunk 1 (default: goto match_failure) validated on kernel #1: the unknown-specifier format is rejected (ret=0) and no argument is written; all other experiments behave per their own fixes/controls.", "fix_evidence": [ "run_patched.log: \"dfpoc: E5 '5' %f%d: ret=0 a=-1 b=-1\"", "run.log (baseline kernel #0): ret=1 a=5 b=-1", "fix.diff hunk 1" ] } |