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

vlv_dsi_init dereferences vbt.dsi.config without a NULL/dangling guard β€” boot-time NULL-page panic or UAF read of freed config

Summary

vlv_dsi_init decides DSI presence from the VBT child-device list but then dereferences dev_priv->vbt.dsi.config with no guard. dsi.config is only set when the separate BDB_MIPI_CONFIG section exists and both kmemdups in parse_mipi_config succeed. Missing section or config-kmemdup OOM => NULL-page deref panic at i915 attach; pps-kmemdup OOM => the producer kfrees config WITHOUT NULLing it => vlv_dsi_init reads freed memory and intel_dsi_vbt_init then parses the entire freed struct. Firmware/OOM-gated. Consumer-guard + producer-NULL fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4205 Β· 5 files
FileTypeDescriptionSize
README.md β€” 2.9 KB ↓ raw
VERDICT.md β€” 2.8 KB ↓ raw
verdict.json β€” 2.8 KB view raw
fix.diff β€” 918 B view raw
manifest.json β€” 962 B view raw

DF-4205 β€” vlv_dsi_init dereferences vbt.dsi.config without NULL/dangling guard

File: sys/dev/drm/i915/vlv_dsi.c (pass 2) Severity: Low | Confidence: certain | CWE-476 (NULL pointer dereference) / CWE-457 (use of uninitialized/freed pointer variant)

Build / Run

No build or run: Phase V skipped per contract (Low severity, firmware-gated trigger). The audit QEMU guest has no Intel GPU, so i915 never attaches and vlv_dsi_init is not reachable there; the trigger additionally requires a VBT whose child-device list declares a MIPI output while the BDB_MIPI_CONFIG block is absent (or an OOM at a specific kmemdup).

Static repro of the path is fully cited below β€” the defect is provable from source alone.

Expected behavior if triggered (on real VLV/CHV/BXT hardware)

  • Missing BDB_MIPI_CONFIG + MIPI child device present: kernel NULL-page deref panic at driver attach (config->dual_link, vlv_dsi.c:1797).
  • OOM in the pps kmemdup after config kmemdup succeeded: 1-byte UAF read at vlv_dsi.c:1797, then full parse of freed memory in intel_dsi_vbt_init (intel_dsi_vbt.c:523-544) and ->pwm_blc read at vlv_dsi.c:1826.

The path (path:line)

  1. vlv_dsi_init() vlv_dsi.c:1747 β€” intel_bios_is_dsi_present(dev_priv, &port) returns true based on the child device list (intel_bios.c:2034-2047: any child with DEVICE_TYPE_MIPI_OUTPUT and dvo_port in {DVO_PORT_MIPIA, DVO_PORT_MIPIC}).
  2. dev_priv->vbt.dsi.config is populated by a different BDB section: parse_mipi_config() intel_bios.c:826-830 β€” if find_section(bdb, BDB_MIPI_CONFIG) fails it returns with dsi.config == NULL.
  3. intel_bios.c:843-851 β€” dsi.config = kmemdup(...); on pps kmemdup failure (line 848-851) it does kfree(dev_priv->vbt.dsi.config) without setting it to NULL β†’ dangling.
  4. Back in vlv_dsi_init: vlv_dsi.c:1797 dev_priv->vbt.dsi.config->dual_link and vlv_dsi.c:1826 dev_priv->vbt.dsi.config->pwm_blc β€” dereferenced with no guard.

Nothing between intel_bios_init and vlv_dsi_init re-checks dsi.config or dsi.panel_id (which stays MIPI_DSI_UNDEFINED_PANEL_ID when the section is missing β€” intel_bios.c:816,855).

Distinct from known findings

  • DF-1390: OOB indexing inside parse_mipi_config (config[panel_type]).
  • DF-1773: NULL-deref of vbt.lfp_lvds_vbt_mode in intel_dsi_vbt_init (different pointer, later in the call chain β€” it never runs because vlv_dsi.c:1797 faults first).
  • DF-4122: block-size floors in other parse_* consumers.

Fix

Guard at the consumer (this file) + NULL the dangling pointer at the producer β€” see fix.diff.

Threat model / impact ceiling

Attacker needs to control the Intel VBT (BIOS/OpROM flash = root) or hit a narrow OOM. Impact: kernel panic at driver attach (local DoS of the boot / driver load), not arbitrary code execution. Rooted-firmware supply chain or broken BIOS is the realistic scenario.

VERDICT.md
↓ download raw

DF-4205 VERDICT

