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

vf2pf checksum computed over pf2vf_size bytes instead of sizeof(amdgim_vf2pf_info): out-of-bounds read of up to ~3 KB past the 1 KB vf2pf struct and a wrong protocol checksum published to the host

Summary

Copy-paste of the host-controlled pf2vf length into the vf2pf checksum call: the byte-sum reads up to 4095 bytes from a 1024-byte object (~3KB past, potentially off the kmap), and the host receives a checksum over the wrong range so a conformant PF silently rejects the guest's vf2pf message (telemetry exchange broken on benign hosts). Inherited verbatim from upstream. One-line length fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4917 Β· 6 files
FileTypeDescriptionSize
README.md β€” 1.4 KB ↓ raw
VERDICT.md β€” 3.1 KB ↓ raw
trigger.md β€” 768 B ↓ raw
env.txt β€” 178 B view raw
verdict.json β€” 2.7 KB view raw
manifest.json β€” 975 B view raw

DF-4917 β€” vf2pf checksum computed with pf2vf_size instead of sizeof(amdgim_vf2pf_info): OOB read past the 1 KB vf2pf struct + broken protocol checksum

Build / Run / Expected

Same hardware preconditions as DF-4916 (SR-IOV VF AMD GPU guest, host-fed pf2vf message). On real hardware:

  1. Host writes a pf2vf message with size = 4095 and a valid checksum.
  2. Guest kldload amdgpu β†’ amdgpu_virt_init_data_exchange() zeroes p_vf2pf (1 KB), fills header/driver_version/driver_cert, then computes the vf2pf checksum over pf2vf_size (4095) bytes starting at p_vf2pf (amdgpu_virt.c:435-439) instead of over the 1 KB struct.
  3. Expected: the byte-sum loop (amdgpu_virt.c:383-384) walks ~3 KB past the end of amdgim_vf2pf_info β€” past the end of the exchange area and, with a tight reservation, past the kmap (OOB read of device memory); and the checksum the host receives covers the wrong byte range, so a spec-conformant PF/GIM rejects the vf2pf message (telemetry/features exchange silently broken even on benign hosts).

Success criterion: dmesg-visible GIM-side checksum mismatch, or OOB read fault with a tight reservation.

Files in this pack

  • VERDICT.md β€” narrative + upstream comparison.
  • trigger.md β€” host-side setup.
  • env.txt β€” guest hardware inventory (no AMD GPU present).
  • manifest.json, verdict.json β€” machine records.
VERDICT.md
↓ download raw

DF-4917 β€” VERDICT

Status: not_reproduced (hardware-gated β€” no AMD GPU / SR-IOV VF in the audit guest). The defect is certain from source and confirmed identical in upstream Linux v4.19 (inherited, not port-introduced).

The defect

amdgpu_virt.c:435-439:

            AMDGPU_FW_VRAM_VF2PF_WRITE(adev, checksum,
                amdgpu_virt_fw_reserve_get_checksum(
                adev->virt.fw_reserve.p_vf2pf,
                pf2vf_size,                /* <-- wrong */
                adev->virt.fw_reserve.checksum_key, 0));

The object being checksummed is the vf2pf message just written at p_vf2pf, whose size is sizeof(amdgim_vf2pf_info) β€” the same value the code itself wrote into the vf2pf header two statements earlier (amdgpu_virt.c:423-424). Instead the length passed is pf2vf_size β€” the pf2vf message's host-controlled length ∈ (0, 4096) (amdgpu_virt.c:411).

