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

amdgpu_amdkfd_gfx_v8: entire gfx8 KFD backend is dead code on DragonFly; latent unvalidated vmid/engine index arithmetic and a structurally broken fw-NULL guard must be fixed before KFD is ever enabled (gfx8 sibling of DF-4499)

Summary

Not compiled (absent from Makefile SRCS) and unreachable even if it were (CONFIG_HSA_AMD undefined -> kgd2kfd NULL -> probe early-return). On enable-day five hazards ship unguarded: pasid_vmid_mapping with no vmid<16 check (OOB MMIO writes past 16-entry banks + 1U<<vmid UB + unbounded cpu_relax poll); uint8_t vmid unchecked in the ATC getters (OOB read to +255 dwords); get_fw_version derefs fw->data in every case BEFORE the hdr==NULL check that can never fire (mec2/sdma1 NULL on single-MEC/single-SDMA parts -> panic); MQD-sourced engine/queue ids multiplied into MMIO offsets unchecked; division by num_pipe_per_mec with no zero check. Enable-day guards fix authored (git-apply-verified).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4578 Β· 7 files
FileTypeDescriptionSize
README.md β€” 5.9 KB ↓ raw
VERDICT.md β€” 5.0 KB ↓ raw
env.txt β€” 1.7 KB view raw
run.log β€” 518 B view raw
fix.diff β€” 22.2 KB view raw
manifest.json β€” 1.8 KB view raw
verdict.json β€” 3.9 KB view raw

DF-4578 β€” amdgpu_amdkfd_gfx_v8.c: KFD gfx8 backend is dead code on DragonFly; latent enable-day hazards must be guarded

File: sys/dev/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v8.c (885 LOC) β€” pass 2 (GLM 5.3 re-audit). Sibling of DF-4499 (gfx_v9, same verdict class).

Build/run/expected

There is nothing to build or run: this file is not part of any DragonFly build and its only call path is compiled out tree-wide. Verification is by source trace + guest inspection:

# host (source of truth)
grep -rn "amdgpu_amdkfd_gfx_v8" sys/dev/drm/amd/amdgpu/Makefile sys/dev/drm/amd/Makefile   # no hits => not compiled
grep -rn "define CONFIG_HSA_AMD\|CONFIG_HSA_AMD=" sys/                                      # no hits => KFD never enabled

# guest
pciconf -l | grep 1002      # no ATI hardware present
ls /dev/dri                 # absent
kldstat | grep -i drm       # no drm KLDs loaded

Expected: all three confirm dead code / no reachable path (see env.txt, run.log).

Liveness proof (why this is dead)

  1. sys/dev/drm/amd/amdgpu/Makefile:155-156 β€” the amdkfd block adds only SRCS+= amdgpu_amdkfd.c. amdgpu_amdkfd_gfx_v8.c (and _gfx_v9.c, and the gfx7 backend, which does not exist in this tree at all) are absent from every SRCS list; the lists are explicit, there are no wildcards. The translation unit is never compiled.
  2. CONFIG_HSA_AMD is defined nowhere under sys/ (grep returns nothing), so amdgpu_amdkfd_init() (amdgpu_amdkfd.c:34-47) sets kgd2kfd = NULL and returns -ENOENT.
  3. amdgpu_amdkfd_device_probe() (amdgpu_amdkfd.c:57-71) returns at if (!kgd2kfd) return; β€” amdgpu_amdkfd_gfx_8_0_get_functions() (the real one, gfx_v8.c:183) is unreachable; the #ifndef CONFIG_HSA_AMD stub (amdgpu_amdkfd.c:554-557) is what links instead.
  4. The KFD frontend (amd/amdkfd/, /dev/kfd, the kfd ioctl entry) does not exist anywhere in the DragonFly tree β€” there is no possible caller of the kfd2kgd ops table even if the backend were compiled.

Latent hazards for the day KFD is enabled (fix before flipping CONFIG_HSA_AMD)

