Unvalidated DisplayPort lane_count from malicious sink causes stack buffer overflow in link-training helpers
Summary
dp_test_send_link_training at dc_link_dp.c:1772-1787: reads raw byte from DPCD DP_TEST_LANE_COUNT(0x220) into link_settings.lane_count (no check vs LANE_COUNT_DP_MAX=4). retrieve_link_cap :2405 reads MAX_LANE_COUNT 5-bit field (0-31). Value propagated to dpcd_set_lt_pattern_and_lane_settings :168: dpcd_lane[4] and dpcd_lt_buffer[5]. Loop :194 for(lane=0;lane<lane_count) writes dpcd_lane[lane] -> OOB for lane_count>4. memmove :215 size_in_bytes=lane_count overflows dpcd_lt_buffer[5]. Also get_lane_status_and_drive_settings :432 dpcd_lane_adjust[4] OOB. UNAUTHENTICATED: malicious DP dongle/MST hub/monitor hotplug -> HPD IRQ -> handle_automated_test -> dp_test_send_link_training -> stack overflow. Sibling of DF-1264. Fix: validate lane_count in {1,2,4} at dc_link_dp_perform_link_training entry and at source.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1319 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | full source trace: unvalidated lane_count overflows dpcd_lane[4] and dpcd_lt_buffer[5] | 5.6 KB | β raw |
| fix.diff | suggested-fix | validate lane_count in {1,2,4} at dp_test_send_link_training source | 1.2 KB | view raw |
| build.sh | build-script | documents hardware repro + validates fix.diff applies cleanly | 1.3 KB | view raw |
| run.sh | run-script | documents hardware repro path | 1.3 KB | view raw |
| build.log | build-log | guest evidence: amdgpu absent, no /dev/dri, only QEMU std-VGA | 254 B | view raw |
| env.txt | environment | guest uname, cc, driver-availability facts | 1.5 KB | view raw |
| README.md | readme | summary + repro + file index | 1.2 KB | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1319 β Unvalidated DisplayPort lane_count β stack overflow (amdgpu)
Status: INCONCLUSIVE (real bug; needs AMD GPU + amdgpu, both absent from audit guest)
Impact: stack buffer overflow via malicious DP sink (unauthenticated physical-layer)
Driver: amdgpu DisplayPort link training β NOT in X86_64_GENERIC, not loaded
Reproduce (hardware-equipped host only)
No userspace PoC can exercise a DP-peripheral attack. Reproduction
requires an amdgpu host with a malicious DP sink (programmable DP
attacker dongle) that returns lane_count > 4 in DPCD 0x220 on
automated link-training test. Hotplug β HPD β stack overflow in
dpcd_set_lt_pattern_and_lane_settings.
On the audit guest: amdgpu absent, no AMD GPU, no DP peripheral β nothing
to run. See VERDICT.md for the source trace and fix.diff for the
validated patch.
Files
VERDICT.mdβ full mechanism trace with path:line citationsfix.diffβ validate lane_count β {1,2,4} at dp_test_send_link_trainingbuild.sh/run.shβ document hardware repro + validate fix.diff appliesbuild.logβ guest evidence: amdgpu absent, no /dev/dri, only QEMU std-VGAenv.txt,manifest.json
DF-1319 β Unvalidated DisplayPort lane_count β stack buffer overflow (amdgpu)
Verdict
INCONCLUSIVE (real bug, needs hardware + driver absent from guest).
Source trace confirms the stack buffer overflow; it cannot be reproduced
on this QEMU audit guest because amdgpu is not compiled into
X86_64_GENERIC, is not a loadable module, and there is no AMD GPU
hardware (the only display PCI device is QEMU std-VGA 0x11111234,
which amdgpu does not drive). The fix was validated to apply cleanly
(git apply --check) against sys/dev/drm/amd/display/dc/core/dc_link_dp.c.
Mechanism
A malicious DP sink (dongle / MST hub / monitor) responds to an automated link-training test request with an out-of-range lane count.
sys/dev/drm/amd/display/dc/core/dc_link_dp.c:1768 dp_test_send_link_training(link) sys/dev/drm/amd/display/dc/core/dc_link_dp.c:1772 core_link_read_dpcd(link, DP_TEST_LANE_COUNT, sys/dev/drm/amd/display/dc/core/dc_link_dp.c:1775 &link_settings.lane_count, 1); // raw byte, NO validation sys/dev/drm/amd/display/dc/core/dc_link_dp.c:1784 link->verified_link_cap.lane_count = link_settings.lane_count; sys/dev/drm/amd/display/dc/core/dc_link_dp.c:1787 dp_retrain_link_dp_test(link, &link_settings, false);
lane_count is a raw byte from DPCD DP_TEST_LANE_COUNT (0x220), whose
lower 5 bits encode 0..31. It is propagated unvalidated into the
link-training helpers:
sys/dev/drm/amd/display/dc/core/dc_link_dp.c:168 union dpcd_training_lane dpcd_lane[LANE_COUNT_DP_MAX] = {0}; // LANE_COUNT_DP_MAX=4 sys/dev/drm/amd/display/dc/core/dc_link_dp.c:171 uint8_t dpcd_lt_buffer[5] = {0}; ... sys/dev/drm/amd/display/dc/core/dc_link_dp.c:194 for (lane = 0; lane < (uint32_t)lt_settings->link_settings.lane_count; lane++) { sys/dev/drm/amd/display/dc/core/dc_link_dp.c:197 dpcd_lane[lane].bits.VOLTAGE_SWING_SET = ...; // *** OOB write for lane_count>4 *** ... sys/dev/drm/amd/display/dc/core/dc_link_dp.c:212 size_in_bytes = lane_count * sizeof(dpcd_lane[0]); // up to 31*4 = 124 sys/dev/drm/amd/display/dc/core/dc_link_dp.c:215 memmove(&dpcd_lt_buffer[...], dpcd_lane, size_in_bytes); // *** dpcd_lt_buffer[5] overflow ***
LANE_COUNT_DP_MAX = LANE_COUNT_FOUR = 4
(sys/dev/drm/amd/display/dc/dc_dp_types.h:35-37). Valid DP lane counts
are {1, 2, 4}; a sink-supplied lane_count of 5..31 overflows the
4-element dpcd_lane[] stack array (line 197) and, via the memmove at
215, the 5-byte dpcd_lt_buffer[] (by up to 119 bytes). The sibling
helper get_lane_status_and_drive_settings
(sys/dev/drm/amd/display/dc/core/dc_link_dp.c:432,
:444-450) has the same dpcd_lane_adjust[LANE_COUNT_DP_MAX] overflow.
This is a stack buffer overflow with attacker-influenced content
(VOLTAGE_SWING/PRE_EMPHASIS bitfields) reachable through an
unauthenticated physical-layer attack: hotplug a malicious DP
peripheral β HPD IRQ β handle_automated_test
(dc_link_dp.c:1965/2053) β dp_test_send_link_training.
Reachability on this guest
- amdgpu is not in
X86_64_GENERIC(grep foramdgpu/drm5insys/config/X86_64_GENERICβ nothing; theamddevice line at :88 is the AMD 53C974 SCSI chip, unrelated). kldstat -v | grep -iE 'amdgpu|drm'β empty (no amdgpu loaded)./dev/dridoes not exist.- The only display PCI device is
vgapci0@pci0:0:2:0chip0x11111234(QEMU std/Bochs VGA) β not an AMD GPU, not bound by amdgpu.
There is therefore no amdgpu code path active on this guest, and no DP peripheral attack surface at all. Phase-4(d): real bug, unreachable on this guest (needs an AMD GPU host + amdgpu + a malicious DP sink).
Exploit chain
The primitive is a stack buffer overflow (write-capable). On a
hypothetical amdgpu host with a malicious DP peripheral, escalation
would proceed via stack corruption of dpcd_set_lt_pattern_and_lane_settings
(local frame). However, this is a display driver code path that
cannot be driven from userspace on the audit guest β it requires
physical DP-layer attacker input. The audit's escalation bar (unpriv
local user β uid=0) does not apply: this is a hardware-attack-surface
finding, evaluated at its realistic impact ceiling (stack overflow β
potential kernel RCE on an amdgpu host via a malicious peripheral). No
userspace exploit chain is developable or applicable here.
PoC changes
No runnable userspace PoC exists for a DP-peripheral attack; the folder
was empty. I authored build.sh/run.sh that document the
hardware-required reproduction steps and validate fix.diff applies
cleanly. VERDICT.md is the primary evidence.
Fix
fix.diff validates lane_count at the source β in
dp_test_send_link_training, immediately after the DPCD read and
before it is propagated into link->verified_link_cap and
dp_retrain_link_dp_test. Anything not in {LANE_COUNT_ONE,
LANE_COUNT_TWO, LANE_COUNT_FOUR} logs a warning and returns early,
dropping the malformed automated-test request. This is defense at the
trusted-boundary (DPCD read) and is the root-cause fix; it closes the
overflow for both dpcd_set_lt_pattern_and_lane_settings and
get_lane_status_and_drive_settings. The diff applies cleanly
(git apply --check).
Fix validation
fix_status: not_testable β amdgpu is not in the kernel build on this
guest, so neither the bug nor the fix can be exercised at runtime or
even compile-tested via nativekernel (the translation unit is not
compiled into GENERIC). Validated by: clean git apply --check, and
source inspection confirming the guard uses the existing
LANE_COUNT_* enum and the existing DC_LOG_WARNING macro (both
already used elsewhere in the same file).
Fix verification
not_testablecompile validated
nativekernel rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. amdgpu DP lane_count unvalidated vs dpcd_lane[4] -> stack overflow. amdgpu not in GENERIC.
No comments yet.