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)
PoC verification
Evidence pack
findings/poc/DF-3628 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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_firmwaremodule parameter; BXT/GEN9_LP eDP only) - Status:
inconclusiveon 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
- BXT / Gemini Lake (IS_GEN9_LP) with eDP panel β rare but supported;
- 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_testablefix.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
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.
No comments yet.