trinity_acquire_mutex() is void and cannot fail: on SMC mutex-grant timeout it silently returns without the mutex, and all six live callers then program SMC registers assuming exclusivity against the SMC firmware
Summary
The grant poll falls out with the request left asserted and the failure unreportable by construction - the six live callers issue SMC register programs immediately, interleaving with the SMC firmware's own accesses. Unique to Trinity in the radeon SMC family; kernel-side smc_idx_lock present (checked). int-return + caller-bail fix (3 files, apply-clean).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5206 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.6 KB | β raw | |
| VERDICT.md | β | 2.9 KB | β raw | |
| analysis.txt | β | 2.4 KB | view raw | |
| fix.diff | β | 2.8 KB | view raw | |
| verdict.json | β | 3.2 KB | view raw | |
| manifest.json | β | 1.1 KB | view raw |
DF-5206 β trinity_acquire_mutex() cannot fail: silently proceeds without the SMC mutex on timeout
File: sys/dev/drm/radeon/trinity_smc.c:112-122 (+ all call sites in trinity_dpm.c)
Severity: Info β hardware-reliability fail-open, unique to Trinity in the radeon
SMC family (kv/si/ci/sumo have no equivalent void mutex-grant wait).
The defect
trinity_acquire_mutex() requests the SMC firmware mutex by writing 1 to
SMC_INT_REQ (register 0x220, trinityd.h:167) and polls for the grant:
void trinity_acquire_mutex(struct radeon_device *rdev) /* :112 β VOID */
{
int i;
WREG32(SMC_INT_REQ, 1); /* :116 */
for (i = 0; i < rdev->usec_timeout; i++) { /* :117 β up to 100 ms */
if ((RREG32(SMC_INT_REQ) & 0xffff) == 1) /* :118 */
break; /* grant */
udelay(1);
}
} /* :122 β timeout: silent return */
If the SMC firmware never grants the mutex (busy, wedged, or in a failure
state), the function returns anyway after the 100 ms poll, without the
mutex and without any way to tell the caller: the return type is void, so the
failure is unreportable by construction. Every caller then programs SMC
registers as if exclusivity vs the SMC firmware were held:
trinity_dpm_enable_bapmβ trinity_dpm.c:1099trinity_dpm_enableβ trinity_dpm.c:1109trinity_dpm_late_enableβ trinity_dpm.c:1138trinity_dpm_disableβ trinity_dpm.c:1158trinity_dpm_set_power_stateβ trinity_dpm.c:1258trinity_dpm_setup_asicβ trinity_dpm.c:1288- (a 7th site at :1300 is inside
#if 0β dead code)
This mutex exists precisely to serialize the host driver against the SMC firmware's own accesses during DPM reprogramming; proceeding without it lets host register sequences interleave with SMC-internal ones β corrupted SMC register programs (clock/voltage/power-gating misprogramming). Reliability defect; no memory-safety primitive and no attacker-controlled input.
Why untested (see VERDICT.md)
Requires AMD Trinity hardware with an SMC that refuses the mutex grant. The
audit QEMU guest has no radeon GPU; Info-class finding exempt from guest
verification per the audit contract. fix.diff (API change: int return +
-ETIME, all live call sites updated to bail out) is authored and
git apply --check-verified, not hardware-validated.
Fix
fix.diff β trinity_acquire_mutex() returns int (0 on grant,
-ETIME after backing off the request), prototype in trinity_dpm.h updated,
and all six live call sites in trinity_dpm.c check the result and bail out.
DF-5206 VERDICT β UNTESTED (hardware-absent; Info-class, Phase V exempt)
Status: untested. Reproduced: no. Impact: none (code-shape defect is certain; runtime consequence requires Trinity hardware to demonstrate).
What is certain (static proof)
sys/dev/drm/radeon/trinity_smc.c:112βvoid trinity_acquire_mutex(...). A void return means a failed SMC-mutex acquisition is structurally unreportable: no caller can ever know the grant timed out.trinity_smc.c:117-121β the grant poll runs at mostrdev->usec_timeoutiterations (100 ms,RADEON_MAX_USEC_TIMEOUT, radeon.h:131); on exhaustion the function silently returns at :122 with the mutex ungranted and the request line left asserted.- All six live call sites (
trinity_dpm.c:1099,1109,1138,1158,1258,1288) proceed to program SMC registers immediately after the call, assuming exclusivity against the SMC firmware. The one remaining reference (:1300) is inside#if 0dead code. - The kernel-side indirect-access race class (DF-2135) was checked and is NOT
present on Trinity:
tn_smc_rreg/tn_smc_wreg(sys/dev/drm/radeon/ni.c:41-58) holdrdev->smc_idx_lockfor the TN_SMC_IND_INDEX/DATA pairs. Whattrinity_acquire_mutexarbitrates is host-vs-SMC-firmware serialization, which that lock does not cover β so the gap is real, not a duplicate of DF-2135/DF-2130.
Why not guest-verified
- Reachable only on AMD Trinity (HD 7xxx APU) ASICs; requires an SMC firmware that refuses/never grants the mutex (busy or wedged). The QEMU audit guest has no radeon GPU; no ioctl/sysctl/other software path reaches these functions β they run at DPM init/enable/disable/set-power-state from the driver's PM core.
- Info severity / no attacker-controlled input β outside the Phase V mandate (Critical/High, memcorrupt/privesc/hammer2 buckets, or trivial local PoC).
- Therefore: no PoC authored,
attempts: 0,code_hashhashesanalysis.txt.
Fix
fix.diff (3 files):
- trinity_smc.c: int trinity_acquire_mutex() β returns 0 on grant,
-ETIME on timeout after de-asserting the request and logging;
- trinity_dpm.h:131: prototype updated;
- trinity_dpm.c: all six live call sites check the result and bail
(void functions return, int functions return -ETIME).
git apply --check verified clean against the pristine tree; never applied to
sys/; not hardware-validated (fix_status: not_testable).
Escalation assessment
None. No memory-safety primitive; the defect degrades hardware PM sequencing only. No unprivileged input touches this path.
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 authored against the read-only sys/ tree (trinity_smc.c + trinity_dpm.h + trinity_dpm.c, six live call sites updated) and verified with git apply --check. Cannot be hardware-validated: baseline cannot be reproduced without Trinity ASICs, so fix_status is not_testable rather than fixed.
fix.diff in this directory; git apply --check clean against the pristine tree (verified during authoring); no kernel build attempted since runtime validation is impossible without TN hardware.
Confirmed kernel references
Detail
Evidence (decisive lines)
analysis.txt (annotated walkthrough); fix.diff (3-file API change: int return + -ETIME, prototype, six live call sites; git apply --check clean); decisive lines trinity_smc.c:112 (void return), :117-121 (poll loop), :122 (silent fall-through); consumers trinity_dpm.c:1099,1109,1138,1158,1258,1288; register def trinityd.h:167; timeout radeon.h:131; negative check ni.c:41-58
PoC changes
No PoC authored: only reachable on AMD Trinity ASICs through the radeon DPM core (init/enable/disable/set-power-state); the QEMU audit guest has no radeon GPU and there is no software-only trigger. Static proof + fix.diff shipped instead.
Verified recommended fix
Make trinity_acquire_mutex() return int (0 on grant, -ETIME after de-asserting the request) and have every live caller bail out on failure (see fix.diff).
Verdict
Static fail-open proven at sys/dev/drm/radeon/trinity_smc.c:112-122: trinity_acquire_mutex() is void, polls the SMC firmware mutex grant (SMC_INT_REQ) for up to 100 ms, and on timeout silently returns without the mutex and with the failure unreportable by construction; all six live call sites in trinity_dpm.c (1099, 1109, 1138, 1158, 1258, 1288) then program SMC registers assuming exclusivity against the SMC firmware. Runtime demonstration requires Trinity hardware with a non-granting SMC β absent from the audit QEMU guest β and as an Info-class hardening finding with no attacker-controlled input it is outside the Phase V mandate, so no PoC was authored or run. The DF-2135/DF-2130 kernel-side idx-lock class was checked and is NOT present on Trinity (tn_smc_rreg/tn_smc_wreg hold smc_idx_lock, ni.c:41-58): this gap is host-vs-SMC-firmware serialization only.
No comments yet.