transmitter_control_v1_6 applies 16-bit deep-color conversions to the 32-bit ulSymClock field β truncated transmitter symbol clock for HDMI deep color > 655.35 MHz
Summary
DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_6.ulSymClock is ULONG (32-bit, 10KHz units), but the HDMI deep-color ratio block (copy-pasted from v1_5 where the field is USHORT) wraps it in le16_to_cpu/cpu_to_le16 - truncating input and result to 16 bits. Any deep-color-corrected 10KHz value > 65535 is wrong: guest replica shows 4/5 realistic modes corrupt (4K60 10bpc 74250->8714, 12bpc 89100->23564, 16bpc 118800->53264). Truncated value handed to the VBIOS. Also omits cpu_to_le32 on assignment. No memory-safety impact; display corruption/link-training failure. 32-bit-conversion fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4048 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.5 KB | β raw | |
| VERDICT.md | β | 2.5 KB | β raw | |
| symclock_trunc.c | β | 2.7 KB | view raw | |
| build.log | β | 11 B | view raw | |
| run.log | β | 1.4 KB | view raw | |
| env.txt | β | 188 B | view raw | |
| fix.diff | β | 1.2 KB | view raw | |
| manifest.json | β | 954 B | view raw | |
| verdict.json | β | 3.0 KB | view raw |
DF-4048 β transmitter_control_v1_6 applies 16-bit deep-color math to the 32-bit ulSymClock field
What
sys/dev/drm/amd/display/dc/bios/command_table.c:857 and :869-890
(transmitter_control_v1_6): the V1_6 transmitter parameter struct's symbol
clock is a 32-bit ULONG (DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_6,
sys/dev/drm/amd/include/atombios.h:1559-1575, "ULONG ulSymClock; // Symbol
Clock in 10Khz"), but the HDMI deep-color ratio code β copy-pasted from
transmitter_control_v1_5, where the field is a USHORT usSymClock β uses
the 16-bit helpers on it:
params.ulSymClock = cntl->pixel_clock/10; /* :857 - also no cpu_to_le32 */
...
case COLOR_DEPTH_101010:
params.ulSymClock =
cpu_to_le16((le16_to_cpu(params.ulSymClock) * 30) / 24); /* :873-874 */
case COLOR_DEPTH_121212: ... * 36 / 24 ... /* :877-878 */
case COLOR_DEPTH_161616: ... * 48 / 24 ... /* :881-882 */
le16_to_cpu(u32) truncates the input to 16 bits and cpu_to_le16(...)
truncates the result to 16 bits. Any symbol clock whose 10 KHz value
overflows 65535 is computed wrong β which includes common HDMI deep-color
modes (4K@60 10bpc needs 74250; 12bpc 89100; 16bpc 118800 β all > 65535).
The wrong ulSymClock is then handed to the VBIOS via
EXEC_BIOS_CMD_TABLE (:892), programming a corrupted transmitter symbol
clock (display artifacts / link training failure / black screen on the
affected mode). :857 also skips the endian conversion entirely (moot on
LE x86-64, wrong on BE).
Reproduce (guest, arithmetic replica)
cc -O2 -Wall -o symclock_trunc symclock_trunc.c ./symclock_trunc; echo EXIT=$? # exits 1 (defect present)
Expected output (run.log)
case in(10KHz) correct produced ok? 1080p60 8bpc (safe zone) 14850 14850 14850 yes 4K60 10bpc x30/24 59400 74250 8714 TRUNC 4K60 12bpc x36/24 59400 89100 23564 TRUNC 4K60 16bpc x48/24 59400 118800 53264 TRUNC HDMI1.4 4K30 16bpc x48/24 34000 68000 2464 TRUNC expected: all cases compute correctly; observed: 4/5 truncated (also: :857 assigns without any cpu_to_le32 conversion - moot on LE x86, wrong on BE) EXIT=1
4/5 realistic deep-color cases produce a wrong 32-bit field value from the kernel expression; only the sub-655.35 MHz 8bpc case survives.
DF-4048 VERDICT β 16-bit deep-color math on 32-bit ulSymClock (transmitter_control_v1_6)
Reproduced?
Yes, at the arithmetic level β the exact kernel expression
(cpu_to_le16((le16_to_cpu(params.ulSymClock) * N) / 24) on a ULONG field)
was replicated 1:1 in symclock_trunc.c and executed on the guest: 4/5
realistic HDMI deep-color symbol clocks come out wrong (see run.log;
exit 1). The downstream display impact (corrupted symbol clock handed to
VBIOS at command_table.c:892) is hardware-gated β the guest has no AMD GPU,
and v1_6 transmitter commands apply to DCE11/12-class parts (Polaris-era) β
so the persisted impact is none with the defect itself proven certain.
Root cause (path:line)
command_table.c:857βparams.ulSymClock = cntl->pixel_clock/10;assigned with nocpu_to_le32(moot on LE x86-64, wrong on BE).command_table.c:869-890β deep-color ratio block usesle16_to_cpu/cpu_to_le16(16-bit conversions) on the 32-bitulSymClockfield: copy-paste fromtransmitter_control_v1_5(command_table.c:811-828), where the field isUSHORT usSymClock(atombios.h:1477-1493). Both the input and the result are truncated to 16 bits.- Struct:
atombios.h:1559-1575βULONG ulSymClock; // Symbol Clock in 10Khzβ the deep-color-corrected value (e.g. 118800 for 4K@60 16bpc) legitimately exceeds 65535, so the truncation bites on real modes, not just laboratory ones.
Impact
Wrong transmitter symbol clock programmed for HDMI deep-color modes with effective clocks > 655.35 MHz on v1_6-transmitter ASICs β display corruption/link instability (local DoS of the display output, no memory safety impact, no info leak). Severity Low (specific hardware + specific modes).
Fix
Use the 32-bit conversions in v1_6 (and convert the initial assignment):
- params.ulSymClock = cntl->pixel_clock/10;
+ params.ulSymClock = cpu_to_le32(cntl->pixel_clock / 10);
...
- params.ulSymClock =
- cpu_to_le16((le16_to_cpu(params.ulSymClock) * 30) / 24);
+ params.ulSymClock =
+ cpu_to_le32((le32_to_cpu(params.ulSymClock) * 30) / 24);
(same for the 36/24 and 48/24 cases; fix.diff carries the full hunk).
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_testableKernel-build validation hardware-gated (no AMD GPU). fix.diff is a mechanical 16->32-bit conversion swap; the arithmetic replica's 'correct' column implements exactly the patched math (all 5 cases then compute correctly).
findings/poc/DF-4048/fix.diff; run.log 'correct' column = post-fix expected values
Confirmed kernel references
- sys/dev/drm/amd/display/dc/bios/command_table.c:857
- sys/dev/drm/amd/display/dc/bios/command_table.c:869
- sys/dev/drm/amd/display/dc/bios/command_table.c:873
- sys/dev/drm/amd/display/dc/bios/command_table.c:877
- sys/dev/drm/amd/display/dc/bios/command_table.c:881
- sys/dev/drm/amd/display/dc/bios/command_table.c:892
- sys/dev/drm/amd/include/atombios.h:1559
Detail
Evidence (decisive lines)
run.log (4/5 TRUNC rows incl. 74250->8714, 89100->23564, 118800->53264, 68000->2464; EXIT=1), VERDICT.md, atombios.h struct quote in README.md
PoC changes
n/a (authored fresh: exact-expression userland replica; kernel-side field is 32-bit so the le16 helpers truncate on both input and output)
Verified recommended fix
Use cpu_to_le32/le32_to_cpu for ulSymClock in transmitter_control_v1_6 (both the :857 assignment and the three deep-color cases); see fix.diff
Verdict
Defect reproduced at the arithmetic level on the guest: symclock_trunc.c replicates transmitter_control_v1_6's deep-color expressions 1:1 (cpu_to_le16((le16_to_cpu(params.ulSymClock)*N)/24) on the 32-bit ULONG ulSymClock, atombios.h:1559-1575) and shows 4/5 realistic HDMI deep-color clocks computed wrong (e.g. 4K60 16bpc: correct 118800, produced 53264; run.log, exit 1). The truncated ulSymClock is handed to the VBIOS via EXEC_BIOS_CMD_TABLE (command_table.c:892), programming a corrupted transmitter symbol clock -> display corruption/link failure for >655.35MHz deep-color modes on v1_6-transmitter DCE11/12 ASICs. Display-level manifestation is hardware-gated (no AMD GPU in guest), hence impact recorded as none; no memory-safety consequence (bounded field write inside the params struct). Also noted: :857 assigns ulSymClock without cpu_to_le32 (BE-only issue).
No comments yet.