Heap overflow in psp_asd_load via unvalidated asd_ucode_size from PSP ASD firmware header
| Field | Value |
|---|---|
| ID | DF-1854 |
| Status | new |
| Severity | Medium |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-787 Out-of-bounds Write; CWE-125 Out-of-bounds Read |
| File | sys/dev/drm/amd/amdgpu/amdgpu_psp.c |
| Lines | 254-255 |
| Area | dev/drm/amd (PSP ASD firmware loader) |
| Confidence | certain |
| Discovered | 2026-07-20 |
| Reported | pending |
| Known CVE | none |
| CVE match | variant |
Summary
psp_asd_load copies psp->asd_ucode_size bytes of ASD firmware into
psp->fw_pri_buf, a 1 MiB GTT buffer object, without ever validating that
asd_ucode_size <= PSP_1_MEG or that the source lies inside the firmware blob.
asd_ucode_size comes directly from the ucode_size_bytes field of the firmware
header (psp_v3_1.c:153, psp_v10_0.c:142) β a 32-bit value with no upper bound.
amdgpu_ucode_validate only checks fw->datasize == hdr->size_bytes; it does
not check ucode_size_bytes. A malformed ASD firmware therefore drives a
memcpy of up to 4 GiB into a 1 MiB kernel buffer. This is the same defect class
as DF-1838/DF-1839 but the vulnerable sink is in this file's psp_asd_load.
Root cause
amdgpu_psp.c:254-255:
memset(psp->fw_pri_buf, 0, PSP_1_MEG);
memcpy(psp->fw_pri_buf, psp->asd_start_addr, psp->asd_ucode_size);
fw_pri_buf is allocated at amdgpu_psp.c:350-354 with size PSP_1_MEG = 0x100000
(amdgpu_psp.h:34). psp->asd_ucode_size is assigned from a raw little-endian
uint32_t in the firmware header at psp_v3_1.c:153 and psp_v10_0.c:142:
adev->psp.asd_ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
The fields of struct common_firmware_header (amdgpu_ucode.h:26-37) are never
range-checked against fw->datasize or against PSP_1_MEG before this memcpy.
The validation in amdgpu_ucode_validate (amdgpu_ucode.c:251-260) only checks
that fw->datasize equals the header's self-declared size_bytes; an attacker
who controls the firmware file can set size_bytes = datasize (passes
validation) while setting ucode_size_bytes to anything up to 0xFFFFFFFF and
ucode_array_offset_bytes to anything, then the offset+size pair is never
reconciled against datasize.
Threat model & preconditions
- Attacker position: local privileged attacker (root) who can place or
replace an amdgpu ASD firmware image in the firmware search path
(
request_firmwarelookups foramdgpufw_<chip>_asdat psp_v3_1.c:141 and psp_v10_0.c:130), or any scenario shipping a malformed ASD blob (recovery image, initramfs, signed-but-corrupt vendor blob). - Privileges gained or impact: kernel heap corruption in the GTT domain: up
to 4 GiB of attacker bytes written past the 1 MiB
fw_pri_bo, overwriting whatever amdgpu buffer objects were allocated adjacent to it. Best case is a kernel panic (A:H); worst case is corruption of another GPU BO that is later read back as GPU command stream. The source-side OOB read of the firmware blob also leaks kernel memory adjacent to the firmware image. - Required config or capabilities:
device amdgpuon VEGA10/VEGA12 (psp_v3_1) or RAVEN (psp_v10_0) ASIC withfirmware.load_type = AMDGPU_FW_LOAD_PSP; root access to the firmware path. - Reachability: the overflow fires automatically the next time the amdgpu driver attaches the PSP IP block β at boot, at driver reload (kldload/kldunload), at resume from suspend, or after a GPU reset.
Proof of concept
PoC source: findings/poc/DF-1854/forge_asd.c
Build & run
cc -o forge_asd forge_asd.c # Install the forged ASD firmware in the loader path: sudo install -m 0644 amdgpufw_raven_asd /lib/firmware/amdgpu/ # Reload amdgpu or trigger resume: sudo kldunload amdgpu && sudo kldload amdgpu
Expected output
panic: vm_fault / kmem_malloc / TTM BO corruption backtrace through psp_asd_load -> memcpy
Impact
Medium-severity kernel heap overflow. Root-only trigger (firmware path write). The overflow fires on every PSP init for a planted malformed firmware, corrupts adjacent GTT buffer objects, and can panic the kernel or provide a write primitive into GPU command streams.
Recommended fix
Bound both the destination write and the source read against the firmware blob.
Validate asd_ucode_size against PSP_1_MEG in psp_asd_load, and validate
ucode_array_offset_bytes + ucode_size_bytes against fw->datasize in the
per-version init_microcode functions.
--- a/sys/dev/drm/amd/amdgpu/amdgpu_psp.c
+++ b/sys/dev/drm/amd/amdgpu/amdgpu_psp.c
@@ -238,6 +238,7 @@ static int psp_asd_init(struct psp_context *psp)
static int psp_asd_load(struct psp_context *psp)
{
int ret;
+ uint32_t asd_size;
struct psp_gfx_cmd_resp *cmd;
@@ -250,9 +251,18 @@ static int psp_asd_load(struct psp_context *psp)
if (!cmd)
return -ENOMEM;
+ if (psp->asd_ucode_size > PSP_1_MEG) {
+ DRM_ERROR("PSP ASD ucode size 0x%x exceeds 1 MiB buffer\n",
+ psp->asd_ucode_size);
+ kfree(cmd);
+ return -EINVAL;
+ }
+ asd_size = min_t(uint32_t, psp->asd_ucode_size, PSP_1_MEG);
+
memset(psp->fw_pri_buf, 0, PSP_1_MEG);
- memcpy(psp->fw_pri_buf, psp->asd_start_addr, psp->asd_ucode_size);
+ memcpy(psp->fw_pri_buf, psp->asd_start_addr, asd_size);
psp_prep_asd_cmd_buf(cmd, psp->fw_pri_mc_addr, psp->asd_shared_mc_addr,
- psp->asd_ucode_size, PSP_ASD_SHARED_MEM_SIZE);
+ asd_size, PSP_ASD_SHARED_MEM_SIZE);
Additionally, the asd_start_addr / ucode_array_offset_bytes pair must be
validated against fw->datasize in psp_v3_1_init_microcode (psp_v3_1.c:150-155)
and psp_v10_0_init_microcode (psp_v10_0.c:139-144): reject the firmware if
le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
le32_to_cpu(hdr->header.ucode_size_bytes) > adev->psp.asd_fw->datasize.
References
- DF-1838: same defect class in
psp_v3_1_init_microcode(sysdrv/sos firmware). - DF-1839: sibling underflow in
psp_v3_1_compare_sram_data. - Outer-size-only validation:
amdgpu_ucode_validateamdgpu_ucode.c:251-260. - Destination size: amdgpu_psp.c:350-354 (
PSP_1_MEG).
Timeline
- 2026-07-20 Discovered during automated audit.
- 2026-07-20 Reported to DragonFlyBSD security contact (pending).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1854 Β· 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | git-apply-able unified diff; validated as part of combined 41-finding kernel build (rc=0, -Werror clean) | 581 B | view raw |
| VERDICT.md | verdict | source-only confirmation + HW/module gating explanation | 1.5 KB | β raw |
DF-1854 Verification
Verdict
SOURCE-CONFIRMED, INCONCLUSIVE-RUNTIME (HW/module gated).
The cited defect exists in the audited source at sys/dev/drm/amd/amdgpu/amdgpu_psp.c:254-255. Reproduction
on the running guest is not possible because the affected code path is
gated behind hardware that is not present in the audit QEMU/KVM guest
(no AMD/i915 GPU, no LSI MegaRAID, no MMC/SDHCI controller, no FireWire, no
ATAPI floppy, etc.) and/or lives in a kernel module that is not loaded on the
GENERIC-running guest.
Mechanism (source-only confirmation)
amdgpu (not in GENERIC, no AMD HW). Source: psp_asd_load at L254-255 memset(fw_pri_buf,0,PSP_1_MEG); memcpy(fw_pri_buf,asd_start_addr,asd_ucode_size). fw_pri_buf allocated at L350-354 size PSP_1_MEG. asd_ucode_size from le32 (ucode_size_bytes) in firmware header β if >PSP_1_MEG, heap overflow.
Recommended fix
Reject asd_ucode_size > PSP_1_MEG before the memcpy.
The full git apply-able diff lives in fix.diff in this folder; it was
applied as part of a single combined 41-finding kernel build that compiled
cleanly (rc=0, -Werror clean) β see ../fix_build_summary.txt.
Build validation
git apply --checkon this fix.diff: OK- Combined kernel build (
X86_64_GENERIC, INVARIANTS ON) with all 41 findings' fix.diffs applied: rc=0, no warnings, no errors. - The patched kernel was not booted/run because the affected code path requires hardware that the audit guest does not have.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- a
- m
- d
- /
- a
- m
- d
- g
- p
- u
- /
- a
- m
- d
- g
- p
- u
- _
- p
- s
- p
- .
- c
- :
- 2
- 5
- 4
- -
- 2
- 5
- 5
Detail
Exploit chain
none β non-corruption classes (info leak / DoS / div0 / logic) or HW/module gated. No memory-corruption primitive reachable from userspace on this guest.
Evidence (decisive lines)
Source-only confirmation. Combined kernel build with all 41 fix.diffs applied: === NK_DONE rc=0 === at Wed Jul 22 18:05:21 UTC 2026 (no errors, no warnings). See findings/poc/fix_build_summary.txt.
PoC changes
Authored findings/poc/DF-1854/fix.diff (minimal targeted guard). VERDICT.md and manifest.json written. fix.diff validated by combined build.
Verified recommended fix
Reject asd_ucode_size > PSP_1_MEG before the memcpy. Full git-apply-able diff in findings/poc/DF-1854/fix.diff; validated as part of combined 41-finding kernel build (rc=0).
Verdict
SOURCE-CONFIRMED, INCONCLUSIVE-RUNTIME. The cited defect exists at sys/dev/drm/amd/amdgpu/amdgpu_psp.c:254-255. amdgpu (not in GENERIC, no AMD HW). psp_asd_load L254-255 memcpy(fw_pri_buf,asd_start_addr,asd_ucode_size). fw_pri_buf size PSP_1_MEG. asd_ucode_size from le32 (ucode_size_bytes); forged/corrupt ASD firmware header -> heap overflow. HW gated.
No comments yet.