resource_build_scaling_params INTERLACE error path leaves shared dc_stream_state.dst.height permanently doubled (compounding signed-int overflow)
Summary
For interlaced streams the function doubles the SHARED, refcounted dc_stream_state dst.height at entry, but the early return-false for viewport < 16px skips the compensating /= 2. A failed validation therefore commits persistent corruption to the stream object owned by amdgpu_dm's crtc state; the same stream object is reused across commits, so each failed interlaced attempt doubles again: dst.height = h*2^N, overflowing signed int after ~21 attempts. VERIFIED: userland verbatim transcription executed on the guest - 1080->2160->...->-2030043136 at attempt 21. Persistent display corruption/local DoS reachable by the DRM-master session user on amdgpu DC hardware. Upstream dropped the shared-state mutation entirely by v5.4. Balance-on-error-return fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3943 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| interlace_sim.c | β | 2.7 KB | view raw | |
| build.sh | β | 99 B | view raw | |
| run.sh | β | 99 B | view raw | |
| build.log | β | 162 B | view raw | |
| run.log | β | 1.4 KB | view raw | |
| env.txt | β | 68 B | view raw | |
| VERDICT.md | β | 3.3 KB | β raw | |
| fix.diff | β | 859 B | view raw | |
| verdict.json | β | 2.4 KB | view raw |
VERDICT β DF-3943
Bottom line
not_reproduced on the audit guest β source-level certain. The audit QEMU
guest has no AMD GPU (virtio-gpu 0x1111:1234, no /dev/dri, drm not built into
the running kernel β env.txt), so the kernel path cannot be exercised. The bug
is however provable by direct reading of the cited lines, and the control-flow
was executed 1:1 in a userland harness on the guest (run.log): every failed
interlaced validation leaves pipe_ctx->stream->dst.height doubled.
Root cause (path:line)
resource_build_scaling_params() mutates the shared dc_stream_state:
- sys/dev/drm/amd/display/dc/core/dc_resource.c:1118-1119 β
if (pipe_ctx->stream->timing.flags.INTERLACE) pipe_ctx->stream->dst.height *= 2; - sys/dev/drm/amd/display/dc/core/dc_resource.c:1125-1126 β
if (viewport.height < 16 || viewport.width < 16) return false;β early return, skips the compensating divide - sys/dev/drm/amd/display/dc/core/dc_resource.c:1188-1189 β
if (INTERLACE) pipe_ctx->stream->dst.height /= 2;β only on the fall-through path
pipe_ctx->stream is the refcounted struct dc_stream_state owned by
amdgpu_dm (dm_new_crtc_state->stream), not a per-validation copy; a failed
dc_validate_global_state() β DC_FAIL_SCALING (dc_resource.c:1201-1206,
1195-1209) aborts the atomic commit but leaves the stream object corrupted.
amdgpu_dm reuses the same stream object across commits that keep the same
mode, so the doubling compounds: N failed attempts β dst.height Γ 2^N.
int (32-bit) overflows after ~21 attempts from 1080 (see run.log attempt 21:
-2030043136 = 1080Β·2^21 wrapped). Downstream sinks of the corrupted field:
calculate_recout() :637-657 (recout.height numerator), calculate_scaling_ratios()
:713-737 (out_h), scl_data.h_active/v_active β all part of the same commit path.
Why upstream confirms this is wrong
The whole "double dst.height in the shared stream during validation" pattern was removed from mainline by Linux v5.4 (resource_build_scaling_params in v5.4 has no INTERLACE mutation at all; verified against the v5.4 blob fetched from api.github.com β /tmp/opencode/dc_v5.4.c:962ff). No dedicated fix commit exists; it died in the DC refactoring between 4.17 and 5.4.
Impact
Local persistent display-state corruption / DoS reachable by the graphical session user (DRM master) on DCE8+/DCN amdgpu hardware in interlaced modes. Not memory corruption: the corrupted value is a bounded int used in display math; worst case is garbage scaling parameters, display corruption, and errant division results (denominators are separately validated by amdgpu_dm; DF-1301 tracks the raw zero-rect class). Severity Medium.
PoC changes vs seed
None β no seed existed (pass-2 new finding). Harness written from scratch; logic transcribed verbatim from dc_resource.c:1118-1189.
Fix validation
Not possible on this guest (no AMD GPU): fix_status=not_testable,
fix_baseline_reproduced=0, fix_patched_reproduced=0. fix.diff is
git apply --check-clean against sys/ (verified in-place).
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_testablecannot build/run amdgpu DC on the audit guest (no AMD GPU); fix.diff applies clean to sys/ and restores the *=2//=/=2 balance
fix.diff; git apply --check clean
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (interlace_sim: compounding doubling + signed wrap at attempt 21), interlace_sim.c (verbatim control flow of :1118-1189), VERDICT.md (root cause + upstream v5.4 comparison), fix.diff (git-apply-clean)
PoC changes
no seed; harness transcribed verbatim from the kernel function
Verified recommended fix
Restore dst.height /= 2 on the early-return path, or stop mutating the shared stream in validation (upstream v5.4 removed the pattern)
Verdict
Source-certain error-path imbalance: resource_build_scaling_params() doubles the shared dc_stream_state.dst.height for INTERLACE at entry (dc_resource.c:1118-1119) but the early return false for viewport<16 (:1125-1126) skips the compensating /=2 (:1188-1189), so every failed interlaced validation permanently doubles the persistent stream's dst.height; compounding across attempts overflows the signed int (~21 doublings from 1080). Guest has no AMD GPU (no /dev/dri, virtio-gpu), so not reproduced at kernel level; a verbatim userland transcription of the control flow run on the guest (run.log) shows 1080->2160->...->-2030043136. Upstream dropped the shared-state mutation entirely by Linux v5.4.
No comments yet.