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

i915 MMIO register mapping (pmap_mapdev KVA + global iomap node + shim bus resource) leaked on every driver detach: the only pci_iounmap in the i915 tree is #if 0'd

Summary

i915_mmio_setup() pci_iomap()s the MMIO BAR into pmap_mapdev_attr KVA plus a kmalloc'd iomap_list node plus an unreleased bus resource hold; the ONLY pci_iounmap anywhere under sys/dev/drm/i915/ is compiled out, and intel_uncore_fini() never unmaps. Each attach/detach cycle (root kldload/kldunload, or probe-fail->retry) permanently leaks all three; unbounded accumulation. DF-3768's twin for a different resource on the same unload path - the two compound on real hardware. Root-gated; slow permanent kernel KVA/resource exhaustion. Un-#if-0 fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3900 Β· 11 files
FileTypeDescriptionSize
README.md β€” 2.5 KB ↓ raw
VERDICT.md β€” 3.2 KB ↓ raw
mmio_leak_cycle.sh β€” 579 B view raw
build.sh β€” 75 B view raw
run.sh β€” 133 B view raw
build.log β€” 363 B view raw
run.log β€” 363 B view raw
env.txt β€” 332 B view raw
fix.diff β€” 510 B view raw
manifest.json β€” 805 B view raw
verdict.json β€” 2.6 KB view raw

DF-3900 β€” i915 MMIO register mapping (and shim bus resource) leaked on every driver detach

File: sys/dev/drm/i915/i915_drv.c Class: resource leak / teardown asymmetry (DF-3768 twin, distinct resource) Severity: Low (root-gated: kldload/kldunload of i915kms.ko, or error-probe β†’ re-probe)

What

i915_mmio_setup() (i915_drv.c:1027) maps the MMIO BAR:

dev_priv->regs = pci_iomap(pdev, mmio_bar, mmio_size);   /* 512KB gen<5, 2MB gen>=5 */

On DragonFly, pci_iomap() (sys/dev/drm/include/linux/pci.h:299) = pci_resource_start() + pci_resource_len() + ioremap(base, size): * pci_resource_start does bus_alloc_resource_any(..., RF_SHAREABLE) and the caller never releases that resource (shim-wide issue), and * ioremap() β†’ __ioremap_common() (sys/dev/drm/linux_iomapping.c:44) does pmap_mapdev_attr() (kernel KVA) and links a kmalloc'd struct iomap onto the global iomap_list.

The only unmap call anywhere under sys/dev/drm/i915/ is compiled out:

static void i915_mmio_cleanup(struct drm_i915_private *dev_priv)   /* :1040 */
{
    intel_teardown_mchbar(dev_priv);
#if 0
    pci_iounmap(pdev, dev_priv->regs);      /* :1048 β€” never compiled */
#endif
}

intel_uncore_fini() (intel_uncore.c:1643 β€” called on the same unload path, i915_drv.c:1867 β†’ 1105) only sanitizes/forcewake-resets; it does not unmap. i915_driver_unload() therefore never releases dev_priv->regs.

Impact

Every i915 attach/detach cycle (kldload/kldunload i915kms.ko, or a probe that fails after i915_driver_init_mmio() and is retried) permanently leaks: * 512KB / 2MB of pmap_mapdev KVA, * one struct iomap node on the global list, and * one bus-resource acquisition of the MMIO BAR.

Unbounded accumulation; on 32-bit kernels KVA exhaustion is realistic, on 64-bit it is a slow permanent leak. Root-gated (module load), no unprivileged path, no memory corruption. Same class as DF-3768 (GGTT fictitious range, i915_gem_gtt.c) but a distinct leaked resource and a distinct fix.

Reproduce (on hardware with an Intel GPU)