Consequences:

  1. OOB read: the sum loop at amdgpu_virt.c:383-384 reads pf2vf_size bytes from p_vf2pf. With pf2vf_size up to 4095 and a 1024-byte struct, up to ~3 KB past the end of the vf2pf area is read. The exchange area ends at p_pf2vf + pf2vf_size + 1024; the read ends at p_pf2pf + 2*pf2vf_size, which exceeds it by pf2vf_size - 1024 bytes. Combined with DF-4916's missing region check this can walk off the kmap; with a generous reservation it silently reads unrelated VRAM.
  2. Wrong checksum: the value published to the host covers key + N garbage bytes βˆ’ 0, not the vf2pf message, so any consumer that verifies per the GIM protocol discards the guest's message β€” breaking the feature-flag/error-log exchange (adev->virt.gim_feature consumers) on otherwise healthy hosts.

Upstream comparison: Linux v4.19 drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c line 429-433 passes pf2vf_size identically β€” the bug was inherited from the vanilla source this tree ported (verbatim diff archived in the analysis; the same lines existing in DF at amdgpu_virt.c:435-439).

Why not reproduced on the audit guest

No AMD PCI device in the guest (env.txt); the function is unreachable (fw_vram_usage.va == NULL). Hardware-gated β†’ not_reproduced here; the source-level defect is certain.

Exploit chain

None β€” host-visible protocol corruption + bounded OOB read of VRAM adjacent to the exchange area. No guest-privileged primitive.

--- a/sys/dev/drm/amd/amdgpu/amdgpu_virt.c
+++ b/sys/dev/drm/amd/amdgpu/amdgpu_virt.c
@@ -435,7 +435,7 @@ void amdgpu_virt_init_data_exchange(struct amdgpu_device *adev)
                AMDGPU_FW_VRAM_VF2PF_WRITE(adev, checksum,
                    amdgpu_virt_fw_reserve_get_checksum(
                    adev->virt.fw_reserve.p_vf2pf,
-                   pf2vf_size,
+                   sizeof(amdgim_vf2pf_info),
                    adev->virt.fw_reserve.checksum_key, 0));

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-4917 β€” trigger procedure (SR-IOV VF host + guest)

Host side: write a pf2vf message with size = 4095, version = 2, correct checksum (key = mailbox-2 value on AI, 0 on VI).

Guest side:

kldload amdgpu

amdgpu_virt_init_data_exchange then computes the vf2pf checksum over 4095 bytes starting at p_vf2pf (amdgpu_virt.c:435-439), reading ~3 KB past the 1024-byte amdgim_vf2pf_info struct (amdgpu_virt.h:181-210).

Expected observable: - benign host with large reservation: GIM rejects the vf2pf message (checksum mismatch) β€” silent telemetry break; - host with tight reservation (see DF-4916 setup): kernel page-fault at the byte-sum loop amdgpu_virt.c:383-384.

Not runnable in the audit guest: no AMD GPU (env.txt); path unreachable.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

Fix could not be validated: no SR-IOV AMD hardware in the audit guest; the one-line diff restores the intended length trivially.

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

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (length math + upstream comparison); trigger.md (host-side setup); env.txt (guest pciconf: zero AMD devices)

PoC changes

No runnable PoC for this guest; pack carries the host-side trigger procedure and source-level proof.

Verified recommended fix

Pass sizeof(amdgim_vf2pf_info) instead of pf2vf_size to amdgpu_virt_fw_reserve_get_checksum at amdgpu_virt.c:438 (see VERDICT.md diff).

Verdict

amdgpu_virt.c:435-439 computes the vf2pf message checksum with length pf2vf_size (host-controlled, (0,4096)) instead of sizeof(amdgim_vf2pf_info) (1024). The byte-sum loop (amdgpu_virt.c:383-384) then reads up to ~3KB past the 1KB vf2pf struct - past the exchange area and, with a tight reservation, off the kmap - and the checksum published to the host covers the wrong byte range so spec-conformant PFs reject the guest's message (silent telemetry/feature-flag breakage on benign hosts). Inherited verbatim from upstream Linux v4.19 (verified against torvalds/linux). Not reproduced on the audit guest: no AMD PCI device, fw_vram_usage.va == NULL, function returns at amdgpu_virt.c:402. Source-level defect certain.