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

amdgpu_virt_init_data_exchange: host-controlled pf2vf_size drives a 1 KB memset (and the header reads) past the kmap'ed fw VRAM reservation β€” no validation that the region contains the 64 KB-offset exchange area

Summary

The reservation size comes verbatim from the host-controlled vBIOS (can be as small as 1KB) while the exchange area is hardcoded at +64KB: the header reads and the 1KB vf2pf memset at va+64K+pf2vf_size run up to ~69KB into a region that may be 1KB deep - OOB write (zeros, host-chosen offset) + OOB reads at init and post-FLR. Inherited from upstream v4.19; host-fed trigger (Low per the host-fed-mailbox precedent). Gate-on-region-size fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4916 Β· 6 files
FileTypeDescriptionSize
README.md β€” 1.7 KB ↓ raw
VERDICT.md β€” 4.9 KB ↓ raw
trigger.md β€” 1.2 KB ↓ raw
env.txt β€” 178 B view raw
verdict.json β€” 3.4 KB view raw
manifest.json β€” 1.1 KB view raw

DF-4916 β€” amdgpu_virt_init_data_exchange: host-controlled pf2vf_size drives a 1 KB memset past the kmap'ed fw VRAM reservation (no region-bounds validation)

Build / Run / Expected

No runnable PoC on this guest β€” requires an SR-IOV VF AMD GPU guest whose host/PF (or vBIOS) supplies the pf2vf message. On real hardware:

  1. Boot a DragonFly SR-IOV VF guest whose vBIOS VRAM_UsageByFirmware table declares the SR-IOV message reservation (ATOM_VRAM_BLOCK_SRIOV_MSG_SHARE_RESERVATION) with used_by_firmware_in_kb small, e.g. 4 (β†’ fw_vram_usage.size = 4096), and whose GIM/PF writes a pf2vf header at VRAM offset 64 KB with size ∈ (0,4096) and a matching checksum (the key is host-known: on AI it is literally read out of mailbox 2, mxgpu_ai.c:176-180).
  2. kldload amdgpu in the guest.
  3. Expected: amdgpu_virt_init_data_exchange() runs at the end of device init (amdgpu_device.c:1695) and after VF FLR re-init (amdgpu_device.c:3345): - reads at fw_vram_usage.va + 65536 are already past the 4 KB mapping (amdgpu_virt.c:402-408), - on checksum match, p_vf2pf = p_pf2vf + pf2vf_size and memset(p_vf2pf, 0, 1024) (amdgpu_virt.c:416-420) writes up to offset 65536+4095+1024 β‰ˆ 69 KB into the 4 KB mapping β†’ kernel OOB access on the VRAM kmap β†’ guest panic (or silent VRAM corruption of whatever is mapped next).

Success criterion: panic (or dmesg evidence of the OOB) at driver init.

Files in this pack

  • VERDICT.md β€” full bound-math narrative with path:line citations.
  • trigger.md β€” the host-side setup above.
  • env.txt β€” guest hardware inventory (no AMD GPU present).
  • manifest.json, verdict.json β€” machine records.
VERDICT.md
↓ download raw

DF-4916 β€” VERDICT

Status: not_reproduced (hardware-gated β€” no AMD GPU / SR-IOV VF in the audit guest). The bounds defect itself is certain from source; the trigger data is host/PF-supplied, so guest-side reproduction requires SR-IOV hardware.

