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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1319 Β· 9 files
FileTypeDescriptionSize
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
README.md readme summary + repro + file index
↓ download 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 citations
  • fix.diff β€” validate lane_count ∈ {1,2,4} at dp_test_send_link_training
  • build.sh/run.sh β€” document hardware repro + validate fix.diff applies
  • build.log β€” guest evidence: amdgpu absent, no /dev/dri, only QEMU std-VGA
  • env.txt, manifest.json
VERDICT.md verdict full source trace: unvalidated lane_count overflows dpcd_lane[4] and dpcd_lt_buffer[5]
↓ download raw

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 for amdgpu/drm5 in sys/config/X86_64_GENERIC β†’ nothing; the amd device line at :88 is the AMD 53C974 SCSI chip, unrelated).
  • kldstat -v | grep -iE 'amdgpu|drm' β†’ empty (no amdgpu loaded).
  • /dev/dri does not exist.
  • The only display PCI device is vgapci0@pci0:0:2:0 chip 0x11111234 (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_testable

compile 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.