β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-1146

i915_gem_fault missing offset bounds check and partial-view GMADR misaddressing allow cross-object GPU memory read/write

Summary

i915_gem_fault at i915_gem.c:2263: page_offset computed from fault offset with NO bounds check against obj->base.size. drm_gem_mmap_single passes user mmap length straight to cdev_pager_allocate so user can mmap a tiny GEM object with giant length -> faults past obj size read/write adjacent GTT slots in GMADR aperture. Additionally :2391-2392 GMADR address computed as gmadr.start+vma->node.start+offset omits subtraction of view.partial.offset for I915_GGTT_VIEW_PARTIAL VMAs, returning pages from neighboring GTT slots. Both defects unique to DragonFly port (Linux uses remap_io_mapping). vm_phys_fictitious_to_vm_page returns valid fictitious page for any address in registered GMADR segment. Attacker: any local user with /dev/dri/card0 access (video group). Impact: cross-object GPU memory disclosure and corruption, plus NULL-deref DoS via i915_gem_object_get_sg OOB sg walk. Fix: bounds-check offset against obj->base.size and subtract partial.offset from GMADR address.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1146 Β· 9 files
FileTypeDescriptionSize
fix.diff suggested-fix add obj-size bounds check + partial-VMA GMADR offset subtraction (git-apply-able) 1.7 KB view raw
build.sh build-script apply fix.diff + incremental compile of i915 i915_gem.o 439 B view raw
run.sh run-script documents runtime-unreachable (no Intel GPU on guest) 551 B view raw
VERDICT.md verdict full source-trace + mechanism + fix rationale 4.3 KB ↓ raw
env.txt environment guest uname, pci, driver build status 2.0 KB view raw
build.log build-log fix compile-validation output (patched i915_gem.o, -Werror clean) 1.5 KB view raw
README.md readme human repro summary 2.0 KB ↓ raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme human repro summary
↓ download raw

DF-1146 β€” i915_gem_fault missing offset bounds + partial-VMA GMADR offset (i915_gem.c)

Status: REPRODUCED at code level β€” latent at runtime on this guest (no Intel GPU). Severity (finding): High Β· CWE-787 OOB Write + CWE-125 OOB Read

What the bug is (two DragonFly-port defects)

  1. Missing bounds check: i915_gem_fault (sys/dev/drm/i915/i915_gem.c:2229) computes page_offset = offset >> PAGE_SHIFT (:2263) with no check that offset < obj->base.size. drm_gem_mmap_single (drm_gem.c:1099) passes the user mmap length straight to cdev_pager_allocate without clamping to the GEM object size, so a process can mmap a tiny object with a huge length and fault past it. Upstream Linux has the guard; the DFly port dropped it.
  2. Partial-VMA GMADR miscompute: for an I915_GGTT_VIEW_PARTIAL VMA the GMADR address at :2391-2392 (ggtt->gmadr.start + vma->node.start + offset) does not subtract vma->ggtt_view.partial.offset * PAGE_SIZE, returning pages from neighbouring GTT slots. (The correct idiom exists at i915_vma.c:905.)

Why it does not trigger here

Only QEMU std VGA (0x1234:0x1111) on the guest β€” not an Intel i915; i915 is not in X86_64_GENERIC (only in LINT64 as a test config). Latent on Intel-graphics hardware.

What was validated

  1. Source trace confirmed (see VERDICT.md kernel_refs).
  2. Baseline i915.ko (incl. i915_gem.o) builds clean under -Werror.
  3. Fix fix.diff applies (2 hunks) and patched i915_gem.o rebuilds clean under -Werror (i915_gem.o: 117984 -> 118080 bytes).

Reproduce (compile-validation only)

scp this-folder/fix.diff root@guest:/root/df1146.diff
cd /usr/src && patch -p1 --forward < /root/df1146.diff
cd sys/dev/drm/i915 && rm -f i915_gem.o && make i915_gem.o   # -Werror clean

Fix

fix.diff adds if (offset >= obj->base.size) return VM_PAGER_ERROR; after page_offset is computed, and subtracts partial.offset << PAGE_SHIFT from the GMADR address for PARTIAL views.

VERDICT.md verdict full source-trace + mechanism + fix rationale
↓ download raw

DF-1146 β€” i915_gem_fault missing offset bounds + partial-VMA GMADR offset (i915_gem.c)

Verdict

