# DF-5136 — vbt_sdvo_panel_type is used as an unvalidated array index into # the VBT SDVO panel-timing table (root-set OOB read) `sys/dev/drm/i915/i915_params.c:68-71` registers `vbt_sdvo_panel_type` as a fully user-of-loader-controlled int tunable (`TUNABLE_INT("drm.i915.vbt_sdvo_panel_type", ...)` plus the macro-generated `KBUILD_MODNAME.vbt_sdvo_panel_type` name), documented as "-2=ignore, -1=auto [default], index in VBT BIOS table" — but neither the param file nor the consumer range-checks the value. The consumer, `parse_sdvo_panel_data()` (sys/dev/drm/i915/intel_bios.c:349-387): ```c index = i915_modparams.vbt_sdvo_panel_type; /* :357 */ if (index == -2) return; /* :358 */ if (index == -1) index = sdvo_lvds_options->panel_type; /* VBT 4-bit field */ dvo_timing = find_section(bdb, BDB_SDVO_PANEL_DTDS); if (!dvo_timing) return; ... fill_detail_timing_data(panel_fixed_mode, dvo_timing + index); /* :381 */ ``` Any value other than exactly -1/-2 (e.g. -3, or 100000, or INT_MAX) is used directly as a pointer offset into the mapped/copied VBT blob: `dvo_timing + index` with `index` an `int` scaled by sizeof(struct lvds_dvo_timing) — an out-of-bounds read of arbitrary offset from the section base (negative indexes read before the section). The garbage `lvvs_dvo_timing` is then copied into `panel_fixed_mode` and consumed as a display mode by the LVDS/SDVO paths (and later `vbt.panel_type`-driven consumers use `panel_type * 4` shifts on VBT data). A huge index faults the mapping → kernel panic; a moderate index yields OOB kernel heap/BIOS-memory read that flows into mode computation and DRM debug logs. Preconditions: root sets `drm.i915.vbt_sdvo_panel_type=` (or the mangled `KBUILD_MODNAME.vbt_sdvo_panel_type`) in loader.conf/kenv before i915 attaches, on a machine whose VBT actually has a BDB_SDVO_PANEL_DTDS section (old SDVO/LVDS platforms). No runtime sysctl exists for this param on this port, so the trigger is boot/loader-time and root-only — hence severity Low (input-validation gap / hardening), reported because the identical gap was deemed worth fixing upstream (Linux later clamps `panel_type`). ## Status Not executed on the audit guest: the QEMU std-VGA machine has no Intel GPU, no OpRegion/VBT, and i915 never attaches, so `parse_sdvo_panel_data()` cannot be reached there (`intel_bios_init` only runs on real hardware). The defect is established by source: the value flows from the tunable to `dvo_timing + index` with no bound other than the two exact magic values. ## Fix Clamp after the -2/-1 handling: ```diff --- sys/dev/drm/i915/intel_bios.c +++ sys/dev/drm/i915/intel_bios.c @@ -371,6 +371,10 @@ parse_sdvo_panel_data(struct drm_i915_private *dev_priv, index = sdvo_lvds_options->panel_type; } + if (index < 0 || index > 0xf) { + DRM_DEBUG_KMS("Invalid VBT SDVO panel type %d\n", index); + return; + } + dvo_timing = find_section(bdb, BDB_SDVO_PANEL_DTDS); ``` (panel_type is a 4-bit field per the VBT spec; the BDB_SDVO_PANEL_DTDS table it indexes holds 16 entries on every generation that ships it.)