/*
 * DF-4227 trigger: use-after-detach of /dev/cyapa0-67
 * (sys/dev/smbus/cyapa/cyapa.c detach vs open fds)
 *
 * REQUIRES REAL HARDWARE (Cypress APA trackpad on SMBus addr 0x67).
 * On such a system, run as root (device is 0600 root:wheel):
 *
 *      ./trigger_detach_uaf &           # opens + reads
 *      sleep 1 && kldunload cyapa.ko    # second shell
 *
 * After kldunload the reader's blocked read() hits:
 *   cyaparead (cyapa.c:616): sc = CYAPA_SOFTC(minor)   -> NULL
 *        (subr_bus.c:1164 dc->devices[unit] = NULL after detach)
 *   cyaparead (cyapa.c:626): cyapa_lock(sc)            -> no NULL check
 *        -> lockmgr(&sc->lk) on NULL+offset -> kernel panic
 * (or, if the read is dispatched after module unload, the call goes
 *  through freed module ops memory.)
 *
 * On the DragonFly QEMU audit guest this stops at open(): no SMBus
 * controller + no trackpad => cyapa never attaches => no /dev/cyapa*.
 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

int main(void)
{
	char buf[16];
	int fd;

	fd = open("/dev/cyapa0-67", O_RDONLY);
	if (fd < 0) {
		printf("open(/dev/cyapa0-67): %s\n", strerror(errno));
		printf("(device not present on this machine - "
		       "needs real cypress trackpad on smbus)\n");
		return 2;
	}
	printf("opened, blocking in read(); now: kldunload cyapa.ko\n");
	fflush(stdout);
	/*
	 * Blocks in lksleep() inside cyaparead (cyapa.c:749).  cyapa_detach
	 * never wakes blocked readers and proceeds to devclass teardown.
	 */
	if (read(fd, buf, sizeof(buf)) < 0)
		printf("read returned: %s\n", strerror(errno));
	else
		printf("read returned data\n");
	return 0;
}
