⬢ DragonFlyBSD Kernel Audit
DF-4458 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4458",
  "status": "inconclusive",
  "reproduced": 0,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Code defect certain, runtime GPU-gated: drm_gem_mmap_single (drm_gem.c:1082-1109) creates the OBJT_MGTDEVICE pager with cdev_pager_allocate(..., size, nprot, DRM_GEM_MAPPING_MAPOFF(*offset), ...) where BOTH operands are attacker-controlled and never validated against gem_obj->size: vm_mmap passes the mmap(2) length verbatim (vm_mmap.c:1377 -> drm_drv.c:1270), cdev_pager_allocate grows the vm_object to foff+size instead of clipping (device_pager.c:109-127), i915s cdev_pg_ctor is a no-op (i915_gem.c:6383-6389), and the user adds arbitrary bits 0..39 (in-object offset) to the published fake offset since drm_gem_object_from_offset only validates the KEY and the 22-bit idx (drm_gem.c:1070-1077, drm_gem.h:40-46). The only upstream clamp, drm_gem_mmap_objs obj_size < vma->vm_end - vma->vm_start -> -EINVAL, is #if 0d in this file (drm_gem.c:914-1041, check at 944-946) because DragonFly replaced ->mmap with d_mmap_single without re-implementing it. Every fault in [maoff, maoff+size) therefore reaches the driver pager with an unbounded offset; on i915 (the sole DRIVER_GEM user of this path; radeon/amd take ttm_bo_mmap_single, drm_drv.c:1266-1268) the known driver-side gap DF-1146 converts it into vm_phys_fictitious_to_vm_page(ggtt->gmadr.start + vma->node.start + offset) \u2014 cross-object GGTT read/write. Core-side fix closes the OOB for every BSD-path GEM driver at once. Runtime reproduction impossible on the audit guest: no /dev/dri (QEMU bochs 1111:1234 VGA, no KMS); trigger df4458.c compiles -Wall -Wextra clean on guest cc 8.3 and fails exactly at the gate (open ENOENT, run.log). Attempting to force-load drm.ko on the guest wedges the loader (no hardware) \u2014 not repeated.",
  "exploit_chain": "on GPU hardware: open /dev/dri/card0 (0666) -> CREATE_DUMB (1 page) -> MODE_MAP_DUMB -> fake offset -> mmap(fd, fake + N, len up to 2^40) -> fault at obj->base.size+delta -> i915_gem_fault maps foreign GGTT fictitious pages into the process (read+write other users/objects GPU buffers). Sink mechanics already filed as DF-1146; access-control gap as DF-1633.",
  "evidence": "VERDICT.md hop-by-hop trace (vm_mmap.c:1377, drm_drv.c:1256-1274, drm_gem.c:1082-1109 + dead check 944-946, device_pager.c:109-127, i915_gem.c:6383-6389/2229); run.log (BUILD_RC=0 then open ENOENT); build.log (drm.ko rebuilt with fix, -Werror, RC=0); fix.diff (git apply --check OK)",
  "kernel_refs": [
    "sys/dev/drm/drm_gem.c:1082",
    "sys/dev/drm/drm_gem.c:1099",
    "sys/dev/drm/drm_gem.c:944",
    "sys/vm/vm_mmap.c:1377",
    "sys/vm/device_pager.c:109",
    "sys/dev/drm/i915/i915_gem.c:6383"
  ],
  "poc_changes": "no seed; wrote complete trigger from scratch (dumb-buffer + two attack shapes), DragonFly UAPI structs inlined; compiled clean on guest cc 8.3 first try after fixing an argv ternary typo",
  "attempts": 2,
  "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": 600,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -Wall -Wextra -o /tmp/df4458 /tmp/df4458.c   (guest; BUILD_RC=0)",
  "run_cmd": "/tmp/df4458 /dev/dri/card0   (guest: open ENOENT \u2014 no DRM device)",
  "code_hash": "899b113404c6e95937abacd1d7673eccecc60e2c26d3be850eed331cbb9b3e86",
  "notes": "Pass-2 negatives in VERDICT.md: drm_gem_open_ioctl post-put size read safe (handle ref), handle_create_tail unwind balanced, flink/close race safe, vm_object_deallocate(NULL) safe, handle_delete idr_replace pattern safe, drm_gem_object_init cannot observe NULL vm object, export lifecycle in drm_prime.c not this file. Guest /usr/src restored pristine (md5 ad7317cd\u2026 matches); guest up, no dirty state.",
  "recommended_fix": "In drm_gem_mmap_single, reject mappings that leave the object: maoff = DRM_GEM_MAPPING_MAPOFF(*offset); if (size > gem_obj->size || maoff > gem_obj->size - size) return EINVAL",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff (git apply --check OK) adds the maoff+size <= obj->size clamp before cdev_pager_allocate. Compile-validated on the guest: drm.ko rebuilt with the fix (combined with sibling DF-4459/DF-4460 hunks), -Werror, RC=0 (build.log). Behavioral baseline/patched runs impossible: no GPU on the guest (fix_status not_testable, compile-validated).",
  "fix_evidence": "fix.diff; build.log (patched drm.ko full -Werror compile+link, DRM_BUILD_RC=0); VERDICT.md Fix validation"
}