RVDA raw-VBT mapping: pmap_mapbios() of unvalidated firmware-supplied 64-bit address / 32-bit length, re-leaked every resume (amplifies DF-1629; panic-capable KVA exhaustion)
Summary
intel_opregion_setup maps the ASLE mailbox's rvda/rvds (up to 4GiB-1) with pmap_mapbios verbatim - no size sanity, no overflow check, no real-memory check. pmap_mapbios's only failure mode is a panic. Resume calls setup again every cycle while iounmap never frees pmap_mapbios KVA (the DF-1629 mechanism) - each suspend/resume leaks up to rvds bytes of KVA/PTEs, unbounded amplification by firmware-chosen size. Downstream safe (NULL-map rejected by is_valid_vbt). Malicious-VBIOS/SMM threat model. Bound-and-validate fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4453 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.8 KB | β raw | |
| verdict.json | β | 2.9 KB | view raw | |
| fix.diff | β | 531 B | view raw |
DF-4453 β RVDA VBT mapping: pmap_mapbios() of firmware-supplied 64-bit address / 32-bit length with no validation (i915 OpRegion)
File: sys/dev/drm/i915/intel_opregion.c (pass 2)
Root cause
intel_opregion_setup() maps the raw VBT pointer taken verbatim from the
firmware-shared ASLE mailbox:
if (opregion->header->opregion_ver >= 2 && opregion->asle &&
opregion->asle->rvda && opregion->asle->rvds) {
opregion->rvda = pmap_mapbios(opregion->asle->rvda, /* u64 phys addr :126 */
opregion->asle->rvds); /* u32 size :127 */
vbt = opregion->rvda;
vbt_size = opregion->asle->rvds;
if (intel_bios_is_valid_vbt(vbt, vbt_size)) {
(sys/dev/drm/i915/intel_opregion.c:1029-1043)
- No check that
rvda/rvdsdescribe sane memory, norvda+rvdsoverflow check, no upper bound onrvds(up to 4 GiB-1). DragonFly'spmap_mapbios()(sys/platform/pc64/x86_64/pmap.c:6180) maps any physical address with any length into KVA, write-back cached, and only fails bypanic("pmap_mapdev: Couldn't alloc kernel virtual memory")(pmap.c:6208) β i.e. a large enoughrvdsrequest that exhausts the kernel_map KVA arena becomes a kernel panic, not an error return. - The OpRegion contents (rvda/rvds) are SMM/firmware-writable shared memory.
- The mapping is re-established on every resume
(
intel_opregion_setup()called fromi915_drm_resume(),sys/dev/drm/i915/i915_drv.c:2124) whileiounmap()never releasespmap_mapbiosKVA (the DF-1629 mechanism), so each suspend/resume cycle leaks up torvdsbytes of KVA + page-table entries: an unbounded amplification of DF-1629 by firmware-chosen size. - Downstream is safe: a failed map (
rvda == NULL) is rejected byintel_bios_is_valid_vbt()'s NULL check (sys/dev/drm/i915/intel_bios.c:1655), and the VBT block walks live inintel_bios.c, not in this file.
Impact
Firmware-trust boundary issue (malicious/compromised VBIOS-SMM or a re-flashed
OpRegion): kernel KVA/PTE exhaustion (system-wide resource starvation, KVA
arena fragmentation), and in the pmap_mapdev OOM case a direct kernel panic
at boot. Not reachable from an unprivileged local user; no guest runtime test
possible (the audit QEMU guest has no Intel GPU β i915 never attaches).
Fix direction (see fix.diff)
Validate before mapping:
if (opregion->header->opregion_ver >= 2 && opregion->asle &&
opregion->asle->rvds >= sizeof(struct vbt_header) &&
opregion->asle->rvds <= VBT_MAP_MAX && /* e.g. 4 MiB */
opregion->asle->rvda &&
opregion->asle->rvda + opregion->asle->rvds > opregion->asle->rvda) {
and bail to the mailbox-#4 VBT on validation failure (upstream Linux took the same approach of bounding the rvda mapping, commit 6b4e4ba2cc02 family).
Fix verification
not_testableNo Intel GPU on the audit guest: i915 never attaches, so neither baseline nor patched behavior is exercisable. Fix is by inspection (mirrors upstream Linux practice of bounding the rvda mapping).
findings/poc/DF-4453/fix.diff (git-apply-able against sys/).
Confirmed kernel references
Detail
Evidence (decisive lines)
README.md; fix.diff. Key lines: intel_opregion.c:1029-1043 (unvalidated pmap_mapbios of rvda/rvds), pmap.c:6179-6213 (pmap_mapbios semantics, panic-only failure), i915_drv.c:2124 (re-map on every resume).
PoC changes
No PoC produced: hardware-preconditioned (Intel GPU + ACPI OpRegion firmware data), absent on the QEMU audit guest.
Verified recommended fix
Validate rvds against a sane maximum (e.g. 4 MiB) and rvda+rvds for overflow before pmap_mapbios, falling back to the mailbox-#4 VBT (see fix.diff).
Verdict
intel_opregion_setup() maps the firmware-supplied RVDA VBT pointer with pmap_mapbios(rvda, rvds) (intel_opregion.c:1029-1043) with no validation of the 64-bit physical address or the 32-bit length (up to 4 GiB-1) and no rvda+rvds overflow check. DragonFly's pmap_mapbios (pmap.c:6180) maps any physical address WB-cached into KVA and cannot fail gracefully - its only failure mode is panic('pmap_mapdev: Couldn't alloc kernel virtual memory') (pmap.c:6208). The mapping is re-established on every resume (i915_drv.c:2124) while iounmap never releases pmap_mapbios KVA (the DF-1629 mechanism), so each suspend/resume cycle leaks up to rvds bytes of KVA and PTEs - an unbounded amplification of DF-1629 by firmware-chosen size. Not runtime-testable on this audit guest: it has no Intel GPU, so the i915 driver never attaches and no OpRegion exists (the ASLS config read returns 0 -> -ENOTSUPP at intel_opregion.c:980-983). Downstream safety verified by inspection: a failed map (NULL) is rejected by intel_bios_is_valid_vbt's NULL check (intel_bios.c:1655), and the BDB block walks live in intel_bios.c, not in this file.
No comments yet.