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)
PoC verification
Evidence pack
findings/poc/DF-1818 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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
fixedVALIDATED 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)
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- m
- i
- s
- c
- /
- s
- y
- s
- c
- o
- n
- s
- /
- s
- c
- k
- m
- s
- r
- n
- d
- r
- .
- c
- :
- 4
- 2
- 0
- s
- y
- s
- /
- d
- e
- v
- /
- m
- i
- s
- c
- /
- s
- y
- s
- c
- o
- n
- s
- /
- s
- c
- k
- m
- s
- r
- n
- d
- r
- .
- c
- :
- 4
- 3
- 5
- s
- y
- s
- /
- d
- e
- v
- /
- m
- i
- s
- c
- /
- s
- y
- s
- c
- o
- n
- s
- /
- s
- c
- k
- m
- s
- r
- n
- d
- r
- .
- c
- :
- 4
- 4
- 5
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).
No comments yet.