DF-5198 / verdict.json
{ "finding_id": "DF-5198", "status": "not_reproduced", "reproduced": false, "impact": "none", "confidence": "certain", "verdict": "fire_saver() ignores the return of set_video_mode(adp, M_VGA_CG320) (fire_saver.c:69) and then unconditionally caches vid=adp->va_window (fire_saver.c:85) and memcpys 64000 bytes to it on every 25 Hz tick (fire_saver.c:104) \u2014 the exact DF-5114 (dragon)/DF-5197 (warp) twin, completing the syscons-saver family. On a mode-set failure vid would alias the 32KB text aperture (0xB8000) and the blit would overrun it by ~32KB into 0xC0000+ (video-BIOS shadow / adapter MMIO, not kernel RAM), repeatedly at 25 Hz. The branch is unreachable on a stock kernel: fire_initialise (fire_saver.c:118) gates registration on get_mode_info(M_VGA_CG320) on the same adapter (splash_adp = sc->adp), and vga_set_mode can only fail via prologue(!vga_init_done | !V_ADP_MODECHANGE \u2014 set once at vga.c:684, never cleared) or vga_get_info(get_mode_param==NULL) (vga.c:1003-1006, 888-893) \u2014 the same checks the load-time gate already passed. Live verification on the QEMU guest exercised the full benign path: kldload (gate passed), two blank cycles (~13s of fire ticks), clean unblank/text-mode restore (syscons restore_scrn_saver_mode), clean module unload; no panic. Latent hardening defect, consistent with the untested DB treatment of both twins; no uid=0 route exists for MMIO-window-relative writes, and arming the saver at all requires root kldload.", "exploit_chain": "", "evidence": "run.log (KLDLOAD_OK/ARMED/REARMED_OK/STILL_ALIVE/SECOND_CYCLE_DONE/UNLOADED/CLEAN, serial log with no panic); VERDICT.md (full path trace: syscons.c:2182-2232/2546-2580/2632-2668/2706-2720, splash.c:101-156, vga.c:414/684/888/1003/1005); fix.diff (applies cleanly, patch --dry-run APPLY_OK)", "kernel_refs": [ "sys/dev/misc/syscons/fire/fire_saver.c:69", "sys/dev/misc/syscons/fire/fire_saver.c:85", "sys/dev/misc/syscons/fire/fire_saver.c:104", "sys/dev/misc/syscons/fire/fire_saver.c:118", "sys/dev/misc/syscons/syscons.c:2229", "sys/dev/misc/syscons/syscons.c:2559", "sys/dev/video/fb/vga.c:1003", "sys/dev/video/fb/vga.c:1005" ], "poc_changes": "No PoC source needed fixing: the finding is verified by exercising the stock module live (kldload + vidcontrol blank-timer cycles + unload) rather than a compiled trigger. One command adjustment: vidcontrol -t 0 is rejected by vidcontrols parser (positive numbers only) \u2014 unblanking uses vidcontrol -t 600 (CONS_BLANKTIME touches the saver; the next scrn_timer tick stops it and restores text mode).", "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": 75, "guest_dirty": 0, "build_cmd": "sh build.sh # ls /boot/kernel/fire_saver.ko && kldload fire_saver (prebuilt module; load runs fire_initialise/get_mode_info gate)", "run_cmd": "sh run.sh # trigger.sh: kldload; vidcontrol -t 1/-t 600 blank cycles (~13s fire); kldunload", "code_hash": "5cc68122c9b575489a302f74a95631224b04bc4a0fb81bb94a73b34ecd1738f1", "notes": "Pass-2 file completion: the last syscons saver. Known DF-2193 (buf[64320] one-past-end read in the fade loop) NOT re-reported; all other pass-2 candidates killed with citations (writes bounded to 63999; left-edge wrap read in-bounds; seed row in-bounds, krandom()%160+96 in [96,255]; palette indices <=767 with cosmetic 255th-entry wrap; memcpy exactly 64000 = mode-13h surface; unblank/mode-restore owned by syscons restore_scrn_saver_mode; missing crit_enter around first-blank harmless \u2014 single scrn_timer caller under syscons_lock; unload path exercised live). Family context: dragon DF-5114, warp DF-5196/5197, snake DF-5194, syscons DF-5195. Guest NIC is vtnet (no miibus) \u2014 unrelated to this finding.", "recommended_fix": "Check set_video_mode()s return in fire_saver() and bail out with ENODEV on failure so syscons marks the saver failed and restores the saved mode (fix.diff).", "fix_status": "not_testable", "fix_baseline_reproduced": false, "fix_patched_reproduced": false, "fix_verdict": "fix.diff authored against the read-only sys/ tree and apply-verified (patch --dry-run APPLY_OK) but not kernel-build-validated: the finding is latent (not_reproduced) so there is no baseline bad behavior for a patched-kernel A/B comparison. Fix validation is mandatory only for findings that reproduced.", "fix_evidence": "findings/poc/DF-5198/fix.diff (unified diff, applies to sys/dev/misc/syscons/fire/fire_saver.c:69); apply check shown in session log" } |