# DF-4255 VERDICT — agp_i915_write_gtt writes GTT PTEs to the wrong BAR (MMADR, the register window) instead of GTTADR

## Status: inconclusive on the audit guest (hardware-gated). Root cause certain at source level.

## The bug

DragonFly `sys/dev/agp/intel-gtt.c:998-1004`:

```c
static void
agp_i915_write_gtt(device_t dev, u_int index, uint32_t pte)
{
	struct agp_i810_softc *sc;

	sc = device_get_softc(dev);
	bus_write_4(sc->sc_res[0], index * 4, pte);      /* <-- WRONG BAR */
}
```

`agp_i915_res_spec` (intel-gtt.c:139-143) maps two BARs for the CHIP_I915
driver family (915G 0x2582, E7221 0x258A, 915GM 0x2592, 945G 0x2772,
945GM 0x27A2, 945GME 0x27AE — intel-gtt.c:318-347):

* `sc_res[0]` = AGP_I915_MMADR (BAR 0x10) — the memory-mapped **register**
  window (128 KB on 915G/945G).
* `sc_res[1]` = AGP_I915_GTTADR (BAR 0x1C) — the **GTT** itself.

The file's own `agp_i915_sync_gtt_pte` (intel-gtt.c:1092-1098) reads the GTT
back from `sc_res[1]`, and FreeBSD upstream — the direct ancestor of this
file — programs the PTE through `sc_res[1]` as well
(freebsd-src sys/dev/agp/agp_i810.c, agp_i915_write_gtt:
`bus_write_4(sc->sc_res[1], index * 4, pte);`). Only the DragonFly copy
writes `sc_res[0]`. This is a transcription divergence with security
consequences, not an intentional change: nothing else in the file treats
MMADR as containing the GTT for gen3.

## Consequences on affected hardware

Reachable from unprivileged userspace: any GGTT bind on a gen3 part goes
i915_ggtt_insert_entries (i915_gem_gtt.c:2703-2713) →
intel_gtt_insert_sg_entries (intel-gtt.c:1415) → agp_i915_install_gtt_pte
(intel-gtt.c:985) → agp_i915_write_gtt. Same for
i915_ggtt_insert_page → intel_gtt_insert_page (a distinct function from the
known DF-1476/DF-2401 argument swap; the argument order here is correct —
the *destination BAR* is wrong).

1. **Register-space corruption (any aperture size):** PTE index i is
   written at MMADR + i*4, clobbering live GPU registers. PTE index 0x808
   lands exactly on MMADR+0x2020 == `AGP_I810_PGTBL_CTL` (agpreg.h:198) —
   the GTT base pointer — reprogramming it to (GEM page phys | 1). A user
   that can get its GEM page installed as the GTT base effectively gets to
   author GTT entries from userspace (mmap'd GEM contents), i.e. arbitrary
   GPU-visible physical mappings → GPU DMA anywhere → full system
   compromise chain on real hardware.
2. **Out-of-bounds write past the mapped resource (aperture > 128 MB):**
   bus_space handles are KVA mappings sized to the BAR. With the common
   256 MB aperture, gtt_mappable_entries = 65536, and PTE indices
   32768..65535 write MMADR+128 KB .. MMADR+256 KB — the first 128 KB of
   which lies entirely past the end of the mapped MMADR resource, into
   whatever kernel virtual memory adjoins it. Contents are
   attacker-influenced (page frame numbers of user GEM pages | PTE flags).

## Why inconclusive on this guest

The audit guest is QEMU/KVM without any Intel integrated graphics:
`pciconf -l` shows no 8086:2582/258A/2592/2772/27A2/27AE device and
agp/drm/i915 are not loaded (env.txt). The trigger cannot execute; the
code path is exclusively hardware-driven (device attach → DRM ioctls).

## Fix

One-line: restore the upstream BAR index in agp_i915_write_gtt
(fix.diff). Not validated on hardware for the same reason; the patched
line matches FreeBSD upstream exactly.

## References
- sys/dev/agp/intel-gtt.c:139-143 (res spec: MMADR + GTTADR)
- sys/dev/agp/intel-gtt.c:198-218 (i915 driver binds write_gtt)
- sys/dev/agp/intel-gtt.c:998-1004 (the wrong write, sc_res[0])
- sys/dev/agp/intel-gtt.c:1092-1098 (sync reads sc_res[1] — internal inconsistency)
- sys/dev/drm/i915/i915_gem_gtt.c:2703-2713, 3538-3540 (unpriv-reachable callers)
- FreeBSD sys/dev/agp/agp_i810.c agp_i915_write_gtt (upstream: sc_res[1])

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
