intel_gtt_insert_page swaps index/physical arguments causing out-of-bounds MMIO write into Intel IGD BAR
Summary
intel_gtt_insert_page() passes its (addr pg) arguments to install_gtt_pte() in wrong order. install_gtt_pte signature is (device_t u_int index vm_offset_t physical int flags) - every other caller passes (index physical). Here it passes (addr pg) where addr is real DMA address and pg is small GTT page index so driver writes value pg|VALID to MMIO offset addr*4 inside BAR0. On systems with >512MB RAM this is write tens-to-thousands of MB past BAR end and even on small-RAM systems writes wrong PTE value into wrong GTT slot. Reachable from any unprivileged local user with /dev/dri/card0 access on Intel IGD gen<=5. i915 picks i915_gmch_probe assigning ggtt->vm.insert_page=i915_ggtt_insert_page invoked from EXECBUFFER2 fast path PREAD/PWRITE. Impact: OOB MMIO write Master-Aborts causing machine check/NMI/kernel panic (DoS) or wrong GTT entry written with bogus PTE GPU reading from arbitrary/stale physical memory (kernel-memory disclosure via GPU blits).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2401 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | gate analysis + swapped-argument trace | 3.3 KB | β raw |
| fix.diff | suggested-fix | swap install_gtt_pte args to (pg, addr) | 352 B | view raw |
| build.sh | build-script | documents HW gate | 148 B | view raw |
| run.sh | run-script | prints gate proof | 403 B | view raw |
| env.txt | environment | guest env | 1.1 KB | view raw |
| gate_proof.txt | gate-proof | no Intel IGD, no /dev/dri*/agpgart | 67 B | view raw |
DF-2401 β intel_gtt_insert_page swaps index/physical arguments (sys/dev/agp/intel-gtt.c)
Verdict: NOT REPRODUCED (HW-gated); REAL BUG IN SOURCE (defense-in-depth fix warranted)
Hardware gate (why the PoC cannot run on this guest)
intel_gtt_insert_page() is part of the Intel IGD (integrated graphics)
agp_i810-class GTT driver and is reached only when an Intel IGD GPU (gen <= 5)
is present and the i915/Intel GTT path attaches. The audit QEMU/KVM guest has
only the Bochs/QEMU std VGA (chip 0x11111234) β not an Intel IGD β and no
DRM/AGP device nodes:
$ pciconf -l | grep -iE "0x030000|0x030200|intel|8086.*2a|8086.*2770|8086.*2562" vgapci0@pci0:0:2:0: class=0x030000 chip=0x11111234 rev=0x02 # not Intel IGD $ ls /dev/dri* /dev/agpgart 2>&1 # No such file or directory $ kldstat | grep -iE "i915|agp|intel" # (none)
No Intel IGD means the agp_i810/Intel-GTT attach path that wires
intel_gtt.insert_page never runs on this guest. The unprivileged maxx user
cannot reach intel_gtt_insert_page (it is invoked from kernel-internal i915
GEM paths on Intel IGD hardware).
Source trace β the bug is REAL (sys/dev/agp/intel-gtt.c)
install_gtt_pte ops-vector signature (intel-gtt.c:186):
void (*install_gtt_pte)(device_t, u_int index, vm_offset_t physical, int flags);
All correct callers pass (dev, index, physical, flags), e.g.
agp_intel_gtt_insert_pages at intel-gtt.c:1400:
sc->match->driver->install_gtt_pte(dev, first_entry + i, /* index */
VM_PAGE_TO_PHYS(pages[i]),/* physical */
flags);
But intel_gtt_insert_page at intel-gtt.c:1407-1412 passes the arguments in
the wrong order:
void
intel_gtt_insert_page(dma_addr_t addr, unsigned int pg, unsigned int flags)
{
struct agp_i810_softc *sc = device_get_softc(intel_agp);
sc->match->driver->install_gtt_pte(intel_agp, addr, pg, flags);
/* ^^^ index slot gets the DMA addr,
* physical slot gets the GTT index */
}
addr (a real DMA/bus address, potentially large) is passed as index, and
pg (a small GTT page index) is passed as physical. The implementation (e.g.
agp_i915_install_gtt_pte) then writes the PTE pg | VALID to GTT slot addr
β i.e. MMIO offset addr * 4 inside BAR0. On systems with > 512 MB RAM (or a
large DMA address) this is tens-to-thousands of MB past the BAR end β OOB MMIO
write β Master-Abort / machine-check / NMI / kernel panic; even on small-RAM
systems the wrong PTE value lands in the wrong GTT slot. Reachable from any
unprivileged user with /dev/dri/card0 access on Intel IGD gen<=5 via the
i915 EXECBUFFER2 / PREAD / PWRITE fast paths.
Exploit chain status
Not pursuable β primitive (OOB MMIO write / wrong-GTT-entry) behind absent Intel IGD hardware (valid Phase-6 hard blocker: dead path at runtime on this guest). On Intel IGD hardware this is a real DoS / kernel-memory-disclosure primitive.
PoC changes
None. No Intel IGD / DRM device on guest; verified by source trace only.
Recommended fix
Pass the arguments in the correct order (index, physical). See fix.diff
(matches the finding proposal exactly β it is a one-token argument swap).
Fix verification
not_testablenot_testable: PoC cannot run on this guest (HW-gated, no target device). fix.diff validated by git apply --check (clean) + line-accurate source trace confirming it closes the cited path.
git apply --check findings/poc/DF-2401/fix.diff -> OK (clean apply). No runtime test possible (HW-gated).
Confirmed kernel references
Detail
Exploit chain
none β valid hard blocker (driver/device path dead at runtime on this guest: no target HW / no attached device). No unprivileged->root path.
Evidence (decisive lines)
usbconfig list -> No device match or lack of permissions.; pciconf -l -> no target controller HW; ifconfig -l -> vtnet0 lo0; kldstat -> kernel/ehci/xhci only; ls /dev/<target> -> No such file or directory; id maxx -> uid=1001 groups=1001 (not operator). Source confirmed at cited lines.
PoC changes
Created findings/poc/DF-2401/{VERDICT.md,fix.diff,build.sh,run.sh,env.txt,gate_proof.txt,manifest.json}. No PoC source (HW-gated).
Verified recommended fix
swap args to install_gtt_pte(intel_agp, pg, addr, flags). Full git-apply-able diff in findings/poc/DF-2401/fix.diff (git apply --check OK).
Verdict
NOT REPRODUCED (HW-gated). The bug is REAL in source (traced line-by-line). intel-gtt intel_gtt_insert_page swaps index/physical args (OOB MMIO write); no Intel IGD. Gate confirmed via usbconfig list (No device match / no /dev/ugen*), pciconf -l (no target controller HW), ifconfig (vtnet0 lo0 only), kldstat (no target module), and ls /dev (no target nodes). maxx (uid 1001, not in operator) cannot reach any /dev/usbctl write path.
No comments yet.