dm_dp_aux_transfer discards dc_link_aux_transfer() return on both AUX write paths: failed DPCD/I2C writes reported as full success to the DRM DP core (MST sideband desync on failing/hostile DP hardware)
Summary
Both WRITE cases ignore the return and unconditionally report msg->size - NACKed/timed-out writes look successful to drm_dp_dpcd_write and the MST sideband advances state the branch never received: persistent display deadlocks/wedged topology on hostile-dongle class. Assign-the-return fix (mirrors upstream unification).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4911 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.0 KB | β raw | |
| VERDICT.md | β | 2.7 KB | β raw | |
| aux_chardev_probe.c | β | 973 B | view raw | |
| build.sh | β | 108 B | view raw | |
| run.sh | β | 148 B | view raw | |
| run.log | β | 166 B | view raw | |
| env.txt | β | 347 B | view raw | |
| fix.diff | β | 1.2 KB | view raw | |
| verdict.json | β | 2.7 KB | view raw | |
| manifest.json | β | 954 B | view raw |
DF-4911 β dm_dp_aux_transfer discards dc_link_aux_transfer() return value on both AUX write paths, reporting failed DPCD/I2C writes as full success
File: sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:105-117,133-148
Severity: Low | CWE-252 | confidence: certain (defect) / likely (impact)
Build / run
The bug is in the kernel's AUX transfer callback; it is exercised whenever the
amdgpu DM driver performs DPCD/AUX writes (link training, MST sideband
messaging) against real DisplayPort hardware. There is no direct unprivileged
trigger: the raw AUX character device (/dev/drm_dp_auxN) is compiled out on
DragonFly (CONFIG_DRM_DP_AUX_CHARDEV is not set in
sys/dev/drm/kconfig.h), so writes are kernel-initiated.
./build.sh # cc -o aux_chardev_probe aux_chardev_probe.c
./run.sh # on AMD hardware with CONFIG_DRM_DP_AUX_CHARDEV: write to a DPCD
# address while the sink NACKs; drm dp core logs success anyway.
Expected result (on AMD DP hardware with failing/NACKing sink)
dc_link_aux_transfer returns -1 on AUX_CHANNEL_OPERATION_FAILED_TIMEOUT /
INVALID_REPLY (sys/dev/drm/amd/display/dc/core/dc_link_ddc.c:669-683), but
dm_dp_aux_transfer executes:
case DP_AUX_NATIVE_WRITE:
...
dc_link_aux_transfer(...); /* return value dropped */
result = msg->size; /* unconditional success */
so drm_dp_dpcd_write() callers (sys/dev/drm/drm_dp_helper.c:334+) see
msg->size (>0 == success) for a write that never reached the sink. The MST
sideband engine then advances message state / believes payload allocations
were programmed while the branch device never received them -> topology
desync, wedged links, black screens. A hostile DP dongle can NACK every write
deliberately, turning this into a persistent display-DoS amplifier and
confusing the kernel's model of programmed payload tables.
Actual result on this audit guest
No AMD display device, no /dev/dri, aux chardev compiled out β runtime
unreachable; status honestly untested. Defect itself is certain from
source.
VERDICT β DF-4911
status: untested (runtime requires AMD DM hardware performing real AUX
writes; the audit guest has no AMD display device and the raw AUX chardev is
compiled out β CONFIG_DRM_DP_AUX_CHARDEV absent from
sys/dev/drm/kconfig.h, verified sys/conf/files:2294 builds drm_dp_aux_dev.c
but registration is gated on that config). reproduced: 0.
The defect (certain, from source)
dm_dp_aux_transfer (sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c):
DP_AUX_NATIVE_WRITEcase, lines 105-117: the return value ofdc_link_aux_transfer()is discarded andresult = msg->size;is assigned unconditionally.DP_AUX_I2C_WRITEcase, lines 133-148: identical discard.
dc_link_aux_transfer (sys/dev/drm/amd/display/dc/core/dc_link_ddc.c:635-686)
returns -1 for AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN /
INVALID_REPLY / TIMEOUT (lines 678-682) and 0 for HPD-disconnect
(line 675-677). None of these reach the caller on the write paths: the DRM DP
core (drm_dp_dpcd_write, sys/dev/drm/drm_dp_helper.c:334) interprets the
returned msg->size (> 0) as full success.
Consequence: a DPCD/I2C write that NACKed, timed out, or raced an HPD disconnect is silently reported as delivered. Consumers that trust it:
- MST sideband DOWN_REQ transmission (
drm_dp_mst_topology.ctx path builds messages withdrm_dp_dpcd_writetoDP_SIDEBAND_MSG_DOWN_REQ_BASE) β the core's sequence numbers advance although the branch never saw the request; the topology state machine desyncs from the hub. - Payload allocation writes (
PAYLOAD_ALLOC_SET) believed programmed. - Link-training DPCD writes believed acknowledged.
Threat: a hostile or failing DP/MST device (evil-dongle class) that NACKs
writes while the driver reads plausible DPCD data causes persistent display
deadlocks and payload-table confusion β functional DoS, no memory corruption
identified (sizes are separately capped at 16 by the WARN_ON(msg->size > 16)
guard at line 89, which is correct; msg->reply is filled by DC on all
paths). Impact ceiling: local display DoS on AMD hardware; hence Low.
Guest evidence
run.log: probe builds; guest reports NO_AMD_PCI_DEVICE, NO_DEV_DRI,
and no /dev/drm_dp_aux* devices. Path unreachable at runtime on this
guest.
Fix
Assign the return value on both write paths exactly like the read paths
(fix.diff, mirrors the eventual upstream unification of all four cases).
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_testableFix cannot be validated on this guest (baseline path needs AMD DP hardware); fix.diff is a trivial, behavior-preserving-on-success change mirroring the read cases.
fix.diff
Confirmed kernel references
- sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:105
- sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:116
- sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:140
- sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:147
- sys/dev/drm/amd/display/dc/core/dc_link_ddc.c:635
- sys/dev/drm/amd/display/dc/core/dc_link_ddc.c:669
- sys/dev/drm/drm_dp_helper.c:334
- sys/dev/drm/kconfig.h
Detail
Evidence (decisive lines)
VERDICT.md (source trace + consumer analysis), run.log (guest: NO_AMD_PCI_DEVICE / NO_DEV_DRI / no aux chardev), fix.diff (assign result on write paths).
PoC changes
No seeded PoC; wrote aux_chardev_probe.c to document the (disabled) direct userspace handle.
Verified recommended fix
Propagate dc_link_aux_transfer()'s return value on the NATIVE_WRITE and I2C_WRITE cases instead of unconditionally returning msg->size (see fix.diff).
Verdict
Certain code defect, untested at runtime (no AMD display hardware on the audit guest; raw AUX chardev compiled out β CONFIG_DRM_DP_AUX_CHARDEV not defined in sys/dev/drm/kconfig.h). dm_dp_aux_transfer discards the dc_link_aux_transfer() return on both write cases (amdgpu_dm_mst_types.c:105-117 NATIVE_WRITE, 133-148 I2C_WRITE) and returns msg->size unconditionally, so NACKed/timed-out/HPD-raced DPCD and I2C-over-AUX writes are reported to the DRM DP core as full success (dc_link_aux_transfer returns -1/0 on those failures, dc_link_ddc.c:669-683). MST sideband DOWN_REQ sequencing and payload-alloc programming then desync from reality on failing or hostile DP hardware β persistent display DoS; no memory-safety impact found (16-byte cap at :89 is correct).
No comments yet.