dce112_compressor_construct() init-order defects: dead 64-bit-bus LPT guard + lpt_channels_num wipe make program_lpt_control's only valid case unreachable (LPT armed with unprogrammed config)
Summary
The 'single DRAM channel => no LPT' guard reads memory_bus_width while it is still 0 (assigned only later), so it never fires; lpt_channels_num=1 is unconditionally wiped to 0 later, so program_lpt_control always hits default and LPT_MODE is never programmed - while enable_lpt still arms LPT with stale values whenever FBC+LPT come up on eDP. banks/raw/dram_channels never set => lpt_size_alignment always 0. Upstream-inherited statement order. Register-programming logic only, no kernel memory corruption; semi-privileged Polaris eDP reach. Statement-for-statement logic mirror PROVEN on guest (both defects confirmed). Reorder-the-init fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4601 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| construct_order.c | β | 4.3 KB | view raw | |
| build.sh | β | 145 B | view raw | |
| run.sh | β | 106 B | view raw | |
| build.log | β | 95 B | view raw | |
| run.log | β | 450 B | view raw | |
| env.txt | β | 188 B | view raw | |
| README.md | β | 3.5 KB | β raw | |
| VERDICT.md | β | 3.7 KB | β raw | |
| fix.diff | β | 1.6 KB | view raw | |
| manifest.json | β | 1.3 KB | view raw | |
| verdict.json | β | 3.5 KB | view raw |
DF-4601 β dce112_compressor_construct() init-order defects: dead 64-bit-bus LPT guard + lpt_channels_num wipe
File: sys/dev/drm/amd/display/dc/dce112/dce112_compressor.c (DCE11.2 / Polisar FBC+LPT engine)
Severity: Info (correctness/dead-code; no demonstrated kernel memory-safety impact β register-programming logic only)
Confidence: certain (both facts provable by source alone; demonstrator reproduces the logic)
What it is
Two initialization-ordering defects in dce112_compressor_construct()
(dce112_compressor.c:787-829):
-
Dead single-DRAM-channel guard (:802). The object is
kzalloced bydce112_compressor_create()(:831-841), somemory_bus_width == 0whendce112_compressor_construct()evaluatesif (compressor->base.memory_bus_width == 64)at :802. The field is assigned its real value,ctx->asic_id.vram_width, only afterwards at :810. The guard's whole purpose ("only 1 DRAM channel -> LPT should not be supported") can therefore never fire: LPT_SUPPORT stays set on 64-bit-bus ASICs. -
lpt_channels_numwipe (:818). :797 setslpt_channels_num = 1("For DCE 11 always use one DRAM channel for LPT"), then :818 unconditionally overwrites it with 0. Consequently the only validcase 1:ofdce112_compressor_program_lpt_control()(:675-691) is unreachable β the switch always falls todefault(:686-690) andLOW_POWER_TILING_MODEis never programmed by this driver; the register keeps whatever stale value it holds.
Knock-on (same construct): banks_num, raw_size, dram_channels_num,
channel_interleave_size are zeroed at :814-817 and no code anywhere in
the tree ever sets them (verified by grep across
sys/dev/drm/amd/display/dc/), so lpt_size_alignment() (:98-103) always
returns 0 and dce112_compressor_program_lpt_control() leaves
LOW_POWER_TILING_ROWS_PER_CHAN at 0 (:708-714) and
dce112_compressor_program_compressed_surface_address_and_pitch() skips the
address alignment (:500-505) β yet dce112_compressor_enable_lpt()
(:594-651) still arms LOW_POWER_TILING_ENABLE=1 and
GMCON_LPT_TARGET = stale_mode+1 whenever FBC+LPT come up on eDP
(dce110_hw_sequencer.c:enable_fbc -> enable_lpt when LPT_SUPPORT &&
paths_num<2 && wh <= 25601600).
Why no runtime reproduction on the audit guest
The QEMU guest has no AMD GPU (virtio only), so the DCE11.2 register path cannot execute there. The two defects are nevertheless pure static facts β no hardware-dependent behavior is needed to prove them β and the pack contains a host-compiled demonstrator that mirrors the exact statement order and asserts both. On-HW impact (LPT armed with unprogrammed config on Polaris eDP panels) is speculative and out of the audit's demonstrable scope; worst realistic case is display underflow / GPU hang (local DoS by a modeset-capable user), not kernel memory corruption. Filed as Info hardening for upstream.
Reproduce (host, no GPU)
./build.sh # cc -O2 -Wall -o construct_order construct_order.c ./run.sh # expects: PROOF-1 OK / PROOF-2 OK / RESULT: DEFECT CONFIRMED, exit 0
Fix
fix.diff (authored against the read-only sys/ tree, never applied
there): assign memory_bus_width from asic_id.vram_width before the
64-bit-bus check, and set lpt_channels_num = 1 after the zeroing block
instead of wiping it. Kernel-build validation on the guest is not possible
(the amd drm driver cannot attach to QEMU virtio hardware), hence
fix_status=not_testable.
DF-4601 VERDICT β not_reproduced on guest (target ASIC absent); logic defect CONFIRMED by mirror; severity Info
The defect (static proof, no hardware needed)
dce112_compressor_construct() (dce112_compressor.c:787-829) runs on a
kzalloced object (created at :831-841), so every field starts at 0:
- Dead guard.
if (compressor->base.memory_bus_width == 64)at :802 is evaluated while the field is still 0; it is assigned its real value,ctx->asic_id.vram_width, only afterwards at :810. The guard whose comment says "if only 1 [DRAM channel] then LPT should not be supported" can therefore never fire βLPT_SUPPORTstays set on 64-bit-bus ASICs. - Wiped field.
lpt_channels_num = 1at :797 ("For DCE 11 always use one DRAM channel for LPT") is unconditionally overwritten to 0 at :818 (part of the zero-everything block). No other code in the tree writes the field (verified acrosssys/dev/drm/amd/display/dc/), so at the only read β the switch indce112_compressor_program_lpt_control()(:675-691) β it is always 0: the sole validcase 1:is unreachable and the switch always falls todefault(:686), leavingLOW_POWER_TILING_MODEunprogrammed (stale HW value).
Knock-on (same construct + tree-wide grep): banks_num, raw_size,
channel_interleave_size, dram_channels_num are zeroed (:814-817) and
never set anywhere, so lpt_size_alignment() (:98-103) always returns 0:
rows_per_channel stays 0 (:708-714) and the compressed-surface address
alignment is skipped (:500-505) β while dce112_compressor_enable_lpt()
(:594-651) can still arm LPT (LOW_POWER_TILING_ENABLE=1 at :645-650,
GMCON_LPT_TARGET = stale_mode+1 at :628-640) whenever FBC+LPT come up on
eDP (dce110_hw_sequencer.c:enable_fbc β enable_lpt when LPT_SUPPORT &&
paths_num<2 && wh β€ 25601600; fbc_gpu_addr IS set at runtime by
amdgpu_dm.c:376, so the path is live on real hardware).
Verification performed
construct_order.cβ a statement-for-statement userspace mirror of construct() + the switch β built and run on the audit guest (host has no compiler): both proofs hold,RESULT: DEFECT CONFIRMED, exit 0 (run.log). This is a logic proof, not a kernel-path execution.- Kernel-path execution is impossible on this guest: it has no AMD GPU
(QEMU virtio only), so the DRM register path cannot run. Status therefore
not_reproduced/impact none(the orchestrator's status enum has no feature-absent value;not_testableis disallowed). - Reachability is modeset-gated (
/dev/dri, typically root/operator), eDP - FBC-memory + single-display preconditions further narrow it, and the consequence is register misconfiguration on the GPU (display underflow / possible hang β speculative), not kernel memory corruption. Filed at Info severity with certain confidence in the (provable) logic defect itself.
Fix validation
fix.diff (minimal reorder: assign memory_bus_width before the ==64
check; set lpt_channels_num = 1 after the zeroing block instead of wiping
it) β cannot be kernel-validated on this guest (driver cannot attach), so
fix_status = not_testable. The diff was authored against the pristine
sys/ tree and applies by construction.
Guest state
Guest used only to compile/run the userspace mirror; returned clean via
vm.sh reset with-src after evidence capture.
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_testableSingle-fix kernel validation impossible: the amd drm driver cannot attach to the QEMU virtio guest, so neither baseline nor patched kernels can exercise the path. fix.diff is a minimal reorder authored against the pristine sys/ tree (applies by construction).
fix.diff (assign memory_bus_width before the 64-bit-bus guard; stop wiping lpt_channels_num β set it to 1 after the zeroing block)
Confirmed kernel references
- sys/dev/drm/amd/display/dc/dce112/dce112_compressor.c:797
- sys/dev/drm/amd/display/dc/dce112/dce112_compressor.c:802
- sys/dev/drm/amd/display/dc/dce112/dce112_compressor.c:810
- sys/dev/drm/amd/display/dc/dce112/dce112_compressor.c:818
- sys/dev/drm/amd/display/dc/dce112/dce112_compressor.c:675
- sys/dev/drm/amd/display/dc/dce112/dce112_compressor.c:645
Detail
Evidence (decisive lines)
run.log (PROOF-1 OK / PROOF-2 OK / KNOCK-ON alignment=0 / RESULT: DEFECT CONFIRMED, exit 0); README.md path:line walkthrough; fix.diff
PoC changes
n/a (demonstrator authored fresh; no seed PoC existed)
Verified recommended fix
In dce112_compressor_construct(), assign memory_bus_width from ctx->asic_id.vram_width BEFORE the ==64 check, and set lpt_channels_num=1 after the zeroing block instead of wiping it (fix.diff).
Verdict
Info-severity init-order defects, both provable statically and confirmed by a host-side logic mirror (construct_order.c): (1) dce112_compressor_construct() evaluates the 'single DRAM channel => no LPT' guard (memory_bus_width == 64, :802) while the field is still 0 (kzalloc'd object; assigned from asic_id.vram_width only at :810) β the guard is dead and LPT_SUPPORT is never cleared on 64-bit-bus ASICs; (2) lpt_channels_num=1 set at :797 is unconditionally wiped to 0 at :818, making the only valid case of the program_lpt_control() switch (:675-691) unreachable β LOW_POWER_TILING_MODE is never programmed. Knock-on: banks_num/raw_size/dram_channels_num are never set anywhere in the tree, so lpt_size_alignment() is always 0 and enable_lpt() (:594-651) can arm LPT (LOW_POWER_TILING_ENABLE=1, :645-650) with ROWS_PER_CHAN=0 and a stale MODE whenever FBC+LPT come up on eDP. Runtime kernel-path reproduction is impossible on the audit guest (no AMD GPU; QEMU virtio only), hence not_reproduced/impact none; on-HW consequence (display underflow / GPU hang on affected Polaris eDP systems) is speculative and requires modeset privilege (/dev/dri).
No comments yet.