radeon MST connector leaks one EDID blob per probe and per destroy (radeon_connector->edid overwritten without kfree)
Summary
get_ddc_modes stores the fresh allocation from drm_dp_mst_get_edid (a NEW edid every call) over the previous blob without freeing it, and neither MST destroy path kfrees it: every forced probe (unpriv DRM ioctls) leaks 128-512 bytes; unbounded heap growth. kfree-before-overwrite + destroy-path fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4632 Β· 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.1 KB | β raw | |
| VERDICT.md | β | 1.6 KB | β raw |
DF-4632 β radeon MST connector leaks one EDID blob per probe and per destroy
What
radeon_dp_mst_get_ddc_modes() (sys/dev/drm/radeon/radeon_dp_mst.c:186-204)
does:
edid = drm_dp_mst_get_edid(connector, &master->mst_mgr, radeon_connector->port);
radeon_connector->edid = edid; /* <-- old pointer dropped, no kfree */
drm_dp_mst_get_edid() (sys/dev/drm/drm_dp_mst_topology.c:2553-2571) returns a
fresh allocation on every call β drm_edid_duplicate(port->cached_edid)
when cached, drm_get_edid() otherwise. The previous
radeon_connector->edid allocation is never freed, and neither destroy path
frees it either (radeon_dp_destroy_mst_connector :307-320 and
radeon_dp_mst_connector_destroy :248-258 both omit kfree(edid)).
Trigger (unprivileged)
Every drm_helper_probe_single_connector_modes β get_modes for an MST
connector allocates a new EDID (128β512 bytes, drm_edid block(s)) and
abandons the previous one. A user with /dev/dri/card0 access can force probes
in a loop (DRM_IOCTL_MODE_GETCONNECTOR without a cached mode set, or the
sysfs status read path) β each iteration leaks one EDID blob. Every hub
connect/disconnect cycle additionally leaks the last EDID of each destroyed
child connector.
Impact: unbounded kernel heap growth (memory-exhaustion DoS) driven by an unprivileged desktop user; needs a DP MST topology (radeon DCE5+). Same reach class as DF-1778's malicious-hub model.
Upstream
Upstream radeon has the same historical pattern but its probe path for MST was reworked; regardless, the in-tree fix is a one-liner (kfree before overwrite + kfree in the destroy path).
Fix
--- a/sys/dev/drm/radeon/radeon_dp_mst.c
+++ b/sys/dev/drm/radeon/radeon_dp_mst.c
@@ -191,6 +191,7 @@ static int radeon_dp_mst_get_ddc_modes(struct drm_connector *connector)
int ret = 0;
edid = drm_dp_mst_get_edid(connector, &master->mst_mgr, radeon_connector->port);
+ kfree(radeon_connector->edid);
radeon_connector->edid = edid;
@@ (radeon_dp_mst_connector_destroy / radeon_dp_destroy_mst_connector)
+ kfree(radeon_connector->edid); /* before kfree(radeon_connector) */
DF-4632 VERDICT β EDID leak per probe/destroy (radeon MST)
status: untested β no radeon GPU / DP MST hub on the audit guest. The leak is source-certain (no runtime behavior to discover):
drm_dp_mst_get_edid()returns a NEW allocation on every call (sys/dev/drm/drm_dp_mst_topology.c:2562-2567:drm_edid_duplicate()of the cached EDID, or a freshdrm_get_edid()), with no caller-side retention.radeon_dp_mst_get_ddc_modes()stores it intoradeon_connector->edid(radeon_dp_mst.c:194) without freeing the previous value.get_modesruns on every forced connector probe (drm_helper_probe_single_connector_modes), i.e. an unprivileged/dev/dri/card0user can drive it in a loop.- Neither destroy path (
radeon_dp_mst_connector_destroy:248-258 norradeon_dp_destroy_mst_connector:307-320) kfreesradeon_connector->edid, so the final blob is leaked at connector teardown as well.
Impact ceiling: memory-exhaustion DoS (128-512 bytes per probe, unbounded); trivially fixed by kfree-before-overwrite + kfree in destroy. Severity Low (hardware-gated, slow leak, no info disclosure β the memory is never exposed, only abandoned).
Recommended fix: see README.md diff.
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.
(No fix.diff for this item: documented recommendation only - see Fix above.)
Fix verification
not_testableno radeon hardware on guest; cannot validate
Confirmed kernel references
Detail
Evidence (decisive lines)
radeon_dp_mst.c:193-194 overwrite without kfree; drm_dp_mst_topology.c:2562-2567 fresh allocation per call
PoC changes
No runnable PoC: needs radeon + DP MST hardware.
Verified recommended fix
kfree(radeon_connector->edid) before the overwrite in radeon_dp_mst_get_ddc_modes, and kfree it in both MST connector destroy paths
Verdict
radeon_dp_mst_get_ddc_modes (radeon_dp_mst.c:194) overwrites radeon_connector->edid with a fresh allocation returned by drm_dp_mst_get_edid (drm_dp_mst_topology.c:2553-2571 allocates anew on every call) without freeing the previous blob; neither MST destroy path kfrees it either. Unbounded kernel heap growth driven by unprivileged connector probes on a DP MST topology; impact ceiling is memory exhaustion (no contents disclosure). Source-certain; untested because the audit guest has no radeon GPU.
No comments yet.