DF-4458 / verdict.json
{ "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" } |