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

Unsigned underflow in agp_i915_get_stolen_size: sc->stolen wraps to ~0x3FFFFFFF entries when BIOS stolen size < GTT size, permanently breaking all GTT binds

Summary

sc->stolen = (stolen - gtt_size)*1024/4096 computed unsigned with no guard. Pineview with a G4X-encoded GMS yields stolen=0 against gtt_size=4KB; a 965 with GMS=1M against a 2MB PGTBL. Both wrap to 0x3FFFFFFF entries, making bind/unbind reject every GTT index with EINVAL - graphics permanently dead from attach (availability only; the value only widens the refused prefix). Guard-the-subtraction fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4256 Β· 6 files
FileTypeDescriptionSize
trigger.sh β€” 538 B view raw
env.txt β€” 1007 B view raw
VERDICT.md β€” 2.2 KB ↓ raw
fix.diff β€” 530 B view raw
manifest.json β€” 748 B view raw
verdict.json β€” 2.0 KB view raw
VERDICT.md
↓ download raw

DF-4256 VERDICT β€” sc->stolen unsigned underflow in agp_i915_get_stolen_size

Status: inconclusive (hardware-gated; no Intel GTT device on the audit guest). Arithmetic certain at source level.

Reproduction attempt

Checked the audit guest for any device handled by this driver: pciconf -l | grep 8086.*(2582|258a|2592|2772|27a2|27ae|...) β€” none; agp module not loaded (env.txt). The underflow executes only during agp_i810_attach β†’ get_stolen_size (intel-gtt.c:867) on real 915-class hardware with firmware that programs GMS smaller than the page-table size. The DragonFly kernel has no other caller.

Analysis (why it is a real bug)

(stolen - gtt_size) is computed in unsigned int (both are unsigned int, intel-gtt.c:566) with no guard at intel-gtt.c:698:

  • Pineview path: for CHIP_IGD the G4X stolen encodings (0xa0/0xb0/0xc0/0xd0) set stolen = 0 (intel-gtt.c:682-688) while gtt_size is 0+4 KB (intel-gtt.c:622-625, 696) β†’ (0u - 4u) * 1024 / 4096 == 0x3FFFFFFF.
  • 965 path: GMS=1M + PGTBL=2MB β†’ (1024u - 2052u) wraps the same way.

sc->stolen is then only consumed as a lower bound to refuse:

  • intel-gtt.c:1065-1067 if (sc->stolen != 0 && index < sc->stolen) return (EINVAL);
  • intel-gtt.c:1083-1085 same in unbind.

So the corrupted value cannot widen writes; it locks out the GTT entirely (graphics permanently broken β€” availability only), and the wrong stolen_size propagates to i915 via agp_intel_gtt_get (intel-gtt.c:1445) for stolen-range accounting.

Severity Low (needs specific/faulty firmware; impact is loss of graphics function, not memory unsafety). Confidence: certain for the arithmetic, speculative for the trigger precondition (well-formed BIOS avoids it).

Fix

Validate stolen >= gtt_size before the subtraction and fail attach (or clamp sc->stolen to 0) β€” see fix.diff.

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 adds the guard; hardware-gated, cannot be exercised on this guest

findings/poc/DF-4256/fix.diff
↓ fix.diffper-fix-DF-4256

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (wrap cases + consumer analysis); env.txt (hardware absent)

PoC changes

no runnable PoC exists without affected firmware; trigger.sh only observes the accounting

Verified recommended fix

Validate stolen >= gtt_size before the subtraction (fail attach or clamp sc->stolen to 0)

Verdict

agp_i915_get_stolen_size computes sc->stolen = (stolen - gtt_size) * 1024 / 4096 in unsigned int with no guard (sys/dev/agp/intel-gtt.c:698). For Pineview with a G4X-encoded GMS value (0xa0-0xd0, where the chiptype check at 661-688 sets stolen=0) or a 965 with GMS=1M and a 2MB PGTBL, the subtraction wraps and sc->stolen becomes 0x3fffffff entries; agp_i810_bind_page/unbind_page (1065-1067/1083-1085) then reject every index ('bind into stolen memory') β€” permanent graphics denial of service from attach on, plus a wrong stolen_size exported to i915. Not memory-unsafe (the wrapped value only widens the refused prefix). Not executable on the audit guest (no Intel GTT hardware, env.txt); arithmetic verified at source level; trigger precondition requires mis-programmed firmware.