NULL pointer dereference in dm_dp_add_mst_connector on fake-MST encoder allocation failure
- File:
sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c - Lines: 346β348 (deref via
aconnector->mst_encoder->base) - Severity: Low
- CVSS 3.1:
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:H - CWE: CWE-476 NULL Pointer Dereference
- Confidence: certain
- Status: new
Summary
dm_dp_add_mst_connector() stores the result of
dm_dp_create_fake_mst_encoder() without NULL-checking it, then computes
&aconnector->mst_encoder->base and hands that address to
drm_connector_attach_encoder, which unconditionally dereferences
encoder->base.id. dm_dp_create_fake_mst_encoder returns NULL on kzalloc
failure (line 291β293), so under memory pressure this path collapses into a
kernel NULL-deref panic during connector bring-up.
Root cause
In sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:346-348:
346 aconnector->mst_encoder = dm_dp_create_fake_mst_encoder(master);
347 drm_connector_attach_encoder(&aconnector->base,
348 &aconnector->mst_encoder->base);
dm_dp_create_fake_mst_encoder (lines 283β308) does:
291 amgpu_encoder = kzalloc(sizeof(*amdgpu_encoder), GFP_KERNEL);
292 if (!amdgpu_encoder)
293 return NULL;
On memory pressure the function returns NULL, mst_encoder is set to NULL,
and the next statement evaluates &aconnector->mst_encoder->base.
struct amdgpu_encoder (sys/dev/drm/amd/amdgpu/amdgpu_mode.h:462-463) begins
with struct drm_encoder base; as its FIRST field, so offsetof is 0 and
&((struct amdgpu_encoder *)NULL)->base == NULL.
drm_connector_attach_encoder (sys/dev/drm/drm_connector.c:310-336) reaches
connector->encoder_ids[i] = encoder->base.id; at drm_connector.c:331,
dereferencing the NULL encoder pointer β fatal page fault.
Threat model
Attacker position: local unprivileged user on a system where amdgpu is in
active use AND the kernel is under sufficient memory pressure to fail a
sizeof(struct amdgpu_encoder) (a few hundred bytes) GFP_KERNEL allocation.
Practically, this means running the system to OOM or fragmenting the
GFP_KERNEL slab while MST topology changes are in flight (unplug/replug an
MST hub during heavy memory load).
Required privilege: none beyond the ability to cause memory pressure (any local user can fork-bomb or amass slab caches).
Required config: amdgpu loaded, an MST-capable connector.
Impact: kernel NULL-deref panic β reboot. Confidentiality/Integrity: none.
Lower practical severity than the EDID bug (DF-1966) because it requires the
kernel to be unable to satisfy a small GFP_KERNEL allocation at the exact
instant an MST port is being added β but it is a deterministic panic on that
condition, and the fix is trivial.
Proof of concept
Prereq: amdgpu with an MST-capable DP connector present (any modern AMD APU/dGPU with DP or USB-C DP-alt-mode).
Force the system toward GFP_KERNEL exhaustion:
for i in $(seq 1 4096); do dd if=/dev/zero of=/tmp/fill.$i bs=1M count=256 & done
(or a tiny fork/alloc bomb) while simultaneously cycling an attached MST hub:
while true; do
xrandr --output DP-1 --off
sleep 0.1
xrandr --output DP-1 --auto
sleep 0.1
done
Each topology rebuild on the master connector queues dm_dp_add_mst_connector
for each discovered child port; when one of those invocations races with an
OOM, kzalloc in dm_dp_create_fake_mst_encoder returns NULL and the next
instruction takes the page fault.
Expected result: panic in dm_dp_add_mst_connector β drm_connector_attach_encoder
with fault address near 0.
Recommended fix
Check the return of dm_dp_create_fake_mst_encoder and roll back the
partially-initialized connector (which has already been drm_connector_init'd)
before returning NULL to the topology manager.
--- a/sys/dev/drm/amd/display/amgpu_dm/amdgpu_dm_mst_types.c
+++ b/sys/dev/drm/amd/display/amgpu_dm/amdgpu_dm_mst_types.c
@@ -343,6 +343,12 @@ dm_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
amdgpu_dm_connector_init_helper(
&adev->dm, aconnector, DRM_MODE_CONNECTOR_DisplayPort,
master->dc_link, master->connector_id);
+
+ aconnector->mst_encoder = dm_dp_create_fake_mst_encoder(master);
+ if (!aconnector->mst_encoder) {
+ DRM_ERROR("amdgpu_dm: failed to allocate MST encoder\n");
+ drm_connector_cleanup(connector);
+ kfree(aconnector);
+ return NULL;
+ }
- aconnector->mst_encoder = dm_dp_create_fake_mst_encoder(master);
drm_connector_attach_encoder(&aconnector->base,
&aconnector->mst_encoder->base);
This unblocks the NULL deref at drm_connector.c:331 and surfaces the failure
to the MST topology manager, which already handles NULL returns from this
callback.
References
sys/dev/drm/amd/display/amgpu_dm/amdgpu_dm_mst_types.c:346-348β the buggy derefsys/dev/drm/amd/display/amgpu_dm/amdgpu_dm_mst_types.c:283-308βdm_dp_create_fake_mst_encoderreturns NULL on kzalloc failuresys/dev/drm/drm_connector.c:310-336βdrm_connector_attach_encoderderefsencoder->base.idsys/dev/drm/amd/amdgpu/amdgpu_mode.h:462-463βstruct amdgpu_encoderbegins withstruct drm_encoder base;
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1967 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | Source verification narrative | 1.2 KB | β raw |
| fix.diff | suggested-fix | Fix: Add NULL check on mst_encoder; cleanup and return on failure. | 601 B | view raw |
| build.sh | build-script | Build/validation instructions | 366 B | view raw |
| run.sh | run-script | Run instructions (HW-gated, source-only) | 184 B | view raw |
| env.txt | environment | Guest environment | 404 B | view raw |
DF-1967 - Source Verification
Verdict: REPRODUCED (source-only confirmation)
Finding: sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:346-348
Mechanism: dm_dp_add_mst_connector assigns mst_encoder from dm_dp_create_fake_mst_encoder without NULL check, then passes &mst_encoder->base to drm_connector_attach_encoder. OOM β NULL page fault.
Hardware dependency: Requires AMD GPU with DisplayPort MST topology.
Fix: Add NULL check on mst_encoder; cleanup and return on failure.
Verification method
Source-only confirmation. The cited code path was traced line-by-line in the audited sys/ tree. The bug exists exactly as described. This is a HW-gated driver finding β the vulnerable code path requires specific hardware (GPU, controller, PHY, TPM, etc.) not present in the QEMU audit guest. Runtime reproduction on this guest is not possible without the hardware.
Fix validation
fix.diff authored and applied to guest source. All 40 fixes in this batch
compile cleanly in a single combined kernel build: make -j6 nativekernel
KERNCONF=X86_64_GENERIC β rc=0, zero -Werror violations.
Kernel: DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026
Fix verification
not_testablenot_testable: HW-gated. fix.diff applies + compiles in batch build (rc=0 -Werror). Source trace confirms fix closes the path.
Batch build: 40 fix.diffs applied, make nativekernel β rc=0 -Werror. Bug at sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:346-348 source-confirmed.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- a
- m
- d
- /
- d
- i
- s
- p
- l
- a
- y
- /
- a
- m
- d
- g
- p
- u
- _
- d
- m
- /
- a
- m
- d
- g
- p
- u
- _
- d
- m
- _
- m
- s
- t
- _
- t
- y
- p
- e
- s
- .
- c
- :
- 3
- 4
- 6
- -
- 3
- 4
- 8
Detail
Exploit chain
none
Evidence (decisive lines)
Source trace sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:346-348. HW-gated (no HW in QEMU). Fix compiles in batch build rc=0.
PoC changes
Evidence pack: VERDICT.md, fix.diff, manifest.json. Fix: mst_encoder NULL deref on OOM. Add NULL check + cleanup.
Verified recommended fix
See fix.diff. mst_encoder NULL deref on OOM. Add NULL check + cleanup.
Verdict
REPRODUCED (source-only). sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:346-348: mst_encoder NULL deref on OOM. Add NULL check + cleanup.
No comments yet.