Hazards are not reachable today (dead code). They are the gfx8 analogues of the DF-4499 list plus one gfx8-specific NULL-deref:

  1. kgd_set_pasid_vmid_mapping (gfx_v8.c:245-270) β€” no local vmid < 16 check. mmATC_VMID0_PASID_MAPPING + vmid (:260) and mmIH_VMID_0_LUT + vmid (:267) are 16-entry register banks; an out-of-range vmid writes/reads unrelated MMIO. 1U << vmid (:262) is UB for vmid >= 32. Also the cpu_relax() status poll (:262-263) has no timeout β€” a hung GPU wedges the calling thread forever.
  2. get_atc_vmid_pasid_mapping_valid / get_atc_vmid_pasid_mapping_pasid (gfx_v8.c:702-720) β€” vmid is uint8_t (0..255) and unchecked: RREG32(mmATC_VMID0_PASID_MAPPING + vmid) OOB-reads up to 255 dwords past the 16-entry bank.
  3. get_fw_version (gfx_v8.c:778-833) β€” each switch case dereferences the firmware pointer before the NULL check at :828 (if (hdr == NULL) can only catch fw->data == NULL, never fw == NULL). Concretely NULL on real parts: adev->gfx.mec2_fw is NULL on num_mec == 1 VI ASICs (gfx_v8_0.c:2029-2041: TOPAZ/STONEY/default β†’ 1; STONEY is upstream-KFD-supported and one probe-list entry away), and adev->sdma.instance[1].fw is NULL on single-SDMA-instance parts (sdma_v3_0.c:1114-1118: STONEY β†’ 1 instance).
  4. get_sdma_base_addr (gfx_v8.c:291-300) / kgd_hqd_sdma_dump (:486-492) β€” sdma_engine_id/sdma_queue_id (MQD-sourced) and engine_id/queue_id (caller-sourced) are multiplied into MMIO offsets with no bound check (engine_id * SDMA1_REGISTER_OFFSET + queue_id * KFD_VI_SDMA_QUEUE_OFFSET). gfx8 uses multiplication rather than v9's array, so it is an MMIO-offset hazard rather than a kernel-memory OOB β€” same DF-4499 class.
  5. acquire_queue (gfx_v8.c:211-220) / kgd_init_interrupts (:272-279) / kgd_hqd_load (:331-332) β€” divide by adev->gfx.mec.num_pipe_per_mec with no zero check (div-by-zero if called before gfx sw_init populates it).
  6. Note-only: set_scratch_backing_va (:768-776) truncates the 64-bit va to 32 bits in WREG32(mmSH_HIDDEN_PRIVATE_BASE_VMID, va) β€” upstream-identical (scratch backing VA is a <4GB aperture offset by KFD design), not a DF divergence.

Verified-in-bounds (pass-2 negative results, with evidence)

  • MQD walks in kgd_hqd_load (:345-360) β€” walk 1 mmCP_MQD_BASE_ADDR..mmCP_HQD_EOP_CONTROL = 0x3245..0x326c (indices 0..39) and walk 2 mmCP_HQD_EOP_EVENTS..mmCP_HQD_ERROR = 0x326f..0x3278 (indices 42..51) against struct vi_mqd field positions (vi_structs.h:289-340): exact 1:1 layout match, and the struct continues past index 51 up to reserved_t[256] (:417), so no OOB even for a bare vi_mqd.
  • kgd_hqd_dump (:387-421) β€” count is exactly 54+4 (0x3245..0x327a = 54 regs + 4 STATIC_THREAD_MGMT); DUMP_REG is additionally WARN_ON_ONCE(i >= HQD_N_REGS) bounds-guarded. No overflow possible.
  • get_tile_config (:108-128) β€” ARRAY_SIZE over the fixed arrays tile_mode_array[32] / macrotile_mode_array[16] (amdgpu_gfx.h:175-176), not over a pointer; counts are always 32/16 and match the arrays exported.
  • invalidate_tlbs (:848-871) β€” vmid loop bounded 0..15, is_kfd_vmid-gated, 1 << vmid safe for vmid < 16; PASID compare masked with PASID_MASK vs uint16_t.
  • invalidate_tlbs_vmid (:873-885) / set_vm_context_page_table_base (:835-846) β€” both gate on amdgpu_amdkfd_is_kfd_vmid() (compute_vmid_bitmap 0xFF00 β†’ vmid 8..15), so +vmid-8 stays in 0..7 of mmVM_CONTEXT8_PAGE_TABLE_BASE_ADDR and 1 << vmid in 1<<8..1<<15. Safe.

Fix

