β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4226

Output-lock asymmetry: radeon_legacy_tmds_int_commit and radeon_legacy_tv_dac_commit re-LOCK the output instead of unlocking after modeset

Summary

prepare() locks the output; commit() must release. All three sibling commit functions pass false, but tmds_int_commit and tv_dac_commit pass true - copy-paste of the prepare side. The output PLL lock stays asserted after every DVI/TV modeset, inhibiting subsequent DPMS/modeset register programming by the BIOS command processor (upstream passes false in both). Functional display DoS on r100-r500; register-bit only. Four-token flip fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4226 Β· 4 files
FileTypeDescriptionSize
README.md β€” 1.6 KB ↓ raw
VERDICT.md β€” 2.0 KB ↓ raw
fix.diff β€” 924 B view raw
verdict.json β€” 2.2 KB view raw

DF-4226 β€” radeon legacy commit paths re-LOCK the output instead of unlocking

What (pure source-level finding, hardware-gated)

The prepare/commit protocol of the DRM encoder helpers is: prepare() locks the output PLL against BIOS command-processor updates (radeon_atom_output_lock(encoder, true)), commit() must release it (..., false).

In sys/dev/drm/radeon/radeon_legacy_encoders.c two commit functions pass true (lock) again:

  • radeon_legacy_tmds_int_commit() β€” drm lines 775, 777
  • radeon_legacy_tv_dac_commit() β€” drm lines 1146, 1148

Every sibling in the same file does it correctly: - radeon_legacy_lvds_commit() β€” lines 169/171 (false) - radeon_legacy_primary_dac_commit() β€” lines 578/580 (false) - radeon_legacy_tmds_ext_commit() β€” lines 940/942 (false)

Consequence: after any modeset on the internal-TMDS (DVI) or TV-DAC encoder, the output remains locked; subsequent DPMS/modeset PLL programming via the BIOS command tables is inhibited β€” persistent display malfunction (blank output / failed modesets) until re-init. This is a functional DoS of the display pipeline; no memory-safety impact (register bit only). Upstream Linux fixed both call sites to false.

Reproduce

Not reproducible on the QEMU guest: requires r100–r500 radeon hardware (guest has Bochas VGA, no DRM device). Source-level certainty: the asymmetry is self-evident against the three correct siblings in the same file, and prepare() at lines 762/764 (tmds_int) and 1133/1135 (tv_dac) locks with true β€” commit() must mirror with false.

Fix

true β†’ false at the four cited lines (see fix.diff).

VERDICT.md
↓ download raw

DF-4226 β€” VERDICT

Status: untested β€” hardware-gated (requires ATI r100–r500 GPU; the QEMU guest has Bochs VGA 0x1234:1111, no /dev/dri, and radeon.ko cannot attach). Source-level certainty is complete; no runtime behavior needs to be observed to establish the defect.

Why this is certain from source alone

  • The helper contract is symmetric: prepare() calls radeon_{atom,combios}_output_lock(encoder, true) to lock the output during reprogramming (radeon_legacy_encoders.c:762/764 for tmds_int, 1133/1135 for tv_dac); commit() must call the same function with false to release the lock. Three of five legacy encoders in this very file do exactly that (lvds: 168-171, primary_dac: 577-580, tmds_ext: 939-942).
  • radeon_legacy_tmds_int_commit() (768-778) and radeon_legacy_tv_dac_commit() (1139-1149) instead call the lock function with true β€” a copy-paste of the prepare() side. The output PLL lock bit stays asserted after the modeset completes.
  • Upstream Linux drivers/gpu/drm/radeon/radeon_legacy_encoders.c passes false in both commit functions; the DragonFly tree diverges.

Impact

Functional display DoS on affected hardware after a DVI/TV modeset (output stays locked; later DPMS/modeset register updates via the BIOS command processor fail). Triggering requires DRM master (modeset). No memory corruption, no info leak β€” register-bit-only defect, hence Low.

Fix

fix.diff flips the four true arguments to false (lines 775, 777, 1146, 1148). Fix boot validation is not testable on this guest (no radeon GPU); the change restores exact parity with the three correct sibling functions.

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_testable
baseline no→ patch + rebuild →patched clean

No radeon hardware on the guest; fix is a four-argument change restoring exact parity with the three correct sibling commit functions in the same file.

fix.diff; source comparison in VERDICT.md
↓ fix.diffper-fix-DF-4226

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md side-by-side of the five prepare/commit pairs in the file; fix.diff flips the four arguments to false, restoring parity with siblings and upstream Linux.

PoC changes

n/a (source-level finding)

Verified recommended fix

Pass false to radeon_{atom,combios}_output_lock in radeon_legacy_tmds_int_commit and radeon_legacy_tv_dac_commit (lines 775/777/1146/1148).

Verdict

Hardware-gated functional defect (no memory-safety impact, hence impact=none): radeon_legacy_tmds_int_commit (radeon_legacy_encoders.c:775/777) and radeon_legacy_tv_dac_commit (:1146/1148) call radeon_{atom,combios}_output_lock with true (LOCK) where the prepare/commit contract and all three sibling commit functions in the same file (lvds:169, primary_dac:578, tmds_ext:940) pass false (UNLOCK). After a DVI/TV modeset the output stays locked and later DPMS/modeset register programming is inhibited β€” display DoS until re-init, triggerable by a DRM master. Requires r100-r500 hardware; the QEMU guest has Bochs VGA and no DRM device, so no runtime test is possible or needed: the asymmetry is fully established from source.