Pixel-mode cursor draw functions lack the cursor_base < font_height bound the text path enforces: negative font-buffer index and negative video-memory offset
Summary
draw_pxlcursor_direct/packed/planar compute (font_height - cursor_base - 1) without validating cursor_base (unlike draw_txtcharcursor which early-returns). cursor_base >= font_height yields negative scanline offsets: char_data points before scp->font and draw_pos above va_window. cursor_base is BIOS-derived and not ioctl-settable (QEMU yields 0), so the mismatch needs firmware cursor_end <= 7 - hardening gap. Mirror-the-text-path-check fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4422 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.7 KB | β raw | |
| VERDICT.md | β | 2.1 KB | β raw | |
| fix.diff | β | 975 B | view raw | |
| manifest.json | β | 605 B | view raw | |
| verdict.json | β | 2.2 KB | view raw |
DF-4422 β draw_pxlcursor_{direct,packed,planar} lack the cursor_base < font_height bound the text path enforces
scvgarndr.c text-mode path validates before use (draw_txtcharcursor:236): if (scp->cursor_base >= h) return; The three pixel-mode cursor draw functions instead use scp->cursor_base unvalidated: draw_pos = VIDEO_MEMORY_POS(scp, at, ...) + (scp->font_height - scp->cursor_base - 1) * line_width; (:789-790, :841-842, :890-891) char_data = &scp->font[... + scp->font_height - scp->cursor_base - 1];
With cursor_base >= font_height (char field, syscons.h:294) both offsets go NEGATIVE: OOB READ before scp->font and OOB WRITE (writel/writeb) that many scanlines ABOVE va_window (before the framebuffer/VGA aperture mapping).
Reachability analysis (why Low/speculative): scp->cursor_base is refreshed from sc->cursor_base via update_cursor_image/sc_set_cursor_image (syscons.c:3183,3205) which clamp only with imax(0, ...) β an upper bound is missing β but sc->cursor_base itself is only ever set at scinit from the BIOS cursor shape (syscons.c:3372: font_height(16) - bios_value.cursor_end - 1, bounded < 16), and no ioctl can set it. A mismatch therefore requires sc->cursor_base >= font_height of a LATER pixel mode with a smaller font (8/14), i.e. BIOS cursor_end <= 7 at boot with a subsequent 8-line pixel mode. QEMU/SeaBIOS reports cursor_end=15 (base 0), so the standard guest cannot trigger it; real firmware varies. CONS_CURSORTYPE (SC_CHAR_CURSOR, scvgarndr.c:221) is reachable unprivileged from the console tty, which is what makes the path live at all.
Verdict: untested (Low severity hardening; trigger is firmware-dependent, not attacker-controlled on this rig). Fix mirrors the text path check.
DF-4422 β draw_pxlcursor_{direct,packed,planar} lack the cursor_base < font_height bound the text path enforces
scvgarndr.c text-mode path validates before use (draw_txtcharcursor:236): if (scp->cursor_base >= h) return; The three pixel-mode cursor draw functions instead use scp->cursor_base unvalidated: draw_pos = VIDEO_MEMORY_POS(scp, at, ...) + (scp->font_height - scp->cursor_base - 1) * line_width; (:789-790, :841-842, :890-891) char_data = &scp->font[... + scp->font_height - scp->cursor_base - 1];
With cursor_base >= font_height (char field, syscons.h:294) both offsets go NEGATIVE: OOB READ before scp->font and OOB WRITE (writel/writeb) that many scanlines ABOVE va_window (before the framebuffer/VGA aperture mapping).
Reachability analysis (why Low/speculative): scp->cursor_base is refreshed from sc->cursor_base via update_cursor_image/sc_set_cursor_image (syscons.c:3183,3205) which clamp only with imax(0, ...) β an upper bound is missing β but sc->cursor_base itself is only ever set at scinit from the BIOS cursor shape (syscons.c:3372: font_height(16) - bios_value.cursor_end - 1, bounded < 16), and no ioctl can set it. A mismatch therefore requires sc->cursor_base >= font_height of a LATER pixel mode with a smaller font (8/14), i.e. BIOS cursor_end <= 7 at boot with a subsequent 8-line pixel mode. QEMU/SeaBIOS reports cursor_end=15 (base 0), so the standard guest cannot trigger it; real firmware varies. CONS_CURSORTYPE (SC_CHAR_CURSOR, scvgarndr.c:221) is reachable unprivileged from the console tty, which is what makes the path live at all.
Verdict: untested (Low severity hardening; trigger is firmware-dependent, not attacker-controlled on this rig). Fix mirrors the text path check.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testablenot_testable: precondition not constructible on this guest (BIOS-derived cursor_base = 0)
Confirmed kernel references
Detail
Evidence (decisive lines)
README.md trace; syscons.c:3183/3205 (imax(0,...) only), syscons.c:3371-3376 (BIOS-derived bound), scvgarndr.c:236 (the check the pixel paths lack).
PoC changes
n/a
Verified recommended fix
in vga_pxlcursor_direct/packed/planar: bail out when scp->cursor_base < 0 || scp->cursor_base >= scp->font_height (mirroring scvgarndr.c:236)
Verdict
untested: Low-severity missing-bound hardening gap. draw_pxlcursor_direct/packed/planar (scvgarndr.c:789-791, 841-842, 890-891) compute (font_height - cursor_base - 1) without the cursor_base < font_height validation that the text-mode path performs at :236; a stale/larger cursor_base yields a negative font-buffer index (OOB read before scp->font) and a negative va_window offset (OOB write above the framebuffer mapping). The precondition (sc->cursor_base >= pixel-mode font_height) cannot be produced on this guest: sc->cursor_base is BIOS-derived (syscons.c:3372, bounded < boot font_height 16; QEMU SeaBIOS yields 0) and no ioctl sets it; only unusual firmware cursor_end values plus a smaller-font pixel mode can trip it. SC_PIXEL_MODE and the unprivileged CONS_CURSORTYPE ioctl path are compiled in and live (verified on the guest: opt_syscons.h SC_PIXEL_MODE=1, X86_64_GENERIC).
No comments yet.