⬢ DragonFlyBSD Kernel Audit
DF-1702 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-1702",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Reproduced on the guest console, from an unprivileged user: mouse_cut() (scmouse.c:319-330) writes one \\r per fully-selected line plus a trailing NUL into a cut_buffer sized xsize*ysize+1 (sc_alloc_cut_buffer, scmouse.c:82-98). A full-screen 40x25 selection pastes exactly 1024 bytes (999 chars + 25 \\r) back through the console tty - byte-for-byte the computed content - proving cut_buffer[0..1023] were populated and the terminating NUL was written at index 1024, i.e. 24 bytes past the 1001-byte kmalloc request. HOWEVER the High/memcorrupt impact is downgraded by measurement: cut_buffer is boot-sized for the 80x25 console (kmalloc(2001) -> 2048-byte slab chunk) and only ever grows, while the write excess is bounded by ysize (<=60), smaller than the >=128-byte granularity of the slab size classes above 1 KiB - for every reachable text mode the writes stay inside the physical chunk (2025<2048, 2430<2560, 3483<3584, 4050<4096, 4860<5120, 3325<3328, 6650<6656). 3000 overflowing cuts under fork/exec churn left the guest healthy. So the bug is a real kernel invariant violation (OOB write past the requested allocation size) that becomes true heap corruption only under an exact-size/redzone allocator or a future text mode with ysize exceeding the class slack; uid=0 is not reachable with the compiled-in mode set. Recommended severity: Low (latent memory-safety defect), fix still warranted.",
  "exploit_chain": "unprivileged console user -> open /dev/ttyv0 (or own controlling tty fd) -> CONS_SET mode 23 (40x25) -> fill screen with non-space chars -> CONS_MOUSECTL: MOUSE_SHOW, MOVEABS(0,0), BUTTON1 press, MOVEABS(319,399) drag -> mouse_cut() full-screen cut writes indices up to xsize*ysize+ysize -> paste-button returns the OOB-written bytes (1024 > 1000 nominal). Escalation to uid=0: BLOCKED - on this kernel every write lands in the same slab chunks allocator slack (arithmetic above); no adjacent-object corruption exists to groom.",
  "evidence": "run.log (root: paste=1024 bytes / 25 CRs, the exact computed full-screen content), run.user.log (uid=1001: paste=1015/25 CRs - unprivileged), run.2.log (re-reproduced after a fresh vm.sh reset with-src: 1024/25 CRs), stress.c+VERDICT.md (3000 overflowing cuts + churn: no panic), fix.diff + fix_build.log + fix_run.log (patched kernel #1: same legal content 1010<=1026, in bounds)",
  "kernel_refs": [
    "sys/dev/misc/syscons/scmouse.c:319",
    "sys/dev/misc/syscons/scmouse.c:325",
    "sys/dev/misc/syscons/scmouse.c:330",
    "sys/dev/misc/syscons/scmouse.c:346",
    "sys/dev/misc/syscons/scmouse.c:82",
    "sys/dev/misc/syscons/scmouse.c:86",
    "sys/kern/kern_slaballoc.c:638"
  ],
  "poc_changes": "Replaced the seeded userspace logic harness (which wrongly claimed the guest lacks the hardware) with a real in-guest PoC on /dev/ttyv0: CONS_SET to 40x25, CONS_SCRSHOT-verified screen fill (the tty output queue needs chunked writes), full-screen cut via the CONS_MOUSECTL mouse state machine, and a concurrent tty reader because the ~1KiB raw-input queue truncates single-reader paste counts; best-of-25 cycles gives the full 1024-byte delivery.",
  "attempts": 11,
  "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": 480,
  "guest_dirty": 0,
  "build_cmd": "cc -O -o df1702 df1702.c",
  "run_cmd": "./df1702 /dev/ttyv0 23 40 25",
  "code_hash": "sha256:e2c7254d91c8db86a32cbecefd2d1413bfe4c1e85701715c8270a71147198f96",
  "notes": "Paste-count variance 1015-1024 across runs comes from rawq drops during the burst; 1024 (the theoretical strlen) observed on three separate guest boots. First invocation on a cold boot can flake (mouse-visibility timing) - immediate retry reproduces (run.2.log). mouse_cut_line()s xsize+2 write needs ysize==1 - no such mode exists. sc_mouse_ioctl runs under vga_token (syscons.c:969-1006), serializing cut/paste against CONS_SET mode changes - no cut_buffer UAF window (checked negative). scrn_update clamps scp->start/end (syscons.c:2321-2325) so mouse_cuts sentinel one-past marks are harmless.",
  "recommended_fix": "Size cut_buffer for the worst case in sc_alloc_cut_buffer(): xsize*ysize + ysize + 1 (accounts for the per-line \\r bytes and the trailing NUL).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff (sc_alloc_cut_buffer sizing xsize*ysize+ysize+1) applied to the guest /usr/src, kernel rebuilt (nativekernel, X86_64_GENERIC) and booted; the exact PoC re-run: cut/paste fully functional with the same 1024-byte legal content and the guest stable. The OOB write is closed by construction: max write index = xsize*ysize + ysize = size-1. Baseline wrote at index 1024 of a 1001-byte request; patched allocation is 1026 bytes (1088-class chunk) and the same cut stays in bounds.",
  "fix_evidence": "fix_build.log (kernel build tail with BUILD_AND_INSTALL_DONE), fix_run.log (patched-kernel PoC run: OVERFLOW content identical, guest up), VERDICT.md section 4"
}