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

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_firmware lookups for amdgpufw_<chip>_asd at 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 amdgpu on VEGA10/VEGA12 (psp_v3_1) or RAVEN (psp_v10_0) ASIC with firmware.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.

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_validate amdgpu_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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1854 Β· 2 files
FileTypeDescriptionSize
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
VERDICT.md verdict source-only confirmation + HW/module gating explanation
↓ download 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.

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 --check on 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

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.