NULL amdgpu_encoder / enc_priv dereference family after best_encoder() in the two scaling-mode property handlers and amdgpu_connector_get_monitor_bpc (radeon DF-4033 twin)
Summary
Three sites assume best_encoder never returns NULL and enc_priv is always allocated: the get_monitor_bpc eDP/LVDS branch (best_encoder returns NULL on an empty possible-encoder list; enc_priv initialized NULL and only filled if the dig/lcd info kzallocs succeed) - both dereferenced unchecked (the same file guards enc_priv elsewhere); and the two scaling-mode property handlers via DRM_MASTER-gated SETPROPERTY. An encoder-less connector arises when the VBIOS device bits match no encoder. to_amdgpu_encoder(NULL) = container_of(NULL) -> deterministic NULL-page panic. NULL-guard fix at all three sites.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4201 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.9 KB | β raw | |
| VERDICT.md | β | 3.0 KB | β raw | |
| trigger_df4201.c | β | 4.5 KB | view raw | |
| build.sh | β | 87 B | view raw | |
| run.sh | β | 89 B | view raw | |
| build.log | β | 9 B | view raw | |
| run.log | β | 108 B | view raw | |
| env.txt | β | 244 B | view raw | |
| fix.diff | β | 1.7 KB | view raw | |
| manifest.json | β | 1.0 KB | view raw | |
| verdict.json | β | 3.7 KB | view raw |
DF-4201 β NULL amdgpu_encoder dereference family after best_encoder() (radeon DF-4033 twin)
File: sys/dev/drm/amd/amdgpu/amdgpu_connectors.c:139-141, 583-594, 792-803 (pass 2)
Severity: Low (VBIOS-misconfig-gated NULL deref panic; DRM_MASTER-gated ioctls / modeset path)
Status: not reproduced at runtime β hardware-gated; source-level certainty.
Sites
-
amdgpu_connector_get_monitor_bpc()β eDP/LVDS branch whendisplay_info.bpc == 0(no EDID / EDID without bpc):c struct drm_encoder *encoder = connector_funcs->best_encoder(connector); /* :139 may be NULL */ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); /* :140 NULL->container */ struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv; /* :141 */ if (dig->lcd_misc & ...) /* :143 deref */Two NULLs in the chain:best_encoder()(NULL on an empty possible-encoder list βamdgpu_connector_best_single_encoder:346-357,amdgpu_connector_dvi_encoder:1122-1148 both return NULL), andenc_privitself, whichdce_v*_0_encoder_addinitializes to NULL (dce_v10_0.c:3494) and only replaces ifamdgpu_atombios_encoder_get_lcd_info()/get_dig_info()kzallocs succeed (dce_v10_0.c:3520/3524/3528). The same file knows to guardenc_privβ see the coherent-mode handler at :484 β but not here. Reached from every modeset (atombios_crtc.c:773,atombios_dp.c:261,dce_v{8,10,11}_0.c). -
amdgpu_connector_set_property()β scaling-mode branch:c } else { amdgpu_encoder = to_amdgpu_encoder(connector_funcs->best_encoder(connector)); /* :584 */ } ... if (amdgpu_encoder->rmx_type == rmx_type) /* :594 NULL deref */Reached viaDRM_IOCTL_MODE_SETPROPERTY/DRM_IOCTL_MODE_OBJ_SETPROPERTY(bothDRM_MASTER-gated β drm_ioctl.c:667/679): the display session owner. -
amdgpu_connector_set_lcd_property()β identical pattern, deref at :803. Same ioctls (this is the eDP/LVDS.set_property).
Reach
An empty (or NULL-producing) possible-encoder list requires the VBIOS to
describe a connector whose device bits no encoder claims β
amdgpu_link_encoder_connector() (amdgpu_encoders.c:45-56) then attaches
nothing. Atom-table parser quirk territory; upstream Linux carries the
identical unguarded code.
Evidence
trigger_df4201.cβ raw-ioctl scaling-mode write over every connector property (becomes DRM master, enumerates connectors, writes DRM_MODE_SCALE_CENTER). Builds and runs on the guest; guest has no AMD GPU (env.txt), so the amdgpu handlers are unreachable there.fix.diffβ NULL guards at all three sites (applies cleanly to a copy of the real file; cumulative with DF-4200/4202 diffs).
Fix
See fix.diff (guard best_encoder() result at :584/:793; guard both
encoder and enc_priv in the monitor-bpc branch).
DF-4201 VERDICT β NULL encoder deref family (DF-4033 amdgpu twin)
Verdict: not reproduced at runtime (hardware-gated); source-level certainty: certain. Impact if exercised: kernel NULL-deref panic (DoS) from the display-session owner.
Proof of the NULL sources
amdgpu_connector_best_single_encoder()(amdgpu_connectors.c:346-357) returns NULL whendrm_connector_for_each_possible_encoderyields nothing;amdgpu_connector_dvi_encoder()(:1122-1148) returns NULL the same way (its type-filter loop can miss, then the fallback "pick the first one" loop also finds none).- Possible encoders are attached only by
amdgpu_link_encoder_connector()(amdgpu_encoders.c:45-56): a connector whose VBIOS device bits match no encoder ends with an empty list. The connector is still created and registered (amdgpu_connector_add, amdgpu_connectors.c:1499-1900 β encoder linkage happens later and is never checked). enc_privNULL:dce_v8_0.c:3382,dce_v10_0.c:3494,dce_v11_0.c:3626all start withamdgpu_encoder->enc_priv = NULLand only fill it if the atom-parser kzallocs succeed (amdgpu_atombios_encoder_get_lcd_info/amdgpu_atombios_encoder_get_dig_info). The coherent-mode handler in this very file guards it (if (!amdgpu_encoder->enc_priv) return 0;amdgpu_connectors.c:484-485) β the monitor-bpc path at :141 does not.
Consumers of the NULL
amdgpu_connector_get_monitor_bpc():139-143 β called on every modeset fromatombios_crtc.c:773(crtc prepare),atombios_dp.c:261(link bpp),dce_v8_0.c:444/dce_v10_0.c:504/dce_v11_0.c:530.amdgpu_connector_set_property():583-594 β scaling-mode property write (DRM_MASTER-gated ioctls, drm_ioctl.c:667/679).amdgpu_connector_set_lcd_property():792-803 β the eDP/LVDS.set_propertyfor the same property (same ioctls).
to_amdgpu_encoder(NULL) = container_of(NULL, ...) (amdgpu_mode.h:58);
base is the first member, so the :594/:803 rmx_type reads fault at a
small fixed offset β deterministic NULL-page panic.
Runtime attempt
trigger_df4201.c compiled and ran on the audit guest (cc 8.3); the guest
has no DRM device at all (/dev/dri absent, no AMD/ATI PCI β env.txt), so
the amdgpu connector functions cannot execute there. Triggering on real
hardware needs an amdgpu ASIC whose VBIOS describes an encoder-less
connector (or an encoder whose dig/lcd info allocation failed at probe) β
same VBIOS-quirk gating as the radeon twin DF-4033, which this finding
mirrors site-for-site.
Fix validation
Not possible on this guest (no AMD GPU). fix.diff verified to apply
cleanly to a pristine copy of the real file and is consistent with the
in-file guard patterns (:479-485, :1083).
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.diff applies cleanly to a pristine copy of the real amdgpu_connectors.c (verified, cumulative with DF-4200/4202 diffs), consistent with the in-file guard pattern at :484; kernel-build validation not possible on this guest (no AMD GPU).
fix.diff; local patch --dry-run + applied-copy verification noted in VERDICT.md
Confirmed kernel references
- sys/dev/drm/amd/amdgpu/amdgpu_connectors.c:137-147
- sys/dev/drm/amd/amdgpu/amdgpu_connectors.c:577-604
- sys/dev/drm/amd/amdgpu/amdgpu_connectors.c:789-808
- sys/dev/drm/amd/amdgpu/amdgpu_connectors.c:346-357
- sys/dev/drm/amd/amdgpu/amdgpu_connectors.c:484-485
- sys/dev/drm/amd/amdgpu/amdgpu_encoders.c:45-56
- sys/dev/drm/amd/amdgpu/dce_v10_0.c:3494
- sys/dev/drm/drm_ioctl.c:667
Detail
Exploit chain
display-session owner (DRM master) writes a connector's scaling-mode property on an encoder-less amdgpu connector (VBIOS misconfiguration), or any modeset on an eDP/LVDS panel with bpc-less EDID and failed dig/lcd-info allocation -> NULL deref panic
Evidence (decisive lines)
VERDICT.md (NULL-source proof at both levels: encoder list and enc_priv allocation); trigger_df4201.c + run.log (guest run, hardware-gated); fix.diff (three guards); env.txt
PoC changes
trigger written from scratch (raw ioctls, self-contained struct defs, no libdrm); __u32/__u64 typedefs added after first guest compile
Verified recommended fix
NULL-check best_encoder() results at the two scaling-mode sites and both encoder and enc_priv in the monitor-bpc branch (see fix.diff).
Verdict
DF-4033 radeon twin in amdgpu: three sites dereference the result of best_encoder()/enc_priv without NULL checks β amdgpu_connector_get_monitor_bpc:139-143 (best_encoder NULL from amdgpu_connector_best_single_encoder :346-357 / dvi_encoder :1122-1148 on an empty possible-encoder list, plus enc_priv NULL because dce_v*_0_encoder_add initializes it to NULL and only fills it if get_lcd_info/get_dig_info kzallocs succeed, dce_v10_0.c:3494/3520), amdgpu_connector_set_property:583-594 and amdgpu_connector_set_lcd_property:792-803 (scaling-mode branch, deref rmx_type/native_mode). Possible encoders are attached only by amdgpu_link_encoder_connector (amdgpu_encoders.c:45-56) β a VBIOS connector whose device bits match no encoder stays encoder-less while remaining registered. The monitor-bpc path fires on every modeset (atombios_crtc.c:773); the property paths via DRM_MASTER-gated MODE_SETPROPERTY/OBJ_SETPROPERTY (drm_ioctl.c:667/679). to_amdgpu_encoder(NULL) deref = deterministic NULL-page panic. The same file guards enc_priv at :484 β the omission is provable in-source. Not reproduced at runtime: guest has no AMD GPU (env.txt); trigger built and ran on the guest. Identical unguarded code in upstream Linux.
No comments yet.