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

Integer overflow in offset+size bounds check in amdgpu_vm_bo_map enables OOB read in amdgpu_vm_bo_split_mapping

Summary

amdgpu_vm_bo_map at amdgpu_vm.c:2509-2512 (and replace_map :2574): check offset+size>amdgpu_bo_size(bo) in uint64, but offset+size wraps. offset=0xFFFFFFFFFFFFF000, size=0x2000 -> sum=0x1000, passes check. mapping->offset stored huge. amdgpu_vm_bo_split_mapping:1988: pfn=mapping->offset>>12 = 0x000FFFFFFFFFFFFF. GTT BO: addr=pages_addr[pfn] (:2028) -> OOB read ~2^52 elements. VRAM: while(pfn>=nodes->size)++nodes (:1990-1993) walks past nodes array. Local unprivileged user via AMDGPU_VA_OP_MAP with crafted offset_in_bo. GPU reads arbitrary host memory via corrupted PTEs. Fix: check offset<=bo_size, then size>bo_size-offset (no wrap).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1257 Β· 11 files
FileTypeDescriptionSize
harness.c trigger-source userspace replication of amdgpu_vm_bo_map offset+size wrap + pfn OOB 3.2 KB view raw
build.sh build-script cc -O2 -Wall -o harness harness.c 93 B view raw
run.sh run-script ./harness 48 B view raw
run.log run-log harness output: wrapped check ACCEPT, pfn 0x000fffffffffffff OOB 561 B view raw
fix.diff suggested-fix overflow-safe bounds check in amdgpu_vm_bo_map and amdgpu_vm_bo_replace_map 1.4 KB view raw
fix_build.log build-log amdgpu.ko builds clean with -Werror (fix compiles) 8.7 KB view raw
env.txt environment uname, cc, module list 346 B view raw
README.md readme how to reproduce 687 B ↓ raw
VERDICT.md verdict full analysis 2.7 KB ↓ raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme how to reproduce
↓ download raw

DF-1257 β€” reproduction

Userspace harness replicating the amdgpu_vm_bo_map offset + size integer-overflow bounds check and the resulting pfn = mapping->offset >> 12 OOB read.

The live kernel trigger requires an AMD GPU with amdgpu (absent from the QEMU guest, no /dev/dri/renderD128); the harness proves the overflow β†’ OOB math.

./build.sh && ./run.sh

Expected: PRIMITIVE CONFIRMED: wrapped offset+size passes the bounds check; stored offset yields pfn ~ 2^52 -> OOB read in pages_addr[]. Bug is REAL.

Fix: fix.diff makes the bounds check overflow-safe in both bo_map and bo_replace_map. Builds cleanly into amdgpu.ko (-Werror). See VERDICT.md.

VERDICT.md verdict full analysis
↓ download raw

DF-1257 β€” amdgpu_vm_bo_map offset+size integer overflow -> OOB

Verdict (one line)

CONFIRMED REAL (source trace + harness primitive), NOT reproduced on audit guest (no AMD GPU / no amdgpu).

Finding

sys/dev/drm/amd/amdgpu/amdgpu_vm.c:amdgpu_vm_bo_map() (and amdgpu_vm_bo_replace_map()) validate the BO mapping with offset + size > amdgpu_bo_size(bo) in uint64. The addition wraps, so a huge page-aligned offset near UINT64_MAX plus a small size passes the check and a wrapped mapping->offset is stored. The later page-table walk computes pfn = mapping->offset >> PAGE_SHIFT β‰ˆ 2^52 and indexes pages_addr[pfn] β€” a massive out-of-bounds read.

Mechanism (path:line)

  1. amdgpu_vm.c:2504-2506 — alignment/size gates pass for offset=0xFFFFFFFFFFFFF000, size=0x2000 (both page-aligned, size≠0).
  2. amdgpu_vm.c:2511 β€” if (... || (bo && offset + size > amdgpu_bo_size(bo))) 0xFFFFFFFFFFFFF000 + 0x2000 wraps to 0x1000 ≀ a 0x2000 BO β‡’ passes.
  3. amdgpu_vm.c:2532 β€” mapping->offset = offset; stores the huge value.
  4. amdgpu_vm.c:2576 β€” amdgpu_vm_bo_replace_map has the identical bug.
  5. amdgpu_vm.c:1988 β€” pfn = mapping->offset >> PAGE_SHIFT; β‡’ 0x000FFFFFFFFFFFFF.
  6. amdgpu_vm.c:2028 β€” addr = pages_addr[pfn]; β‡’ OOB read ~2^52 elements. (also amdgpu_vm.c:2019 pages_addr[idx].)

Why not reproduced on the audit guest

amdgpu is a loadable DRM module, not in GENERIC, and the QEMU/KVM guest has no AMD GPU (no /dev/dri/renderD128). The mapping ioctl is reached only after an amdgpu device attaches. The realistic trigger is an unprivileged local user on a machine with an AMD GPU issuing the AMDGPU_VM ioctl with the wrapped offset β€” a legitimate local kernel-OOB threat on such hardware, not exercisable here.

Primitive proof (harness)

harness.c replicates the bounds check and the pfn computation exactly:

alignment gate (amdgpu_vm.c:2504): PASS
offset + size (uint64) = 0x0000000000001000   (wrapped!)
bounds check (amdgpu_vm.c:2511) result: ACCEPT (bug)
pfn = mapping->offset >> 12 = 0x000fffffffffffff
pages_addr[pfn] would read at index 0x000fffffffffffff -> MASSIVE OOB read
PRIMITIVE CONFIRMED: wrapped offset+size passes the bounds check; stored offset
yields pfn ~ 2^52 -> OOB read in pages_addr[]. Bug is REAL.

Fix

fix.diff rewrites both checks (amdgpu_vm.c:2511 and :2576) to avoid the overflowing addition: with __bo_sz = amdgpu_bo_size(bo), reject if size > __bo_sz || offset > __bo_sz - size. Validated: builds cleanly into amdgpu.ko with -Werror.

Reproduce

ssh dfbsd-maxx; cd poc/DF-1257 && cc -O2 -Wall -o harness harness.c && ./harness

Fix verification

not_testable

compile+harness validated

module build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source+harness. amdgpu_vm_bo_map offset+size uint64 wrap -> pfn ~2^52 OOB read. amdgpu not in GENERIC.