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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4453 Β· 3 files
FileTypeDescriptionSize
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/rvds describe sane memory, no rvda+rvds overflow check, no upper bound on rvds (up to 4 GiB-1). DragonFly's pmap_mapbios() (sys/platform/pc64/x86_64/pmap.c:6180) maps any physical address with any length into KVA, write-back cached, and only fails by panic("pmap_mapdev: Couldn't alloc kernel virtual memory") (pmap.c:6208) β€” i.e. a large enough rvds request 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 from i915_drm_resume(), sys/dev/drm/i915/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 + page-table entries: an unbounded amplification of DF-1629 by firmware-chosen size.
  • Downstream is safe: a failed map (rvda == NULL) is rejected by intel_bios_is_valid_vbt()'s NULL check (sys/dev/drm/i915/intel_bios.c:1655), and the VBT block walks live in intel_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_testable
baseline no→ patch + rebuild →patched clean

No 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/).
↓ fix.diffper-fix-DF-4453

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.