fix.diff adds the local bounds/NULL guards (vmid < 16 in the pasid-mapping writer and both ATC mapping getters, per-case firmware-pointer NULL checks, engine/queue bounds in the SDMA paths). Authored against the read-only sys/ tree; not applied.

VERDICT.md
↓ download raw

VERDICT DF-4578 β€” gfx8 KFD backend dead on DragonFly; latent index/NULL hazards

Status: untested (no PoC is applicable β€” the dead-code claim itself is the verified fact). Severity Info, confidence certain. Mirror of DF-4499 (gfx_v9) for the gfx8 backend.

What was verified, and how

  1. Dead code β€” certain. - sys/dev/drm/amd/amdgpu/Makefile:155-156: # add amdkfd interfaces block adds SRCS+= amdgpu_amdkfd.c only. amdgpu_amdkfd_gfx_v8.c appears in no Makefile in the tree (checked sys/dev/drm/amd/Makefile, sys/dev/drm/amd/amdgpu/Makefile, sys/dev/drm/Makefile); SRCS lists are explicit enumerations, no wildcards. The translation unit is never compiled into drm.ko/amdgpu.ko. - grep -rn "CONFIG_HSA_AMD" sys/ β†’ only #ifdef/#ifndef guards (amdgpu_amdkfd.c:38,524; amdgpu_drv.c:553,1383); nothing defines it. amdgpu_amdkfd_init() (amdgpu_amdkfd.c:34-47) therefore forces kgd2kfd = NULL; ret = -ENOENT;. - amdgpu_amdkfd_device_probe() (amdgpu_amdkfd.c:57-71) returns before the CHIP_CARRIZO/TONGA/FIJI/POLARIS10/11 case that would call amdgpu_amdkfd_gfx_8_0_get_functions(). The linked symbol is the #ifndef CONFIG_HSA_AMD stub at amdgpu_amdkfd.c:554-557 returning NULL. - No amd/amdkfd/ frontend exists in the tree (only the four amdgpu_amdkfd*.[ch] glue files): no /dev/kfd, no kfd ioctls, no queue manager β€” nothing can invoke the kfd2kgd table even if it were reachable. - Guest (env.txt): no 0x1002 PCI devices (virtio-gpu only), /dev/dri absent, no drm KLDs loaded β€” no runtime path either.

  2. Latent hazards (unreachable today; guard when enabling) β€” certain by inspection. - kgd_set_pasid_vmid_mapping gfx_v8.c:245-270: unvalidated vmid β†’ mmATC_VMID0_PASID_MAPPING + vmid (:260) / mmIH_VMID_0_LUT + vmid (:267) OOB MMIO writes past 16-entry banks; 1U << vmid (:262) UB at vmidβ‰₯32; unbounded cpu_relax() poll (:262-263) = permanent thread wedge on GPU hang. - get_atc_vmid_pasid_mapping_valid/pasid gfx_v8.c:702-720: uint8_t vmid unchecked β†’ OOB MMIO read up to +255 dwords. - get_fw_version gfx_v8.c:778-833: fw->data dereferenced in every case before the if (hdr == NULL) guard (:828). adev->gfx.mec2_fw is NULL on num_mec==1 VI parts (gfx_v8_0.c:2029-2041 β€” TOPAZ/STONEY/default; STONEY is upstream-KFD-supported, one probe-list entry away from this tree's list at amdgpu_amdkfd.c:65-68), adev->sdma.instance[1].fw NULL on single-instance parts (sdma_v3_0.c:1114-1118) β†’ NULL-deref panic on enable-day. - get_sdma_base_addr gfx_v8.c:291-300 / kgd_hqd_sdma_dump :486-492: MQD/caller-sourced engine_id/queue_id multiplied into MMIO offsets unchecked (DF-4499 hazard #1, multiplication flavour). - acquire_queue :211-220, kgd_init_interrupts :272-279, kgd_hqd_load :331-332: division by adev->gfx.mec.num_pipe_per_mec without a zero check. - Note-only: set_scratch_backing_va :768-776 truncates 64-bit va to 32 bits β€” upstream-identical code, not a DF divergence.

  3. In-bounds confirmations (negative results with line evidence) β€” see README.md: MQD register walks 0x3245..0x326c / 0x326f..0x3278 ↔ vi_structs.h:289-340 exact (max index 51, struct extends to :417); hqd_dump 54+4 exact + WARN_ON_ONCE-guarded; get_tile_config ARRAY_SIZE over fixed arrays (amdgpu_gfx.h:175-176); invalidate_tlbs loop 0..15 gated; set_vm_context_page_table_base / invalidate_tlbs_vmid is_kfd_vmid-gated (vmid 8..15 β†’ +vmid-8 in 0..7).

Why not reproduced / not falsified on the guest

There is no trigger to run: the code is absent from every build and the guest has no AMD hardware (env.txt). A PoC would require (a) adding the file to the build, (b) defining CONFIG_HSA_AMD, (c) porting the KFD frontend, and (d) an AMD gfx8 APU β€” none of which exist on this system. The verification deliverable is the reachability analysis above plus the hardening diff.

Exploit chain

None β€” dead code today. On enable-day without the guards: hazards 1-2 give attacker-influenced OOB MMIO read/write (register-space corruption, GPU lockup / potential system instability) from whatever privilege the kfd frontend grants its callers; hazard 3 gives a NULL-deref panic.

Fix

fix.diff (not applied; authored against read-only sys/): vmid<16 guards in kgd_set_pasid_vmid_mapping and both ATC mapping getters; per-case firmware pointer NULL checks before deref; engine/queue bounds in the SDMA MMIO-offset paths. Recommended one-liner: "when enabling KFD add vmid<16, engine/queue bounds and fw NULL checks to the gfx8 kfd backend (fix.diff)".

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.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff authored against read-only sys/ as hardening for currently dead code; no runtime validation applicable on this guest (no AMD hardware, file not built)

fix.diff
↓ fix.diffper-fix-DF-4578

Confirmed kernel references

Detail

Evidence (decisive lines)

env.txt (guest: no 0x1002 devices, /dev/dri absent, no drm KLDs; build tree: no Makefile reference, no CONFIG_HSA_AMD); run.log; VERDICT.md (in-bounds confirmations + latent hazards with path:line); fix.diff (vmid<16, engine/queue<2, fw-NULL guards)

PoC changes

not applicable (no trigger exists; verification = dead-code proof + guest device-absence evidence)

Verified recommended fix

when enabling KFD: apply fix.diff guards (vmid<16 in set_pasid_vmid_mapping and both ATC mapping getters, fw-pointer NULL checks before deref in get_fw_version, engine/queue<2 bounds in SDMA paths) and bound the cpu_relax poll with a timeout

Verdict

Info finding verified by source and guest inspection (sibling of DF-4499): the entire gfx8 kfd2kgd backend of amdgpu_amdkfd_gfx_v8.c is dead code on DragonFly β€” the file is not in any SRCS list (only amdgpu_amdkfd.c is built, Makefile:155-156), CONFIG_HSA_AMD is undefined tree-wide so amdgpu_amdkfd_init() forces kgd2kfd=NULL and device_probe() early-returns, the #ifndef stub provides the linking get_functions(), and no amd/amdkfd frontend exists at all. No PoC is applicable: the unreachability itself is the verified fact; the guest has no AMD PCI device and no /dev/dri. Latent enable-day hazards are documented and guarded by fix.diff: (1) kgd_set_pasid_vmid_mapping unvalidated vmid -> OOB MMIO writes past the 16-entry ATC/IH banks + 1U< OOB MMIO read up to +255 dwords; (3) get_fw_version dereferences fw->data before the hdr==NULL guard β€” mec2_fw is NULL on num_mec==1 VI parts (TOPAZ/STONEY) and sdma.instance[1].fw NULL on single-instance parts; (4) unchecked MQD-sourced engine/queue ids multiplied into MMIO offsets; (5) division by num_pipe_per_mec without zero check. Verified in-bounds (negative results): MQD register walks 0x3245-0x326c / 0x326f-0x3278 map 1:1 onto vi_structs.h:289-340 with headroom to :417; hqd_dump 54+4 count exact and WARN_ON_ONCE-guarded; get_tile_config uses ARRAY_SIZE over the fixed [32]/[16] arrays; invalidate_tlbs loops vmid<16 behind is_kfd_vmid; set_vm_context_page_table_base/invalidate_tlbs_vmid are is_kfd_vmid-gated.