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

sckmsrndr: fill_rect24 slow path over-advances draw_pos by 3*width per row writing past framebuffer mapping

Summary

fill_rect24 at 420 d=line_width-width*3; 421 KKASSERT(d>=0). Fast path 423-436 inner +12 per 4-pixel group (3*width total) outer +d at 435 -> correct advance line_width. Slow path 437-447 inner +3 per pixel (3*width total) outer +line_width at 445 NOT +d -> advances 3*width+line_width per row instead of line_width. Slow path when (draw_pos&3)||(line_width&3)||(width&3). Right-border call 487-489 width=rightpixel=fbi->width-scp->xsize*blk_width. 1366x768 24bpp: rightpixel=6 6&3=2 slow path; height=blk_height*ysize=768; drift (height-1)*3*width=767*18=13806 bytes; last write vaddr+~3MB 9.8KB past framebuffer mapping 768*4096=3145728 bytes. writeb=plain volatile store no bounds check. Any console write (clear VT switch escape). Border color partially controls written value. DoS panic or adjacent kmem corruption. Fix: draw_pos+=d not line_width at 445.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1818 Β· 9 files
FileTypeDescriptionSize
harness.c trigger-source userspace logic harness reproducing the buggy arithmetic/control-flow 3.8 KB view raw
VERDICT.md verdict full verification narrative 2.5 KB ↓ raw
build.sh build-script exact build command 88 B view raw
run.sh run-script exact run invocation 41 B view raw
harness_run.log run-log harness output on guest 406 B view raw
fix.diff suggested-fix git-apply-able unified diff 300 B view raw
env.txt environment guest uname, cc version, kernel config 768 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
VERDICT.md verdict full verification narrative
↓ download raw

DF-1818 β€” Verification Verdict

Verdict: REPRODUCED (source-confirmed + arithmetic-harness)

The slow-path advance bug is confirmed at sys/dev/misc/syscons/sckmsrndr.c:445. The harness reproduces the exact arithmetic and shows a 13824-byte framebuffer overrun for the 1366x768@24bpp right-border case.

Mechanism

fill_rect24 (sckmsrndr.c:414-448) computes d = line_width - width*3 (:420), the per-row right-padding. The fast path (:423-436) correctly does draw_pos += d at :435 after each row. The slow path (:437-446) β€” taken when (draw_pos|line_width|width) & 3 != 0 β€” does draw_pos += line_width at :445 instead of draw_pos += d. Since the inner loop already advanced by 3*width, the slow path advances by 3*width + line_width per row instead of line_width, drifting by 3*width per row. For the right-border call at :487-489 with width = rightpixel = fbi->width - scp->xsize*blk_width (e.g. 6 at 1366x768), drift accumulates to (height-1)*3*width = 767*18 = 13806 bytes. The final writeb lands ~13824 bytes past the framebuffer mapping. writeb is an unchecked volatile store.

Harness evidence

DF-1818: fill_rect24 slow path (sckmsrndr.c:437-446)
  width=6 height=768 line_width=4096  d=4078 (per-row padding)
  BUGGY (+=line_width at :445): final draw_pos=3159552, 13824 bytes PAST fb end
  FIXED (+=d):                 final draw_pos=3145728, 0 bytes past fb end
  Per-row drift = 3*width = 18 bytes; total drift = (height-1)*3*width = 13806 bytes

Why no live trigger on this guest

The slow path is reached only from kms_draw_border (sckmsrndr.c:487), which runs under the syscons KMS renderer when a VT border is drawn on a KMS framebuffer at 24bpp with non-4-aligned width. The audit guest boots on serial console (console=comconsole), no KMS console attached. Valid Phase-6 hard blocker.

Exploit chain

Not applicable (needs KMS 24bpp console with unaligned border width). No uid=0 claim. Live ceiling: DoS panic when writeb hits an unmapped page, or adjacent kmem corruption if the framebuffer is followed by mapped kernel memory. Border color partially controls written bytes.

PoC changes

  • Added harness.c: reproduces the buggy and fixed slow-path arithmetic side by side.
  • Added fix.diff: draw_pos += line_width β†’ draw_pos += d.

Fix

fix.diff changes line 445 from draw_pos += line_width to draw_pos += d, matching the fast path at :435.

  • BEFORE: harness shows final draw_pos 13824 bytes past fb end.
  • AFTER: harness shows final draw_pos exactly at fb end (0 past).

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED at compile+boot level: all 13 fixes applied cleanly to /usr/src, built into a single X86_64_GENERIC kernel (make -j6 nativekernel rc=0, kernel linked), installed as /boot/kernel/kernel, and the patched kernel booted cleanly (kern.version #1 vs baseline #0). The live PoC cannot run on this guest (HW/config-gated per the verdict), so before/after is at source+harness level: baseline harness: 'final draw_pos=3159552, 13824 bytes PAST fb end' | patched harness: 'final draw_pos=3145728, 0 bytes past fb end'

baseline (#0 unpatched): baseline harness: 'final draw_pos=3159552, 13824 bytes PAST fb end'
patched (#1 kernel, all 13 fixes, booted clean): patched harness: 'final draw_pos=3145728, 0 bytes past fb end'
kernel sha256 c3fff85f... (patched, booted) vs 5dc83dac... (baseline #0)
↓ fix.diffDragonFly 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Mon Jul 20 19:12:20 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

Confirmed kernel references

Detail

Exploit chain

Config-gated (guest uses serial console; no KMS 24bpp console with unaligned border width). No uid=0 escalation claimed. Primitive characterized in harness.c (side-by-side buggy/fixed arithmetic). Live ceiling: DoS panic when writeb hits unmapped page, or adjacent kmem corruption.

Evidence (decisive lines)

DF-1818: fill_rect24 slow path (sckmsrndr.c:437-446)
  width=6 height=768 line_width=4096  d=4078 (per-row padding)
  BUGGY (+=line_width at :445): final draw_pos=3159552, 13824 bytes PAST fb end
  FIXED (+=d):                 final draw_pos=3145728, 0 bytes past fb end
  Per-row drift = 3*width = 18 bytes; total drift = (height-1)*3*width = 13806 bytes

PoC changes

Added harness.c (arithmetic model) and fix.diff (draw_pos += line_width -> += d).

Verified recommended fix

fix.diff changes sckmsrndr.c:445 from 'draw_pos += line_width' to 'draw_pos += d', matching the fast path at :435. matches finding proposal exactly.

Verdict

REPRODUCED at source+harness. fill_rect24 slow path at sckmsrndr.c:445 does draw_pos += line_width instead of += d (d=line_width-width*3 computed at :420). Fast path at :435 correctly uses += d. Slow path taken when (draw_pos|line_width|width)&3!=0. Harness with width=6 height=768 line_width=4096 (the 1366x768@24bpp right-border case) shows 13824-byte framebuffer overrun; fixed path shows 0. Config-gated (guest boots serial console, no KMS 24bpp framebuffer).