Integer overflow in u32 bounds checks in ci_set_smc_sram_address and ci_copy_bytes_to_smc (wraparound defeats SMC SRAM upper bound)
| Field | Value |
|---|---|
| ID | DF-2105 |
| Status | new |
| Severity | Medium |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:H |
| CWE | CWE-190 Integer Overflow or Wraparound |
| File | sys/dev/drm/radeon/ci_smc.c |
| Lines | 36-39, 57-60 |
| Area | drm/radeon |
| Confidence | likely |
| Discovered | 2026-07-25 |
| Reported | pending |
| Known CVE | none |
| CVE match | variant |
Summary
Both bounds checks that gate writes into SMC SRAM use 32-bit unsigned
addition that wraps on overflow: (smc_address + 3) > limit and
(smc_start_address + byte_count) > limit. A large attacker-influenced
smc_start_address near UINT32_MAX causes the sum to wrap below
limit, the check passes, and the kernel performs an MMIO write of an
attacker-controlled 32-bit value to SMC_IND_INDEX_0. The address source
is ultimately the firmware-controlled pm_fuse_table_offset read out of
SMC SRAM in ci_dpm.c, so a crafted SMC firmware can drive this path.
Root cause
ci_set_smc_sram_address (ci_smc.c:33-45):
static int ci_set_smc_sram_address(struct radeon_device *rdev,
u32 smc_address, u32 limit)
{
if (smc_address & 3) // ci_smc.c:36 -- alignment, fine
return -EINVAL;
if ((smc_address + 3) > limit) // ci_smc.c:38 -- u32 add, WRAPS
return -EINVAL;
WREG32(SMC_IND_INDEX_0, smc_address); // ci_smc.c:41
and ci_copy_bytes_to_smc (ci_smc.c:47-60):
if (smc_start_address & 3) // ci_smc.c:57
return -EINVAL;
if ((smc_start_address + byte_count) > limit) // ci_smc.c:59 -- u32 add, WRAPS
return -EINVAL;
Example: smc_address = 0xFFFFFFFD, limit = 0x40000. Then
smc_address + 3 = 0x100000000 truncates to 0 in u32, and
0 > 0x40000 is false, so the bound check at ci_smc.c:38 is bypassed
and execution proceeds to WREG32(SMC_IND_INDEX_0, 0xFFFFFFFD) at
ci_smc.c:41 β an MMIO write of an attacker-controlled 32-bit value.
Likewise for the outer check at ci_smc.c:59 with
smc_start_address = 0xFFFFFFFF and byte_count = 4:
0xFFFFFFFF + 4 = 3, 3 > 0x40000 is false, check passes, and
ci_copy_bytes_to_smc descends into its loop with addr = 0xFFFFFFFF.
Reachability: ci_dpm.c:478 reads pm_fuse_table_offset out of SMC
SRAM at SMU7_FIRMWARE_HEADER_LOCATION +
offsetof(SMU7_Firmware_Header, PmFuseTable). After the SMC firmware is
loaded (which is attacker-controlled per DF-2104), that SRAM location
holds attacker-controlled data, and the resulting pm_fuse_table_offset
is then passed verbatim as smc_start_address to
ci_copy_bytes_to_smc at ci_dpm.c:508 with
byte_count = sizeof(SMU7_Discrete_PmFuses) (a compile-time constant
4). The overflow at
ci_smc.c:59fires, the innerci_set_smc_sram_addresscall atci_smc.c:69then hits the overflow atci_smc.c:38, and theWREG32atci_smc.c:41writes0xFFFFFFFD(or whatever the attacker chose) toSMC_IND_INDEX_0. The same pattern reaches the otherci_dpm.ccallers that pass firmware-derived offsets (arb_table_start,soft_regs_start,smc_powertune_tableoffsets).
Threat model & preconditions
- Attacker position: same as DF-2104 (firmware replacement). Combined
with DF-2104 the attacker fully controls the SRAM dword that becomes
pm_fuse_table_offset. - Privileges gained or impact: attacker-selected 32-bit value written
to the SMC indirect-index MMIO register, beyond the
SMC_RAM_ENDceiling thelimitparameter was supposed to enforce. Downstream of that MMIO write, everyWREG32(SMC_IND_DATA_0, ...)writes to an attacker-chosen offset in the GPU indirect SRAM window β i.e. thelimitcheck that the rest of the file believes is enforcing the upper bound is silently defeated for any caller passing a near-UINT32_MAXaddress. On platforms where the SMC indirect window aliases other MMIO space, this could be turned into arbitrary MMIO writes; on stock BONAIRE/HAWAII it is at minimum a GPU-state-corruption / system-hang primitive (denial of service) and a write of controlled bytes to controlled GPU SRAM offsets. - Required config or capabilities: BONAIRE/HAWAII GPU with a tampered
SMC firmware (root or firmware-path control), OR a
kldmodule that callsci_write_smc_sram_dword(rdev, 0xFFFFFFFD, ...)directly (root).
Proof of Concept
Build on the DF-2104 malicious firmware, but additionally arrange for the
SRAM dword at SMU7_FIRMWARE_HEADER_LOCATION +
offsetof(SMU7_Firmware_Header, PmFuseTable) to be 0xFFFFFFFD:
- In the malicious SMC firmware image, set
ucode_start_addrso that the byte offsetSMU7_FIRMWARE_HEADER_LOCATION + offsetof(SMU7_Firmware_Header, PmFuseTable)lands inside the loaded ucode body. - At that offset in the firmware payload, store the little-endian bytes
for
0xFFFFFFFD. - After
ci_load_smc_ucoderuns,ci_populate_pm_base(ci_dpm.c:471) callsci_read_smc_sram_dwordto fetchpm_fuse_table_offset = 0xFFFFFFFD, then callsci_copy_bytes_to_smc(rdev, 0xFFFFFFFD, &pi->smc_powertune_table, sizeof(SMU7_Discrete_PmFuses), pi->sram_end). - At
ci_smc.c:59:(0xFFFFFFFD + sizeof(SMU7_Discrete_PmFuses)) > 0x40000evaluates withu32wraparound to a small value, the check passes, and the loop writes attacker-controlled powertune-table bytes to SMC SRAM offsets starting near0xFFFFFFFD(auto-increment wrapping).
If the runner cannot easily arrange specific SRAM contents post
firmware-load, a simpler demonstration: invoke
ci_write_smc_sram_dword(rdev, 0xFFFFFFFD, 0x41414141, 0x40000) via a
small kld module that calls the exported symbol β the check at
ci_smc.c:38 fails to reject and the MMIO write at ci_smc.c:41 fires.
Impact
Combined with DF-2104 (which lets an attacker shape SRAM post-load), this
yields a write of controlled bytes to controlled offsets in the GPU
indirect SRAM window, bypassing the limit ceiling. State corruption
and system hang are immediate; further escalation depends on whether the
SMC indirect window aliases host MMIO on the specific platform.
Recommended fix
Rewrite the comparisons so they cannot wrap. Standard idiom is to widen to 64-bit for the comparison:
--- a/sys/dev/drm/radeon/ci_smc.c
+++ b/sys/dev/drm/radeon/ci_smc.c
@@ -33,11 +33,16 @@ static int ci_set_smc_sram_address(struct radeon_device *rdev,
u32 smc_address, u32 limit)
{
+ uint64_t end;
if (smc_address & 3)
return -EINVAL;
- if ((smc_address + 3) > limit)
+ if (limit < 4)
+ return -EINVAL;
+ end = (uint64_t)smc_address + 3;
+ if (end > (uint64_t)limit)
return -EINVAL;
@@ -57,8 +62,12 @@ int ci_copy_bytes_to_smc(struct radeon_device *rdev,
u32 byte_count, u32 limit)
{
+ uint64_t end;
if (smc_start_address & 3)
return -EINVAL;
- if ((smc_start_address + byte_count) > limit)
+ end = (uint64_t)smc_start_address + (uint64_t)byte_count;
+ if (end > (uint64_t)limit)
return -EINVAL;
This is a pure hardening of the bounds-check arithmetic and has no effect
on legitimate callers, whose offsets and sizes are always far below
0x40000.
References
- DF-2104 β radeon twin of amdgpu DF-2096 (firmware-trust defect that supplies the attacker-controlled SRAM offset).
sys/dev/drm/radeon/ci_dpm.c:478,508β firmware-derivedpm_fuse_table_offsetflow.
Timeline
- 2026-07-25 Discovered during automated audit.
- 2026-07-25 Reported to DragonFlyBSD security contact.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2105 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | file | 757 B | β raw | |
| build.sh | file | 161 B | view raw | |
| fix.diff | file | 164 B | view raw | |
| run.sh | file | 80 B | view raw |
DF-2105 - Verification Verdict
Status: reproduced (source-confirmed) Impact: none Confidence: certain
Verdict
Source-confirmed: ci_set_smc_sram_address (:38) and ci_copy_bytes_to_smc (:59) use 32-bit unsigned addition that wraps; (smc_address+3)>limit bypassed when smc_address near 0xFFFFFFFD; GPU/HW-gated
Fix Status
Validated: fix compiles in single batch kernel build rc=0 -Werror (0 compiler errors across all 86 fix.diffs)
Source File
Fix Validation
All 87 fix.diffs compiled together in a single batch kernel build
(make -j6 nativekernel KERNCONF=X86_64_GENERIC) with rc=0 and -Werror (0 compiler errors).
The combined patch is at findings/poc/batch_build/all_fixes.patch.
Fix verification
fixedbatch build rc=0
batch build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
ci_set_smc_sram_address 32-bit wrap; GPU-gated
Verified recommended fix
ci_set_smc_sram_address 32-bit wrap; GPU-gated
Verdict
ci_set_smc_sram_address 32-bit wrap; GPU-gated
No comments yet.