Uninitialized stack array consumed as eDP link rates when DP_SUPPORTED_LINK_RATES DPCD read fails
Summary
intel_edp_init_dpcd() ignores the return value of drm_dp_dpcd_read() into __le16 sink_rates[8]; on a failed/partial read (sink-controlled) the uninitialized stack contents are le16-decoded, multiplied and stored into intel_dp->sink_rates[] with num_sink_rates up to 8. Writes are bounded and values are filtered by intersection with kernel-known source rates, so no OOB and no userland disclosure - but attacker-triggerable uninitialized kernel memory is used as control data (nondeterministic link-rate selection, spurious training failure), and the uninit read is UB under sanitizers. A flaky or hostile eDP panel whose DPCD read of the supported-rates register times out after advertising edp_dpcd[0] >= DP_EDP_14. Fix: memset on short read.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3627 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.5 KB | β raw | |
| fix.diff | β | 758 B | view raw | |
| verdict.json | β | 2.3 KB | view raw | |
| manifest.json | β | 686 B | view raw |
DF-3627 β Uninitialized stack array consumed as eDP link rates when DPCD read fails
- File:
sys/dev/drm/i915/intel_dp.c:3947-3969(intel_edp_init_dpcd) - Class: CWE-457 (use of uninitialized memory), sink-triggered
- Severity: Low (no OOB, no userland leak; garbage control data only)
- Status:
inconclusiveon the audit guest (virtio-only; eDP path needs an internal panel on Intel hardware). Source-level certain.
What happens
if (intel_dp->edp_dpcd[0] >= DP_EDP_14) {
__le16 sink_rates[DP_MAX_SUPPORTED_RATES]; /* 8 x u16, UNINITIALIZED */
int i;
drm_dp_dpcd_read(&intel_dp->aux, DP_SUPPORTED_LINK_RATES,
sink_rates, sizeof(sink_rates)); /* return value IGNORED */
for (i = 0; i < ARRAY_SIZE(sink_rates); i++) {
int val = le16_to_cpu(sink_rates[i]);
if (val == 0)
break;
intel_dp->sink_rates[i] = (val * 200) / 10;
}
intel_dp->num_sink_rates = i;
}
intel_edp_init_dpcd() runs at eDP connector init / detect. edp_dpcd[0] >=
DP_EDP_14 is itself sink-supplied. If the AUX read of
DP_SUPPORTED_LINK_RATES fails (short read, timeout, defer β a misbehaving
or malicious panel controls this), the stack array is left untouched and its
garbage contents are interpreted as link rates:
- garbage flows into
intel_dp->sink_rates[0..7]andnum_sink_rates(β€ 8, writes bounded byDP_MAX_SUPPORTED_RATESβ no OOB write); intel_dp_set_common_rates()intersects them with source rates (intersect_rates, bounded,WARN_ON(k >= DP_MAX_SUPPORTED_RATES)) so only values that exactly match a known-good source rate survive; otherwisenum_common_rates == 0falls back to 162000 (intel_dp.c:536-539).
So the practical effect is uninitialized kernel stack used as control
data: nondeterministic link-rate selection / spurious training failures,
plus MSan-detectable UB. No information disclosure path exists β the values
are only ever compared against kernel constants
(intel_dp_print_rates logs only common rates, which are source constants).
The same unchecked read exists in upstream Linux of this vintage (inherited).
Fix
Check the read result and zero-initialize the array (see fix.diff).
Runtime verification
Not possible on this guest: the eDP/intel_edp_init_dpcd path requires Intel
display hardware with an internal panel; the guest's GPU is virtio
(chip=0x1111:1234) and i915 never attaches. Marked inconclusive per the
honest-status policy; the code ships in /boot/kernel/i915.ko.
Fix verification
not_testablefix.diff zeroes sink_rates when the DPCD read does not return the full 16 bytes; not build-validated on this guest (target path needs Intel eDP hardware).
findings/poc/DF-3627/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
README.md (code excerpt + data-flow + bounds analysis); fix.diff (memset-on-failed-read). Decisive lines: intel_dp.c:3949 (uninit decl), 3952-3953 (unchecked read), 3955-3969 (garbage consumed).
PoC changes
No seed PoC; pure source-analysis finding with a hardening fix.
Verified recommended fix
Treat a failed DP_SUPPORTED_LINK_RATES read as 'no rates' (memset/zero) in intel_edp_init_dpcd.
Verdict
Source-level certain, runtime inconclusive: intel_edp_init_dpcd() (sys/dev/drm/i915/intel_dp.c:3948-3969) ignores the return value of drm_dp_dpcd_read() into the uninitialized __le16 sink_rates[8] stack array; on read failure (sink-controlled: a flaky/hostile eDP panel) garbage stack data is converted into intel_dp->sink_rates[] / num_sink_rates control data. Writes are bounded by DP_MAX_SUPPORTED_RATES (no OOB) and values are subsequently filtered by intersection with kernel-known source rates (no userspace disclosure), so the impact ceiling is nondeterministic link-rate selection and spurious training failures plus MSan-visible UB. Not runnable on this guest: the eDP path requires Intel display hardware with an internal panel; guest GPU is virtio (0x1111:1234) and i915 never attaches. Code ships in /boot/kernel/i915.ko.
No comments yet.