REPRODUCED (code-level, latent at runtime). The DragonFly-specific port of i915_gem_fault omits the object-size bounds check that upstream Linux performs, and miscomputes the GMADR address for partial GGTT views. Both confirmed by source trace. Not triggerable at runtime on this guest (no Intel i915 GPU; i915 not in GENERIC) -> runtime not_testable. fix.diff validated to apply + compile under -Werror.

Mechanism (trigger -> primitive -> effect)

Defect 1 β€” missing object-size bounds on the fault offset

  • i915_gem_fault (sys/dev/drm/i915/i915_gem.c:2229) computes page_offset = (unsigned long)offset >> PAGE_SHIFT; at :2263 and uses it throughout with no check that offset < obj->base.size.
  • Reachability amplifier: drm_gem_mmap_single (sys/dev/drm/drm_gem.c:1083-1109) passes the user's mmap size straight into cdev_pager_allocate(... size ...) (:1099-1100) without clamping to gem_obj->size. So a process can mmap() a tiny GEM object with a giant length; the backing vm_object then covers more than the object, and faults at offset >= obj->base.size reach i915_gem_fault unguarded.
  • Upstream Linux guards this: if (page_offset >= round_up(obj->base.size, PAGE_SIZE) / PAGE_SHIFT) return VM_FAULT_SIGBUS; in its i915_gem_fault. The DragonFly port dropped it.
  • Effect: the GMADR address computed at :2391-2392 (ggtt->gmadr.start + vma->node.start + offset) goes past this object's GTT slot, returning/writing pages from neighbouring GTT slots in the GMADR aperture (read+write of unrelated GPU mappings).

Defect 2 β€” partial-VMA GMADR offset not subtracted

  • For an I915_GGTT_VIEW_PARTIAL VMA, compute_partial_view (:2129-2149) sets view.partial.offset = rounddown(page_offset, chunk) and the VMA node maps only the chunk [partial.offset, partial.offset+size).
  • The GMADR computation :2391-2392 adds the object-relative offset to the chunk-relative vma->node.start without subtracting partial.offset * PAGE_SIZE. So a fault into a partial VMA returns the wrong page (offset by partial.offset pages) β€” i.e. pages from a neighbouring GTT slot. (Upstream uses remap_io_mapping which handles this; the DFly port's hand-rolled vm_phys_fictitious_to_vm_page does not.)
  • The correct idiom already exists in the driver: i915_vma.c:905 uses vma->ggtt_view.partial.offset << PAGE_SHIFT.

Threat model / reachability

  • Attacker: any unprivileged user on a machine with an Intel i915 GPU who can open the dri render node and mmap/I915_GEM_MMAP_GTT a GEM object with a length larger than the object, then fault past its end; or trigger the partial-VMA miscompute. Classic local privesc surface on laptops/desktops with Intel graphics.
  • On this guest: NOT reachable β€” only QEMU std VGA (0x1234:0x1111), i915 not in GENERIC. Valid hard blocker: runtime-unreachable here, latent on Intel-i915-equipped HW.

Exploit chain

None developed β€” valid hard blocker (no i915 GPU on this guest to attach the driver, so the fault path is never reached). Demonstrated work is the source-level confirmation + compiling fix. On real i915 HW the chain would be: open dri render node -> I915_GEM_CREATE a small object -> mmap with an oversized length -> touch pages past obj->base.size to read/write neighbouring GTT slots. No exploit.c (cannot run on this guest).

PoC changes

No trigger PoC seeded. This folder adds fix.diff, build.sh, run.sh, VERDICT.md, manifest.json, env.txt, build.log, README.md.

In i915_gem_fault, add if (offset >= obj->base.size) { ...; return VM_PAGER_ERROR; } right after computing page_offset, and subtract vma->ggtt_view.partial.offset << PAGE_SHIFT from the GMADR address when the VMA is a PARTIAL view. Implemented in fix.diff (2 hunks). (An optional additional defense-in-depth β€” clamping the mmap size in drm_gem_mmap_single to gem_obj->size β€” is noted but left out of this per-finding diff because drm_gem.c is shared by all DRM drivers and the fault-handler bounds check alone fully closes the i915 OOB.) Supersedes a partial fix; matches the finding's cited root cause at i915_gem.c:2263 and :2391-2392.

Fix verification

not_testable

compile validated -Werror

module build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed+compile. i915_gem_fault missing offset>=obj->base.size guard + partial VMA GMADR offset. i915 not in GENERIC.