β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-5194

Snake movement engine confines itself purely by wall-bounce arithmetic writing through the completely unchecked sc_vtb_putc: any geometry inconsistency escapes and runs away with unbounded 2-byte OOB writes marching through the video window into adjacent DMAP'd memory (latent β€” no in-tree path produces the trigger)

Summary

The bounce conditions are the ONLY confinement over a raw unchecked *(vtb_buffer+at) store - ysize==1 geometry escapes deterministically (C truncated division prevents recovery) and each tick OOB-writes video-aperture cells at ~50B/s into DMAP'd low RAM. Latent (CONS_SET always full-dims; geometry changes unblank first); the sim proves the exact kernel arithmetic. Head-validate-and-reinit fix + bounds-check sc_vtb_putc defense-in-depth.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5194 Β· 12 files
FileTypeDescriptionSize
sim.c β€” 2.2 KB view raw
sim.log β€” 622 B view raw
snake_probe.c β€” 3.7 KB view raw
guard_probe.c β€” 2.9 KB view raw
build.sh β€” 66 B view raw
run.sh β€” 172 B view raw
build.log β€” 9 B view raw
run.log β€” 1.5 KB view raw
run.live.log β€” 1.1 KB view raw
fix.diff β€” 990 B view raw
verdict.json β€” 3.8 KB view raw
env.txt β€” 594 B view raw

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

Latent finding: there is no triggerable baseline to validate a fix against (the escape is unreachable through in-tree ioctls). fix.diff authored as the hardening clamp; not built.

fix.diff; sim.log demonstrates the clamped behavior class (in-bounds runs)
↓ fix.diffper-fix-DF-5194

Confirmed kernel references

Detail

Evidence (decisive lines)

sim.log (escape vs in-bounds controls); run.live.log (live snake on the vidconsole guest: 24+1 cells, moving, in bounds); VERDICT.md (full code-path kill list); fix.diff (clamp).

PoC changes

seeded draft assumptions corrected: the screen-saver subsystem requires (a) a vidconsole kernel console (with the stock console=comconsole, splash_init() never runs and kldload only queues the decoder -- saver inert) and (b) CONS_SAVERSTART/CONS_BLANKTIME to force blanking; observation channel switched from CONS_SCRSHOT (reads scp->vtb, which the snake never touches) to raw VGA memory via /dev/mem at phys 0xB8000.

Verified recommended fix

After savs[0] += dirx + diry, validate 0 <= savs[0] < scp->xsize*scp->ysize and reinitialize (blanked = 0) when out of range; defense-in-depth: bounds-check sc_vtb_putc().

Verdict

The OOB itself is latent and was NOT reproduced on the guest -- by design of the finding. What was verified: (1) the escape arithmetic is real -- sim.c, using the exact kernel arithmetic from snake_saver.c:83-111, shows a deterministic escape and unbounded forward drift (+xsize per tick, max observed 14199 cells past an 80-cell vtb) for ysize==1 geometry, while all real geometries (80x25, 132x60, 100k ticks with the random dirx/diry flips) stay strictly in bounds; (2) the live mechanism works and stays in bounds: on a vidconsole boot with snake_saver.ko loaded, the snake drew exactly 24 non-space cells + 1 space (messagelen 25), moved each drawing tick, and all observed cell indices were < xsize*ysize (run.live.log); (3) no in-tree path can produce the triggering geometry: the only geometry-passing caller of sc_set_text_mode is CONS_SET with xsize=ysize=0 (scvidctl.c:515, clamped to full mode dims >= 80x25), and every geometry-change/VT-switch path unblanks the saver first via sc_clean_up, resetting the module's 'blanked' and re-initializing savs[] on the next blank cycle. Bonus discovery from the trigger attempts: mode-change-while-blanked does not merely stay in bounds -- it deadlocks the kernel (reproduced separately as DF-5195 in syscons.c).