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

Unvalidated VBT backlight.controller used as power-sequencer register index (out-of-range MMIO writes on BXT)

Summary

bxt_power_sequencer_idx() returns dev_priv->vbt.backlight.controller verbatim - a raw u8 copied from the VBT LFP backlight control method block with no range check. It is consumed as pps_idx in PP_CONTROL/PP_STATUS/PP_ON_DELAYS/PP_OFF_DELAYS/PP_DIVISOR whose macro computes pps_mmio_base + reg + pps_idx*0x100. Valid hardware values are 0 and 1; a corrupt/malicious VBT value up to 255 redirects PPS programming writes up to ~64KB outside the two-entry PPS block on BXT - stray MMIO writes with semi-controlled values into neighboring display registers (hang/brick-style DoS). MMIO-window misdirection, not kernel-RAM corruption. Requires BXT/GeminiLake eDP hardware plus attacker-controlled VBT (corrupted option ROM, malicious system firmware, or root via i915.vbt_firmware).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3628 Β· 4 files
FileTypeDescriptionSize
README.md β€” 2.5 KB ↓ raw
fix.diff β€” 687 B view raw
verdict.json β€” 2.7 KB view raw
manifest.json β€” 777 B view raw

DF-3628 β€” Unvalidated VBT backlight.controller used as PPS register index (OOB MMIO on BXT)

  • File: sys/dev/drm/i915/intel_dp.c:860-883 (bxt_power_sequencer_idx) + sys/dev/drm/i915/intel_bios.c:333 (unvalidated assignment)
  • Class: CWE-129 (improper validation of array index) β†’ out-of-range MMIO register selection, not kernel-RAM OOB
  • Severity: Low (requires malicious/corrupt VBT β€” BIOS compromise, or root via the i915.vbt_firmware module parameter; BXT/GEN9_LP eDP only)
  • Status: inconclusive on the audit guest (no Intel GPU; BXT hardware required). Source-level certain for the missing validation.

Data flow

VBT binary (BIOS ROM / i915.vbt_firmware=<file>, root-writable 0400 param)
  └─ intel_bios.c:333   dev_priv->vbt.backlight.controller = method->controller;
       (raw u8, i915_drv.h:1131; valid HW values are 0 and 1 β€” never range-checked)
  └─ intel_dp.c:861-872  bxt_power_sequencer_idx() returns it unchanged
  └─ intel_dp.c:1014-1019 intel_pps_get_registers():
         pps_idx = bxt_power_sequencer_idx(intel_dp);
         regs->pp_ctrl = PP_CONTROL(pps_idx);
  └─ i915_reg.h:4613  _MMIO_PPS(pps_idx, reg) =
         _MMIO(dev_priv->pps_mmio_base - PPS_BASE + reg + pps_idx * 0x100)

With a VBT-supplied controller of e.g. 255, every PPS register access for that connector lands at pps_mmio_base + 0xFF00 β€” up to ~64 KB outside the 2-entry PPS register block β€” and intel_dp_init_panel_power_sequencer_ registers() then writes panel-power sequencing values there (intel_dp.c:6013-6019). On BXT pps_mmio_base = PCH_PPS_BASE (0xC7200, intel_display.c:14270), so the stray writes land in neighboring display-block MMIO space. Result: arbitrary-neighbor MMIO writes with semi-controlled values β†’ display engine misprogramming, machine hang/brick-style DoS. Not a kernel memory-safety violation (MMIO window), hence Low.

Preconditions

  1. BXT / Gemini Lake (IS_GEN9_LP) with eDP panel β€” rare but supported;
  2. attacker-controlled VBT: corrupted BIOS option ROM, malicious firmware, or a root user loading one via i915.vbt_firmware (i915_params.c:76 β€” root can already do worse, which caps severity at Low/hardening).

Fix

Clamp the index at the consumer (see fix.diff): backlight_controller = min(backlight_controller, 1); β€” matches the two hardware PPS units and keeps a corrupt VBT from redirecting MMIO.

Runtime verification

Guest is virtio-only (vgapci0 chip=0x1111:1234); i915 never attaches, BXT eDP unreachable. inconclusive per honest-status policy.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff clamps the VBT controller to the two valid PPS indices at the intel_dp.c consumer; not build-validated on this guest (needs BXT hardware + crafted VBT).

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

Confirmed kernel references

Detail

Evidence (decisive lines)

README.md (full data-flow chain with citations); fix.diff (clamp at consumer). Decisive lines: intel_dp.c:864 (raw return), 1015 (pps_idx use), i915_reg.h:4613 (pps_idx*0x100 arithmetic), intel_bios.c:333 (unvalidated copy).

PoC changes

No seed PoC; firmware-trust-boundary finding, hardware-gated.

Verified recommended fix

Clamp vbt.backlight.controller to 0..1 in bxt_power_sequencer_idx() (or validate it at VBT parse time in intel_bios.c).

Verdict

Source-level confirmed missing validation, runtime inconclusive: bxt_power_sequencer_idx() (sys/dev/drm/i915/intel_dp.c:860-883) returns dev_priv->vbt.backlight.controller verbatim; the field is a raw u8 copied from the VBT LFP backlight control method block (intel_bios.c:333, decl i915_drv.h:1131) with no range check, and is used directly as the pps_idx argument of PP_CONTROL/PP_STATUS/PP_ON_DELAYS/PP_OFF_DELAYS/PP_DIVISOR whose macro expands to pps_mmio_base + reg + pps_idx*0x100 (i915_reg.h:4613-4618). A VBT value >1 (up to 255) makes intel_dp_init_panel_power_sequencer_registers() write panel-sequencing values up to ~64KB beyond the PPS block on BXT (pps_mmio_base=PCH_PPS_BASE 0xC7200 per intel_display.c:14270). MMIO-window misdirection (HW register corruption/DoS), not kernel-RAM corruption. Requires BXT eDP hardware plus a corrupt/malicious VBT (BIOS compromise or root via i915.vbt_firmware, 0400 param), hence Low severity. Guest is virtio-only so no runtime test was possible; i915.ko in /boot/kernel carries the code.