/*
 * DF-4509 trigger prober — twa(4) tw_cli_drain_aen_queue FREE_Q double-insert
 *
 * The bug is an internal driver state-machine defect (firmware-hang path):
 *
 *   fw wedged -> reset path -> tw_cli_drain_aen_queue() (tw_cl_misc.c:309)
 *   -> REQUEST_SENSE poll times out (tw_cl_misc.c:353 ETIMEDOUT)
 *   -> out: label (tw_cl_misc.c:379-384) inserts `req` into TW_CLI_FREE_Q
 *      although it is STILL linked in TW_CLI_BUSY_Q (tw_cl_io.c:219) or
 *      TW_CLI_PENDING_Q (tw_cl_io.c:204)  ==> one request in two queues.
 *
 * No unprivileged trigger exists even with hardware: the path runs during
 * controller reset, entered from the MPSAFE watchdog / passthru-timeout /
 * init paths when the firmware stops completing commands.  This program
 * documents the *userspace-visible* preconditions: a twa(4) control device
 * must exist (root-only node /dev/twaN) and the controller must hang.
 *
 * On a machine WITH a twa controller, the reproducer for the underlying
 * state is: cause a firmware hang (e.g. 3ware CLI bug, slot failure) or
 * block MMIO (devctl detach device memory) while I/O is in flight, then
 * watch the reset path hit the 60-second REQUEST_SENSE timeout inside
 * tw_cli_drain_aen_queue and the subsequent queue-structure corruption
 * (duplicate request handed out by tw_cli_get_request while the firmware
 * later completes the original request id).
 *
 * Build:  cc -O -o twa_probe twa_probe.c
 * Run:    ./twa_probe
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int
main(void)
{
	DIR *d;
	struct dirent *de;
	char path[64];
	int fd, found = 0;

	printf("DF-4509 twa drain_aen_queue probe\n");

	d = opendir("/dev");
	if (d != NULL) {
		while ((de = readdir(d)) != NULL) {
			if (strncmp(de->d_name, "twa", 3) == 0) {
				found = 1;
				snprintf(path, sizeof(path), "/dev/%s",
				    de->d_name);
				printf("found control node: %s\n", path);
				fd = open(path, O_RDWR);
				printf("  open(O_RDWR) -> %s\n",
				    fd >= 0 ? "OK (root)" : strerror(errno));
				if (fd >= 0)
					close(fd);
			}
		}
		closedir(d);
	}
	if (!found) {
		printf("no /dev/twa* nodes on this machine: "
		    "twa(4) controller ABSENT -> the vulnerable code path "
		    "(controller reset -> tw_cli_drain_aen_queue ETIMEDOUT) "
		    "cannot be exercised here.\n");
		return (2);
	}
	return (0);
}
