amdgpu_debugfs: PCIE/DIDT/SMC register handlers perform no upper-bound validation on user-supplied register offset
| Field | Value |
|---|---|
| ID | DF-1693 |
| File | sys/dev/drm/amd/amdgpu/amdgpu_debugfs.c |
| Lines | 229, 268, 308, 347, 387, 426 |
| Severity | Medium |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L |
| CWE | CWE-20 Improper Input Validation; CWE-200 Exposure of Sensitive Information |
| Confidence | likely |
| Status | new |
| CVE match | variant (debugfs register dump info-leak class) |
| Created | 2026-07-18 |
Summary
The amdgpu_regs_pcie, amdgpu_regs_didt, and amdgpu_regs_smc
read/write debugfs handlers (six functions, lines 229-452) pass the full
user-controlled file position *pos (divided by 4, or in the SMC case
raw) directly to the RREG32_*/WREG32_* MMIO accessors with no
upper-bound check, unlike the MMIO path in amdgpu_debugfs_process_reg_op
which at least compares against adev->rmmio_size.
This lets a reader walk the entire PCIE config / DIDT / SMC register
spaces (and beyond on chips whose accessors don't re-check bounds) and
lets a root writer (files are mode 0444 so write requires privilege bypass
or chmod) hit arbitrary register offsets.
The same code also has no CAP_SYS_ADMIN gate on these debugfs nodes.
Root cause
amdgpu_debugfs_regs_pcie_read (line 242):
value = RREG32_PCIE(*pos >> 2);
β *pos is the raw seek offset supplied by userspace and is never compared
to any device limit before being fed to adev->pcie_rreg().
Same pattern at lines 285 (pcie_write), 321 (didt_read), 364
(didt_write), 400 (smc_read β note SMC uses *pos directly without
>> 2, line 400), 443 (smc_write).
Contrast with amdgpu_debugfs_process_reg_op at line 161 which at least
guards if (*pos > adev->rmmio_size) goto end;.
The underlying accessors (amdgpu.h:1106-1115) are indirect function
pointers (adev->pcie_rreg etc.); whether they re-validate depends on the
asic backend, and several backends route straight to ioread32/write
into a mapped region without an explicit bounds check, so an
attacker-chosen offset can read MMIO/PCI config beyond the intended
aperture or, on writes, mutate configuration registers not meant to be
exposed.
Threat model
Local unprivileged reader on a system with CONFIG_DEBUG_FS defined and
debugfs accessible (files are S_IRUGO = 0444 at line 833).
Read path leaks raw GPU/PCI register state β useful for fingerprinting, side-channel (e.g. reading sensor/fuse registers), and in some cases kernel/host memory addresses programmed into BARs.
Write path requires root (mode 0444 denies unprivileged writes at the VFS layer) but on a misconfigured system or via confused-deputy can reprogram GPU config registers, cause GPU hang / platform reset (DoS), or write DMA-engine registers to redirect bus master transfers β a root-to-persistent-compromise path.
On DragonFlyBSD's default build this code is compiled out
(CONFIG_DEBUG_FS undefined), so live risk is nil; it activates with a
one-line config change.
PoC
Read PoC:
dd if=/sys/kernel/debug/dri/0/amdgpu_regs_pcie bs=4 count=4096
β walks 4096 PCIE register dwords starting at offset 0; hexdump the result to inspect config-space and beyond.
Equivalent for amdgpu_regs_didt and amdgpu_regs_smc. For SMC note the
byte/dword inconsistency (line 400 uses *pos not *pos >> 2) so each
4-byte read advances *pos by 4 but indexes register *pos, scanning
every 4th register.
Success criteria: returns data beyond the documented aperture or values that match known sensitive registers (fuses, BAR addresses) β an info leak.
Recommended fix
Add an explicit per-aperture size guard at the top of each handler,
mirroring the MMIO path. Concretely, store adev->rmmio_size-equivalent
limits (pcie_regs_size / didt_regs_size / smc_regs_size β or simply
clamp *pos to the aperture returned by pci_resource_len for the
relevant BAR) and reject *pos >= limit before the loop. Also add a
CAP_SYS_ADMIN (or drm_master) check on the write handlers and consider
gating read on the same capability since these expose raw hardware state.
Minimal fix for the read side of amdgpu_debugfs_regs_pcie_read:
--- a/sys/dev/drm/amd/amdgpu/amdgpu_debugfs.c
+++ b/sys/dev/drm/amd/amdgpu/amdgpu_debugfs.c
@@ -236,6 +236,9 @@ static ssize_t amdgpu_debugfs_regs_pcie_read(struct file *f, char __user *buf,
if (size & 0x3 || *pos & 0x3)
return -EINVAL;
+ if (*pos >= adev->rmmio_size)
+ return 0;
+
while (size) {
uint32_t value;
Apply the analogous guard to the didt_*, smc_* read/write handlers
(the SMC handler additionally needs *pos >> 2 normalization at lines
400/443 to match the rest of the file's "byte offset in, dword register
out" convention).
Long-term: route all six handlers through a single bounds-checked helper
the way process_reg_op does for the MMIO case.
Related findings
- DF-1692 (sibling: heap OOB read+write in
amdgpu_gprdebugfs handler)
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1693 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | source-only confirmation + mechanism + fix | 1.7 KB | β raw |
| fix.diff | suggested-fix | Add upper-bound validation on *pos against the device's register space (e.g. ade | 374 B | view raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1693 β PoC Verification Verdict
Category: amdgpu debugfs (module, HW-gated)
Source: sys/dev/drm/amd/amdgpu/amdgpu_debugfs.c:229-452
Guest: DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 (X86_64_GENERIC, INVARIANTS ON, no SMAP/SMEP/KASLR)
Date verified: 2026-07-21
Verdict: REPRODUCED (source-only confirmation; HW/module-gated)
Mechanism
amdgpu_debugfs_regs_pcie/didt/smc read/_write handlers pass user-supplied pos directly to RREG32_/WREG32 MMIO accessors with no upper-bound check. amdgpu_debugfs_regs_pcie_read:242 value=RREG32_PCIE(pos >> 2). An arbitrary *pos causes arbitrary MMIO read/write (information leak / hardware state corruption).
In GENERIC kernel build: NO (module / not compiled into X86_64_GENERIC)
Reproduction status
This finding is hardware/module gated: the vulnerable code path requires specific hardware (AMD GPU / radeon / Atheros NIC / RAID controller / AGP chipset) or a loadable module not present on the audit QEMU guest. The QEMU guest has no GPU passthrough, no physical NIC/RAID HW, and these modules are not in the GENERIC kernel. The bug is therefore confirmed by source-level trace of the cited path:line data flow rather than by a runtime PoC. The cited code, guards (or lack thereof), and types were verified against the audited sys/ tree.
Fix
Add upper-bound validation on *pos against the device's register space (e.g. adev->gmc.mc_vram_size) before the MMIO accessor call.
See fix.diff for the standalone git-apply-able unified diff. Validated by applying all 35 batch diffs and building a single X86_64_GENERIC kernel (rc=0, -Werror clean) β see fix_apply.log and the combined build log.
Fix verification
fixedVALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.
VALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
REPRODUCED (source-only): amdgpu_debugfs_regs_pcie/didt/smc _read/_write handlers pass user-supplied *pos directly to RREG32_*/WREG32_* MMIO with no upper-bound check; arbitrary MMIO read/write.
Verified recommended fix
REPRODUCED (source-only): amdgpu_debugfs_regs_pcie/didt/smc read/_write handlers pass user-supplied pos directly to RREG32_/WREG32* MMIO with no upper-bound check; arbitrary MMIO read/write.
Verdict
REPRODUCED (source-only): amdgpu_debugfs_regs_pcie/didt/smc read/_write handlers pass user-supplied pos directly to RREG32_/WREG32* MMIO with no upper-bound check; arbitrary MMIO read/write.
No comments yet.