⬢ DragonFlyBSD Kernel Audit
DF-4569 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4569",
  "status": "inconclusive",
  "reproduced": 0,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "Root cause certain and kernel-side machinery reproduced on the guest: drm_ioctl() dispatches on cmd&0xff only and never validates the user command against the canonical ioctl->cmd (upstream v4.7s drv_size/usize/asize + cmd=ioctl->cmd logic was dropped by the port), while mapped_ioctl() sizes the handler buffer from the users IOCPARM_LEN/dir bits. Via an exact-semantics replica module, an unprivileged user demonstrated (1) drm_getstats-style memset(248) over the 128-byte on-stack buffer -> return address zeroed -> deterministic fatal kernel-mode page fault at RIP=0 (panic.txt), and (2) drm_getmagic-style 4-byte-write+return-0 handler turning the IOC_VOID|IOC_OUT cmd into an up-to-8KB kernel stack disclosure (27-30 kernel pointer qwords per run, varying across runs). The final DRM-specific hop (nr -> drm_getstats/drm_getmagic through the real table) is proven statically (drm_ioctl.c:215-224, 573, 577; drm_auth.c:58-76) but could not execute on this guest because it has no DRM device (QEMU std VGA, no /dev/dri, DRM not built into X86_64_GENERIC) -- hence inconclusive rather than reproduced.",
  "exploit_chain": "open /dev/dri/cardN (root:video 0660; standard desktop session user) -> ioctl(fd, IOC_OUT|(8<<16)|(d<<8)|0x06) -> mapped_ioctl hands drm_ioctl the 128-byte stack buffer -> drm_getstats memsets sizeof(struct drm_stats)=248 -> 120-byte kernel stack overflow, RIP=0 panic. Leak variant: ioctl(fd, IOC_VOID|IOC_OUT|(size<<16)|0x02) -> drm_getmagic returns 0 -> mapped_ioctl copyouts `size bytes from the 128-byte stack buffer -> up to ~8KB kernel stack disclosure (pointers, trapframe data). Privesc not demonstrated: the overwrite content is zeros-only on this table (drm_stats is the only >128B core struct; driver structs <=72B; heap sizes 129..247 contained by the 256B kmalloc bucket).",
  "evidence": "run.log [1]-[4] (full transcripts: leak hexdump with kernel pointers, 3-run md5 variance, 8191-byte probe, smash console output), panic.txt (Fatal trap 12, instruction pointer = 0x8:0x0, unpriv process 1059), leak_sample.txt, dfioctl_rep.c (replica of drm_ioctl dispatch semantics + drm_getmagic/drm_getstats behaviors), df_primitive.c (unpriv trigger), drm_size_confusion.c (real-DRM trigger for hardware boxes), fix.diff (canonical-cmd check, applied cleanly to guest /usr/src)",
  "kernel_refs": [
    "sys/dev/drm/drm_ioctl.c:801",
    "sys/dev/drm/drm_ioctl.c:809",
    "sys/dev/drm/drm_ioctl.c:819-831",
    "sys/dev/drm/drm_ioctl.c:847",
    "sys/dev/drm/drm_ioctl.c:854",
    "sys/dev/drm/drm_ioctl.c:215-224",
    "sys/dev/drm/drm_ioctl.c:573",
    "sys/dev/drm/drm_ioctl.c:577",
    "sys/dev/drm/drm_auth.c:58-76",
    "sys/kern/sys_generic.c:668-680",
    "sys/kern/sys_generic.c:729-730",
    "sys/dev/drm/include/uapi/drm/drm.h:262-268",
    "sys/dev/drm/drm_drv.c:1178",
    "sys/dev/drm/include/drm/drmP.h:108-110",
    "linux v4.7 drivers/gpu/drm/drm_ioctl.c (drv_size/usize/asize + cmd=ioctl->cmd)"
  ],
  "poc_changes": "The finding seed had no runnable PoC; everything was authored in this run. Guest build required the kmod.mk route (hand-linked .ko panicked the linker with lost base for relatab); replica module needed DFly-specific dev_ops/dev_ioctl_args fixes (sys/device.h + sys/conf.h, cdev_t). df_primitive adds the VOID|OUT leak mode and the smash mode with hand-encoded BSD ioctl command words.",
  "attempts": 4,
  "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": 1800,
  "guest_dirty": 0,
  "build_cmd": "make KMOD=dfioctl_rep (via /usr/src/sys/conf/kmod.mk); cc -O2 -Wall -o df_primitive df_primitive.c; cc -O2 -Wall -o drm_size_confusion drm_size_confusion.c",
  "run_cmd": "kldload ./dfioctl_rep.ko; /tmp/df_primitive leak 2048; /tmp/df_primitive leak 8191; /tmp/df_primitive smash 8",
  "code_hash": "6bddf09c5ee35b9061205ceec977db1e3f6a2812adf007ac9d124cffb8182fd2",
  "notes": "Guest has no DRM device (QEMU std VGA chip 0x1111:1234; no /dev/dri; DRM not in X86_64_GENERIC) so the real drm_ioctl() path cannot execute there; replica-module proof per established DF-3715 practice. The over-large IOC_VOID copyout is a sys/kern/sys_generic.c defect (related pass-1 finding DF-2734 covered only the uninit-buffer-to-driver aspect) -- recommend a separate kern finding + fix (bound the IOC_VOID copyout to a pointer word or allocate/bzero like non-VOID). Guest was reset with-src after the deliberate panic; scratch files removed.",
  "recommended_fix": "In drm_ioctl(), require (unsigned int)cmd == ioctl->cmd (port-side equivalent of upstream cmd=ioctl->cmd) before permit/dispatch; independently bound mapped_ioctl()s copyout for IOC_VOID commands.",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff authored and verified to apply cleanly to the guests byte-identical /usr/src/sys/dev/drm/drm_ioctl.c (patch -p0 RC=0; GNU patch dry-run RC=0). Runtime before/after validation impossible on this guest: no DRM device and DRM is not compiled into X86_64_GENERIC, so the patched dispatch cannot be exercised. Baseline (unpatched) behavior was reproduced via the exact-semantics replica module (leak + RIP=0 panic).",
  "fix_evidence": "findings/poc/DF-4569/fix.diff; VERDICT.md Fix section; guest apply-check transcript in run.log notes (patch -p0 RC=0, grep -c DF-4569 == 1)"
}