The chain, with the numbers

  1. The size of the firmware VRAM reservation is taken verbatim from the vBIOS (in an SR-IOV guest the vBIOS is host-supplied): amdgpu_atombios.c:1803-1807 and amdgpu_atomfirmware.c:91-95 set adev->fw_vram_usage.size = used_by_firmware_in_kb << 10. The only constraint anywhere is size > 0 && size <= visible_vram_size (amdgpu_ttm.c:1707-1708). No minimum size is enforced.
  2. amdgpu_ttm_fw_reserve_vram_init() (amdgpu_ttm.c:1742-1743) kmap's exactly fw_vram_usage.size bytes into fw_vram_usage.va.
  3. amdgpu_virt_init_data_exchange() (amdgpu_virt.c:392-443) β€” called at amdgpu_device.c:1695 (end of device init on VF) and amdgpu_device.c:3345 (post-FLR re-init) β€” hardcodes p_pf2vf = fw_vram_usage.va + AMDGIM_DATAEXCHANGE_OFFSET with AMDGIM_DATAEXCHANGE_OFFSET = 64 * 1024 (amdgpu_virt.h:74), and immediately dereferences it (header.size, header.version, version-dispatched checksum / feature_flags reads via AMDGPU_FW_VRAM_PF2VF_READ, amdgpu_virt.h:225-235) with no check that the reservation is even 64 KB deep. With a small-but-nonzero reservation every one of these reads is out of bounds of the kmap.
  4. pf2vf_size (u32 read from VRAM, fully host-written) is gated only by 0 < pf2vf_size < 4096 (amdgpu_virt.c:411) and a checksum whose key is host-supplied (AI: mailbox 2, mxgpu_ai.c:176-180; VI: checksum_key stays 0 β€” never initialized by mxgpu_vi). The gate therefore binds nothing against the actual reservation size.
  5. On "checksum ok": p_vf2pf = p_pf2vf + pf2vf_size (amdgpu_virt.c:416-418) and memset(p_vf2pf, 0, sizeof(amdgim_vf2pf_info)) (amdgpu_virt.c:419-420). sizeof(amdgim_vf2pf_info) = 1024 (v2 struct is padded to 256 u32s by AMDGIM_GET_STRUCTURE_RESERVED_SIZE(256, 64, 0, 14, 0), amdgpu_virt.h:209). Worst case the memset spans [va+65536+1, va+65536+4095+1024) β€” i.e. up to ~69 KB into a region the vBIOS may have declared as small as 1 KB.

So the guest kernel performs an OOB write (zeroes, 1 KB, host-chosen offset in (64 KB, 68 KB)) and OOB reads on the kmap'ed VRAM BO, purely on host-fed data. Behavior on x86-64 with an ioremap'ed TTM BO: access beyond the mapping either hits adjacent device memory (silent corruption) or an unmapped page (kernel page-fault panic). Guest userspace cannot reach this path (device-init time, root/host-controlled inputs).

This is an inherited upstream defect (Linux v4.19 has the identical code; verified against torvalds/linux v4.19 amdgpu_virt.c lines 396-436), not a port-introduced one β€” but it is a real robustness hole in the guest-kernel trust boundary the file exists to implement.

Why not reproduced on the audit guest

No AMD PCI device exists in the QEMU guest (env.txt); amdgpu.ko never binds; fw_vram_usage.va stays NULL and amdgpu_virt_init_data_exchange returns at line 402. Hardware-gated β†’ not_reproduced on this guest, impact none here; the bound-math defect is certain.

Exploit chain

None from guest userspace. From a malicious/compromised host position: guest panic (init-time DoS) or 1 KB zero-corruption at a ~4 KB-wide host-chosen offset past a known kmap. No guest-privileged code-exec primitive identified.

Validate the reservation actually contains the exchange area before any dereference, e.g.:

--- a/sys/dev/drm/amd/amdgpu/amdgpu_virt.c
+++ b/sys/dev/drm/amd/amdgpu/amdgpu_virt.c
@@ -399,7 +399,14 @@ void amdgpu_virt_init_data_exchange(struct amdgpu_device *adev)
    adev->virt.fw_reserve.p_vf2pf = NULL;

