DF-5777 / verdict.json
{ "finding_id": "DF-5777", "status": "reproduced", "reproduced": true, "impact": "panic", "confidence": "certain", "verdict": "REPRODUCED then FIX VALIDATED. splash() (sys/dev/video/fb/splash.c:204-205) invokes (*splash_decoder->splash)(adp, on) without the NULL guard its sibling callbacks get (init at :83, term at :193). With the guest rig switched to vidconsole so splash_init() runs (syscons.c:3432), a kld decoder registered with .splash == NULL becomes the active splash_decoder; calling the exported splash() -- the same call scsplash_saver() makes at syscons.c:2560 -- transfers control to address 0: Fatal trap 12, fault va 0x0, Stopped at 0:, kernel dead in DDB, ssh gone. One-line guard rebuilt into a nativekernel: identical PoC returns cleanly (nullsplash: returned -- guard present?, sysctl exit 0, guest up). Severity Info/hardening: registry writes require root module load; all 12 in-tree registrants fill .splash, so only a misdeclared third-party root-loaded module hits it.", "exploit_chain": "", "evidence": "panic.txt + vga_panic_2x.png (DDB screendump: Stopped at 0:, fault virtual address = 0x0, current process 988 = sysctl caller); run.log (baseline transcript: init kprintf proving immediate-path registration, then ssh death); fix_run.log (patched kernel A/B); fix.diff (applied to guest /usr/src only; host sys/ untouched); fix_build.log/fixsrc.txt (nativekernel BUILD_EXIT=0, uname #1 Sun Oct 11 20:47:22 UTC 2026, kernel md5s).", "kernel_refs": [ "sys/dev/video/fb/splash.c:204", "sys/dev/video/fb/splash.c:205", "sys/dev/video/fb/splash.c:83", "sys/dev/video/fb/splash.c:193", "sys/dev/misc/syscons/syscons.c:2560", "sys/dev/misc/syscons/syscons.c:3432" ], "poc_changes": "Seed sketch did not exist (new pass-2 finding). PoC authored from scratch: kld module with .splash=NULL/.data_type=NULL decoder + SYSCTL_PROC trigger that calls splash(real_adp, TRUE) with the adapter captured during decoder init. Rig change required: /boot/loader.conf.local console=vidconsole + reboot (serial-only console leaves splash_adp NULL via CN_REMOTE > CN_INTERNAL consdev selection). Direct saver-timeout triggering was also attempted (vidcontrol -t / CONS_SAVERSTART ioctl) but syscons idle-blanking state is orthogonal; the module-driven call exercises the identical public splash() entry point.", "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 (baseline); #1: Sun Oct 11 20:47:22 UTC 2026 (patched fix-validation kernel)", "runtime_sec": 2100, "guest_dirty": 1, "build_cmd": "cd /tmp/nullsplash && make (kld; SYSDIR=/usr/src/sys; clean under -Werror)", "run_cmd": "kldload /tmp/nullsplash/nullsplash.ko && sysctl hw.nullsplash_fire=1 [rig: console=vidconsole in /boot/loader.conf.local + reboot]", "code_hash": "a5ef93ae309c6b816268f80795fbb529a8bf3fcb52de3f39b64019d1e5f7fe31", "notes": "Pass-2 negatives recorded in VERDICT.md: registry arithmetic fully re-derived (no OOB beyond DF-2144s known miscount), kenv/namei splash-image path does not exist in this file (image arrives only via preload_search_by_type, splash.c:61-71), offx/offy math is decoder-side (covered by DF-1857..2111), registry races are DF-2143s root-only class, splash_init double-call unreachable behind SC_SPLASH_SCRN/SC_INIT_DONE guards, kmalloc/bcopy error path correct. Guest reset to clean-source snapshot after verification.", "recommended_fix": "Guard the indirect call like its siblings: if (splash_decoder != NULL && splash_decoder->splash != NULL) return (*splash_decoder->splash)(adp, on);", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Applied the one-line guard to the guests /usr/src copy (host sys/ untouched), rebuilt with make nativekernel KERNCONF=X86_64_GENERIC (BUILD_EXIT=0), installed and rebooted (uname #1 Sun Oct 11 20:47:22 UTC 2026). Identical PoC on the patched kernel: no panic, module prints nullsplash: returned -- guard present?, sysctl exit 0, guest stays up. Baseline on the stock kernel (#0 Jul 2 2026) panicked with Stopped at 0:.", "fix_evidence": "fix.diff; fix_run.log (patched A/B); fix_build.log + fixsrc.txt (applied diff in guest tree, kernel md5 1a5ed551b16ceea081ccc60a2120a8f5 patched vs 4625046e8061b34e1758d291bc6cad18 stock); panic.txt + vga_panic_2x.png (baseline)." } |