DragonFly devm_request_mem_region shim is conflict-blind: i915_adjust_stolen's stolen-region safety check is dead code (+ static-rman double-init/leak/off-by-one)
Summary
The DF shim reserves stolen in a private static rman no other driver consults and rman_manage_region does no overlap check, so the 'something is seriously wrong' -EBUSY path can never fire; combined with DF never carving stolen out of phys_avail, a BIOS leaving stolen inside e820-RAM yields silent kernel-page/GPU-DMA double ownership. Shim internals also buggy: second call re-inits the live rman (duplicate rman_head insert + leaked lock), rm_end off-by-one, res never freed. Firmware-gated; repair-the-shim fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4678 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.9 KB | β raw | |
| VERDICT.md | β | 3.3 KB | β raw | |
| fix.diff | β | 1.2 KB | view raw | |
| env.txt | β | 977 B | view raw | |
| manifest.json | β | 809 B | view raw | |
| verdict.json | β | 5.5 KB | view raw |
DF-4678 β DragonFly devm_request_mem_region shim makes i915's stolen-region
conflict detection vacuous (+ static-rman/leak/off-by-one defects)
sys/dev/drm/i915/i915_gem_stolen.c:83-104 (the #ifdef __DragonFly__
shim) versus the conflict check at :156-188.
No runnable PoC on this guest (no Intel GPU β the shim cannot execute);
this pack is the analysis record with line-accurate citations against the
guest's own /usr/src copy (md5-verified, env.txt). verdict.json records
status untested / impact none accordingly.
What the shim does (and doesn't)
static struct linux_resource * devm_request_mem_region(struct device *dev,
resource_size_t start, resource_size_t n, const char *name)
{
static struct rman stolen_rman;
...
if (rman_init(&stolen_rman, -1)) /* private, static rman */
return NULL;
if (rman_manage_region(&stolen_rman, ...))
return NULL;
res = kmalloc(sizeof(*res), M_DRM, GFP_KERNEL);
return res; /* always non-NULL on success */
}
Upstream, devm_request_mem_region() fails (NULL) when the range conflicts
with another driver's reservation β the property i915_adjust_stolen()
depends on: "if the region is already marked as busy, something is seriously
wrong" β -EBUSY β stolen disabled (:182-187). On DragonFly:
- The check can never fire. The shim's private static rman is consulted
by nobody;
rman_manage_region()performs no overlap check at all (subr_rman.c:123-150, "NB: this interface is not robust ... multiple copies"). The-EBUSYpath is dead code; i915 proceeds with stolen on any BIOS. - DF also never reserves stolen from phys_avail β the
phys_avail_reserve(intel_graphics_stolen_base, ...)call in sys/bus/pci/x86_64/pci_early_quirks.c:310-312 is commented out ("XXX: enable this once the KPI is available"). On firmware that leaves the stolen range inside e820-RAM, the kernel page allocator and the i915 stolen allocator can own the same physical pages β silent GPU-DMA/kernel cross-corruption β the exact scenario the (now vacuous) check exists to catch. - Latent shim defects:
rm_end = start + nmanages one page too many (rman ranges are inclusive); a second call re-runsrman_init()on the live static rman β duplicate insert into the globalrman_head+ leakedrm_slock(subr_rman.c:87-116); the kmalloc'dresis never freed; and the retry at :175-177 passesresource_size(dsm) - 2, which underflows for a 1-page dsm.
Fix
fix.diff repairs the shim internals (one-time init, inclusive end,
wrapped/empty-range refusal). True conflict detection needs kernel KPI the
shim doesn't have β the durable fix is the early-boot phys_avail reservation
(pci_early_quirks.c, cross-referenced). git apply --check clean; not
applicable to run on this guest (fix_status: not_testable).
Related but separate: DF-1712 (unzeroed stolen fbdev, pass 1) and DF-4679 (reserved-base u64 wrap, same file).
DF-4678 β VERDICT
Status: UNTESTED on the guest (no Intel GPU; the shim cannot execute) Β· impact: none directly demonstrable / silent kernel-memory corruption on affected firmware Β· confidence: certain (code facts) Β· fix: not_testable (git-apply clean, hardware-gated)
Facts (all line-verified against the guest's /usr/src, md5-matched)
i915_adjust_stolen()(i915_gem_stolen.c:106-191) usesdevm_request_mem_region()at :162 as its only "is this physical range really free" gate, with the comment "if the region is already marked as busy, something is seriously wrong" and the-EBUSYbail at :182-187 (GEN3 excepted).- On DragonFly that call resolves to the local shim at :83-104: it creates a
private static
struct rman,rman_init+rman_manage_region, and returns a kmalloc'd token. It cannot observe any other driver's reservations, andrman_manage_region()itself does no overlap checking (sys/kern/subr_rman.c:123-150 β the source even warns about multiple copies). Result: the function returns non-NULL except on ENOMEM; the conflict check upstream relies on is structurally vacuous on DF. - Compounding: DF reads the stolen base/size at early boot
(sys/bus/pci/x86_64/pci_early_quirks.c:271-306) but the
phys_avail_reserve()that would carve it out of the kernel's physical memory is commented out at :310-312 ("XXX: enable this once the KPI is available"). So on a BIOS that leaves stolen inside e820-RAM, nothing β neither phys_avail nor the shim β prevents the page allocator from handing out pages that i915 simultaneously programs the GPU to DMA over. That is silent kernel-memory corruption, configuration-gated (no unprivileged trigger; pass 1 marked pci_early_quirks.c clean, so this is recorded as cross-reference context from this file's side). - Shim-internal defects (latent, hardening):
stolen_rman.rm_end = start + nmanages one page beyond the region (rman end is inclusive); calling the shim twice re-runsrman_init()on the live static β duplicaterman_headentry + leaked previousrm_slock(subr_rman.c:87-116);res(kmalloc) is never freed; the BIOS off-by-one retry at :175-177 underflows whenresource_size(dsm) < 2.
Why not reproduced / not_testable
The guest has no Intel graphics device (/dev/dri absent; i915 never
attaches), and the shim is pure kernel KPI (rman) β no faithful userspace
emulation exists. The security claim rests on the (certain) source facts
above plus the upstream semantics of devm_request_mem_region the driver
code itself documents.
Fix
fix.diff (git-apply clean): single guarded rman_init, inclusive end
bounds, refusal of n == 0 / wrapped ranges, keeps the kmalloc token
behavior. This repairs items 4 but cannot manufacture item 1's missing
kernel-wide conflict database β the durable fix belongs in
pci_early_quirks.c (phys_avail reservation). Recommend filing that separately
upstream of this driver.
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_testablefix.diff repairs the shim's internal defects (single rman_init guarded by a static flag, inclusive rm_end, refusal of n==0 or wrapped ranges) and is git-apply-able against the pristine tree, but the fix cannot be exercised on this guest: the i915 device never attaches under QEMU and the shim is kernel-KPI-dependent (rman), so no userspace harness can faithfully run it. fix_status=not_testable for lack of hardware, not for doubt in the change; a full validation needs any physical G4X+/ILK/BDW+ Intel-gfx DragonFly machine.
fix.diff in pack (git apply --check clean against sys/); source citations above.
Confirmed kernel references
Detail
Exploit chain
buggy BIOS reports stolen inside e820-RAM -> DF never reserves it from phys_avail (pci_early_quirks.c:310-312 XXX) AND i915_adjust_stolen's devm_request_mem_region cannot detect the conflict (shim always succeeds) -> kernel page allocator and i915 stolen objects own the same physical pages -> GPU DMA over live kernel memory. No unprivileged trigger; no uid0 route; not demonstrable on this guest (no i915 hardware).
Evidence (decisive lines)
Source citations in VERDICT.md: i915_gem_stolen.c:83-104 (shim), :156-188 (dead conflict path), :175-177 (retry with resource_size(dsm)-2 underflow when size<2); subr_rman.c:87-150 (no overlap check, inclusive ranges, rman_head re-insert); pci_early_quirks.c:56,303-312 (stolen detection + commented-out reservation); env.txt (guest /usr/src md5 match).
PoC changes
No PoC runnable: guest lacks Intel graphics. Analysis-only pack; code path cited line-by-line against the guest's own /usr/src (md5-verified).
Verified recommended fix
Repair the shim (one-time rman init, inclusive bounds, wrapped-range refusal) and, upstream of it, reserve the stolen range from phys_avail at early boot so the page allocator can never hand out stolen pages (fix.diff + pci_early_quirks.c cross-ref).
Verdict
NOT TESTED ON THE GUEST (status untested): this QEMU guest has no Intel graphics device, so the i915 attach path and the DragonFly devm_request_mem_region shim (i915_gem_stolen.c:83-104) cannot execute there; the finding is a source-audit fact verified line-by-line against the guest's /usr/src copy (md5 match, see env.txt). FACTS (certain): (1) the shim's private static 'struct rman stolen_rman' is invisible to every other driver and to the kernel's physical-memory bookkeeping, and rman_manage_region (subr_rman.c:123-150) performs no overlap check ('NB: this interface is not robust ... multiple copies') -- therefore i915_adjust_stolen's conflict detection (i915_gem_stolen.c:156-188), whose entire purpose is to refuse stolen use ('something is seriously wrong') when the range is already owned, is VACUOUS on DragonFly: devm_request_mem_region returns non-NULL unconditionally except on ENOMEM, the -EBUSY path at :182-187 is dead code, and the GEN3 'apparently this works' carve-out is meaningless. (2) DF also does not carve stolen out of phys_avail at early boot (the phys_avail_reserve() call in sys/bus/pci/x86_64/pci_early_quirks.c:310-312 is commented out 'XXX: enable this once the KPI is available'), so on firmware that leaves the stolen range inside e820-RAM, nothing prevents the kernel page allocator and the i915 stolen allocator from owning the same physical pages -- silent cross-corruption of kernel memory by GPU DMA (the exact double-ownership scenario the removed check exists to catch; upstream Linux guards it with e820 reservation + the real devm_request_mem_region). (3) Latent shim defects: rm_end = start + n manages one extra page (rman ranges are inclusive, subr_rman.c convention); a second call re-runs rman_init on the live static rman, re-inserting it into the global rman_head and leaking the old rm_slock (subr_rman.c:87-116); the kmalloc'd res is never freed. SECURITY IMPACT: config/firmware-gated (needs a BIOS that leaves stolen inside RAM -- no unprivileged trigger); consequence is silent kernel-memory corruption on such machines, so impact is framed as none-from-unprivileged / corruption-on-affected-firmware. fix.diff repairs the shim internals (one-time rman init, inclusive end, wrapped/empty-range refusal) but honest framing: true conflict detection requires kernel KPI the shim does not have; the durable fix is reserving stolen from phys_avail at early boot (pci_early_quirks.c) -- cross-referenced, out of this file's scope.
No comments yet.