-   if (adev->fw_vram_usage.va != NULL) {
+   if (adev->fw_vram_usage.va != NULL &&
+       adev->fw_vram_usage.size >=
+       AMDGIM_DATAEXCHANGE_OFFSET + sizeof(amdgim_vf2pf_info)) {
        adev->virt.fw_reserve.p_pf2vf =
            (struct amdgim_pf2vf_info_header *)(
            adev->fw_vram_usage.va + AMDGIM_DATAEXCHANGE_OFFSET);

plus, inside the pf2vf_size gate:

-       if (pf2vf_size > 0 && pf2vf_size < 4096) {
+       if (pf2vf_size >= sizeof(struct amdgim_pf2vf_info_v2) &&
+           pf2vf_size + sizeof(amdgim_vf2pf_info) <=
+           adev->fw_vram_usage.size - AMDGIM_DATAEXCHANGE_OFFSET) {

(Upstream later grew equivalent guards; this mirrors them.)

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.

(No fix.diff for this item: documented recommendation only - see Fix above.)

trigger.md
↓ download raw

DF-4916 β€” trigger procedure (SR-IOV VF host + guest)

Host side (PF, GIM/amdgpu or vendor stack):

  1. Program the VF's vBIOS VRAM_UsageByFirmware table entry with the ATOM_VRAM_BLOCK_SRIOV_MSG_SHARE_RESERVATION flag and used_by_firmware_in_kb = 4 (reservation of 4 KB β€” smaller than the 64 KB AMDGIM_DATAEXCHANGE_OFFSET the guest driver assumes).
  2. At VF setup, write a pf2vf header at VRAM + 64 KB with version = 1 or 2, size = 0x1000 - 1 (4095), a benign feature_flags, and a correct checksum over size bytes (key: mailbox-2 value on AI, 0 on VI).

Guest side:

kldload amdgpu

At the tail of amdgpu_device_init (amdgpu_device.c:1695) the guest reads the pf2vf header past the end of its 4 KB kmap and, on checksum match, memsets 1024 bytes at va + 64 KB + 4095 β€” outside the reservation.

Expected observable: guest kernel page-fault panic at amdgpu_virt.c:419 (memset) or at the header reads (amdgpu_virt.c:406-408); with a larger reservation merely corrupted VRAM contents.

Not runnable in the audit guest: no AMD GPU (env.txt). Amdgpu never binds, fw_vram_usage.va == NULL, function returns at amdgpu_virt.c:402.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

Fix could not be validated: no SR-IOV AMD hardware in the audit guest, so neither baseline nor patched behavior is reachable.

VERDICT.md 'Recommended fix' section contains the git-apply-able diff against amdgpu_virt.c.
per-fix-DF-4916

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (full bound math with path:line); trigger.md (host-side vBIOS/GIM setup); env.txt (guest pciconf: zero AMD devices)

PoC changes

No runnable PoC for this guest; pack carries the host-side trigger procedure (malicious/mismatched vBIOS reservation + forged pf2vf checksum) and source-level proof.

Verified recommended fix

Gate the whole exchange on fw_vram_usage.size >= AMDGIM_DATAEXCHANGE_OFFSET + sizeof(amdgim_vf2pf_info) and validate pf2vf_size >= sizeof(struct amdgim_pf2vf_info_v2) and pf2vf_size + sizeof(amdgim_vf2pf_info) <= size - AMDGIM_DATAEXCHANGE_OFFSET before use (see VERDICT.md diff).

Verdict

amdgpu_virt_init_data_exchange (amdgpu_virt.c:392-443) never validates that the kmap'ed fw VRAM reservation (fw_vram_usage.size taken verbatim from the host-supplied vBIOS, amdgpu_atombios.c:1803-1807 / amdgpu_atomfirmware.c:91-95; only gate is size>0 at amdgpu_ttm.c:1707) actually contains the fixed 64KB AMDGIM_DATAEXCHANGE_OFFSET exchange area. It dereferences va+64KB immediately (amdgpu_virt.c:402-408) and, on a host-forgeable checksum (key is host-supplied via mailbox 2, mxgpu_ai.c:176-180), memsets 1024 bytes at va+64KB+pf2vf_size with pf2vf_size host-controlled in (0,4096) (amdgpu_virt.c:411-420) - an OOB write of up to ~69KB offset into a region that may be as small as 1KB, plus OOB header reads. Guest panic or VRAM corruption at driver init / after VF FLR. Not reproduced on the audit guest (no AMD PCI device; fw_vram_usage.va stays NULL and the function returns at line 402). Bounds defect certain from source; inherited unchanged from upstream Linux v4.19.