DF-1257 / harness.c
/* * DF-1257 harness โ amdgpu_vm_bo_map() offset+size integer overflow -> OOB. * * sys/dev/drm/amd/amdgpu/amdgpu_vm.c: * amdgpu_vm_bo_map (:2511): if (bo && offset + size > amdgpu_bo_size(bo)) * return -EINVAL; * amdgpu_vm_bo_replace_map (:2576): same check. * * `offset` and `size` are uint64. The check `offset + size > bo_size` wraps * mod 2^64: offset = 0xFFFFFFFFFFFFF000, size = 0x2000 -> sum = 0x1000, which * is <= a 0x2000 BO, so the check PASSES and mapping->offset (line 2532 / * 2595) stores the HUGE offset 0xFFFFFFFFFFFFF000. * * Later, amdgpu_vm_bo_split_mapping (:1988): * pfn = mapping->offset >> PAGE_SHIFT; -> 0x000FFFFFFFFFFFFF * ... * addr = pages_addr[pfn]; (:2028) -> OOB read ~2^52 elts * * Kernel trigger requires an amdgpu GPU + an unprivileged user issuing the * AMDGPU_VM ioctl with the wrapped offset โ absent from this QEMU guest * (no AMD GPU, no /dev/dri/renderD128). This harness replicates the bounds * check and the pfn computation, proving the overflow -> OOB math. */ #include <stdio.h> #include <stdint.h> #define PAGE_SHIFT 12 static int bo_map_check(uint64_t offset, uint64_t size, uint64_t bo_size) { /* amdgpu_vm.c:2511 verbatim */ if (offset + size > bo_size) return -1; /* rejected */ return 0; /* accepted */ } int main(void) { uint64_t bo_size = 0x2000; /* a 8 KiB BO */ uint64_t offset = 0xFFFFFFFFFFFFF000ULL; /* near UINT64_MAX, page-aligned */ uint64_t size = 0x2000; /* page-aligned, non-zero */ /* page-mask alignment gates (amdgpu_vm.c:2504-2506) โ all pass: * offset & 0xfff == 0, size & 0xfff == 0, size != 0. */ int align_ok = ((offset & 0xfff) == 0) && ((size & 0xfff) == 0) && (size != 0); printf("alignment gate (amdgpu_vm.c:2504): %s\n", align_ok ? "PASS" : "FAIL"); uint64_t sum = offset + size; /* wraps mod 2^64 */ printf("offset = 0x%016llx\n", (unsigned long long)offset); printf("size = 0x%016llx\n", (unsigned long long)size); printf("offset + size (uint64) = 0x%016llx (wrapped!)\n", (unsigned long long)sum); printf("bo_size = 0x%016llx\n", (unsigned long long)bo_size); int rc = bo_map_check(offset, size, bo_size); printf("bounds check (amdgpu_vm.c:2511) result: %s\n", rc == 0 ? "ACCEPT (bug)" : "reject"); if (rc == 0) { /* mapping->offset stored as the huge value (amdgpu_vm.c:2532) */ uint64_t stored_offset = offset; uint64_t pfn = stored_offset >> PAGE_SHIFT; /* amdgpu_vm.c:1988 */ printf("stored mapping->offset = 0x%016llx\n", (unsigned long long)stored_offset); printf("pfn = mapping->offset >> %d = 0x%016llx\n", PAGE_SHIFT, (unsigned long long)pfn); printf("pages_addr[pfn] would read at index 0x%016llx -> MASSIVE OOB read\n", (unsigned long long)pfn); printf("\nPRIMITIVE CONFIRMED: wrapped offset+size passes the bounds check; stored offset\n"); printf("yields pfn ~ 2^52 -> OOB read in pages_addr[]. Bug is REAL.\n"); return 0; } printf("\nUNEXPECTED: check rejected (no overflow)\n"); return 1; } |