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)
PoC verification
Evidence pack
findings/poc/DF-1257 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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.
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)
amdgpu_vm.c:2504-2506β alignment/size gates pass foroffset=0xFFFFFFFFFFFFF000, size=0x2000(both page-aligned, sizeβ 0).amdgpu_vm.c:2511βif (... || (bo && offset + size > amdgpu_bo_size(bo)))0xFFFFFFFFFFFFF000 + 0x2000wraps to0x1000β€ a 0x2000 BO β passes.amdgpu_vm.c:2532βmapping->offset = offset;stores the huge value.amdgpu_vm.c:2576βamdgpu_vm_bo_replace_maphas the identical bug.amdgpu_vm.c:1988βpfn = mapping->offset >> PAGE_SHIFT;β0x000FFFFFFFFFFFFF.amdgpu_vm.c:2028βaddr = pages_addr[pfn];β OOB read ~2^52 elements. (alsoamdgpu_vm.c:2019pages_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_testablecompile+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.
No comments yet.