See mml_leak_cycle.sh β€” kldload/kldunload loop; after N cycles vmstat -m | grep drm / KVA map inspection shows N leaked iomap nodes (2MB KVA each on gen5+). Not runnable on this audit guest (virtio VGA only, no /dev/dri, no i915 device) β€” status honestly inconclusive, source-certain (rg proves the single #if 0'd pci_iounmap).

Fix

Un-#if 0 the unmap in i915_mmio_cleanup() (fix.diff) β€” iounmap(dev_priv->regs) undoes exactly what pci_iomap() did (pmap_unmapdev + node removal).

VERDICT.md
↓ download raw

DF-3900 VERDICT

Status: inconclusive (source-certain; not runnable on this guest) reproduced: 0 Β· impact class: dos (KVA/resource exhaustion, root-gated) Β· confidence: certain

Why the bug is certain from source alone

  1. i915_mmio_setup() maps the MMIO BAR per attach: dev_priv->regs = pci_iomap(pdev, mmio_bar, mmio_size) β€” i915_drv.c:1027.
  2. On DragonFly pci_iomap β†’ pci_resource_start (sys/dev/drm/include/linux/pci.h:267) does bus_alloc_resource_any(RF_SHAREABLE) β€” never released by anyone β€” and ioremap β†’ __ioremap_common (sys/dev/drm/linux_iomapping.c:44-57) does pmap_mapdev_attr() + kmalloc(struct iomap) + SLIST_INSERT_HEAD(&iomap_list).
  3. The ONLY pci_iounmap in the entire sys/dev/drm/i915/ tree is at i915_drv.c:1048, inside #if 0 (verified: rg -n "pci_iounmap" sys/dev/drm/i915/ β†’ exactly one hit, that line).
  4. The unload path calls i915_driver_cleanup_mmio() (i915_drv.c:1106 within i915_driver_cleanup_mmio = intel_uncore_fini + i915_mmio_cleanup, reached from i915_driver_unload:1867) and intel_uncore_fini (intel_uncore.c:1643-1653) never touches the mapping.

Therefore each attachβ†’detach cycle of i915 leaks 512KB (gen<5) or 2MB (genβ‰₯5) of KVA plus the iomap node plus a bus resource hold. No code path frees them. Trigger: kldload i915kms && kldunload i915mks as root, repeated; or repeated probe-failβ†’reprobe. After N cycles, NΓ—2MB KVA is gone permanently (reboot only).

Why not live-verified

The audit guest is QEMU with virtio VGA (vgapci0: chip=0x11111234 = virtio-gpu), no Intel GPU, no /dev/dri, i915kms.ko cannot attach (see env.txt). Exactly the situation DF-3634/DF-3768 hit; recorded inconclusive per the honest-status table.

Exploit chain

None β€” no memory corruption, no unprivileged reach. This is a permanent kernel resource leak enabling slow system degradation / eventual attach failures (same operational impact family as DF-3768, which exhausts the 8-slot fictitious range; this one exhausts KVA instead β€” the two compound on real hardware).

Fix validation

fix.diff un-#if 0s the pci_iounmap in i915_mmio_cleanup. Not build-tested on the guest because the i915 driver is not buildable/loadable there (no drm in the guest kernel config path used; kernel build of drm requires the module set). fix_status: not_applicable (no reproduced memory corruption; mandatory fix-validation trigger not met).

kernel_refs

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.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff un-#if 0's pci_iounmap(pdev, dev_priv->regs) in i915_mmio_cleanup; not build-validated (guest cannot build/load i915kms; no reproduced corruption to gate mandatory fix-validation)

fix.diff in this directory
↓ fix.diffper-fix-DF-3900

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (step-by-step proof); run.log (guest kldload attempt fails: no i915 hardware); mmio_leak_cycle.sh (hardware trigger); fix.diff (un-#if 0 pci_iounmap)

PoC changes

Trigger authored fresh (kldload cycle script); no seed existed

Verified recommended fix

Compile the pci_iounmap(pdev, dev_priv->regs) back into i915_mmio_cleanup() (and NULL dev_priv->regs)

Verdict

Source-certain teardown asymmetry: i915_mmio_setup() pci_iomap()s the MMIO BAR (i915_drv.c:1027) into pmap_mapdev KVA + a global iomap_list node + a shim bus-resource hold, but the only pci_iounmap in the whole i915 tree is #if 0'd out in i915_mmio_cleanup() (i915_drv.c:1042-1049) and intel_uncore_fini() (intel_uncore.c:1643) never unmaps. Every i915 attach/detach cycle (root kldload/kldunload i915kms.ko or error-probe-retry) permanently leaks 512KB/2MB KVA plus metadata; unbounded accumulation; DF-3768's twin for a different resource. Not runnable on the audit guest (virtio VGA only, no /dev/dri β€” kldload i915kms: 'No such file or directory'), so honestly inconclusive with the code path proven by rg across the tree.