Status: untested (Phase V skipped β€” Low severity, firmware/OOM-gated, no Intel GPU on the audit guest). Bug existence: certain, proven from source.

Root cause

vlv_dsi_init() decides DSI is present from the VBT child device list (intel_bios_is_dsi_present, intel_bios.c:2034-2047), but then dereferences dev_priv->vbt.dsi.config (vlv_dsi.c:1797 ->dual_link, :1826 ->pwm_blc) which is only populated when a different BDB section (BDB_MIPI_CONFIG) exists AND both kmemdups succeed (intel_bios.c:826-856). The two presence signals are decoupled; there is no guard at the consumer.

Three concrete failure inputs:

  1. NULL deref (panic) β€” VBT has a MIPI child device but no BDB_MIPI_CONFIG section: find_section() returns NULL (intel_bios.c:826-830), dsi.config stays NULL, vlv_dsi_init runs to :1797 and faults on the NULL page. Kernel panic at i915 attach.
  2. NULL deref (OOM) β€” kmemdup(config) fails (intel_bios.c:843-845): config stays NULL, same fault at :1797.
  3. UAF read β€” kmemdup(pps) fails after config succeeded (intel_bios.c:847-851): kfree(dsi.config) leaves the pointer dangling; vlv_dsi.c:1797 reads dual_link from freed memory (1 byte), then intel_dsi_vbt_init() parses the entire freed struct mipi_config (intel_dsi_vbt.c:506,523-544) and vlv_dsi.c:1826 reads pwm_blc.

Why not verified on the guest

  • The audit QEMU guest exposes no Intel GPU; i915 does not attach, so vlv_dsi_init is unreachable by construction (not_testable conditions).
  • Even on hardware, the trigger is firmware content (VBT) or a specific OOM, neither of which an unprivileged guest user controls.
  • Severity Low β†’ contract exempts from Phase V.

Impact ceiling

Boot/attach-time kernel panic (DoS) in case 1-2; a UAF read of a 484-byte driver-allocated struct in case 3 (no attacker-controlled content is written through it before free β€” values are copied into intel_dsi fields and programmed to display registers; worst case is garbage register programming after the freed block is reallocated, still no write primitive from userland). No privilege escalation path: the trigger is not user-reachable.

Fix

fix.diff: (a) bail to the (correct, pre-connector) err label when vbt.dsi.config is NULL in vlv_dsi_init; (b) NULL the pointer after the pps-failure kfree in intel_bios.c so no dangling pointer survives. Both hunks are required; neither alone closes case 3.

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

No kernel-build fix validation performed: Low-severity firmware/OOM-gated finding, guest lacks Intel GPU; the two-hunk guard in fix.diff is trivially auditable by inspection.

fix.diff (consumer guard + producer NULL-out); not built or applied to sys/.
↓ fix.diffper-fix-DF-4205

Confirmed kernel references

Detail

Evidence (decisive lines)

README.md and VERDICT.md in this pack; decisive lines: vlv_dsi.c:1747 (presence check), vlv_dsi.c:1797+1826 (unguarded deref), intel_bios.c:826-830 (config left NULL), intel_bios.c:848-851 (kfree without NULL), intel_bios.c:2034-2047 (decoupled presence signal), intel_dsi_vbt.c:506+523-544 (later full parse of the same pointer).

PoC changes

No PoC authored: trigger requires a VBT with MIPI child device but no BDB_MIPI_CONFIG block (or a forced kmemdup failure) on real hardware; the audit guest has no Intel GPU. Static proof of path only.

Verified recommended fix

Bail to the pre-connector err label when vbt.dsi.config is NULL in vlv_dsi_init, and NULL the pointer after the pps-failure kfree in parse_mipi_config (fix.diff).

Verdict

Bug existence certain from source trace: vlv_dsi_init dereferences vbt.dsi.config (vlv_dsi.c:1797 ->dual_link, :1826 ->pwm_blc) with no NULL guard, while the pointer is only populated when BDB_MIPI_CONFIG exists and both kmemdups in parse_mipi_config succeed (intel_bios.c:826-856); presence is decided by the separate child-device list (intel_bios.c:2034-2047). Missing section or config-kmemdup OOM => NULL-page panic at attach; pps-kmemdup OOM => dangling config (kfree without NULL, intel_bios.c:848-851) => UAF read of freed struct. Not verified on the guest: no Intel GPU on the audit VM and the trigger is VBT-firmware/OOM-gated, so Phase V is skipped per contract (Low severity). Impact ceiling is a boot/attach panic or a bounded UAF read of driver memory; no userland-reachable write primitive.