⬢ DragonFlyBSD Kernel Audit
DF-3825 / asr_ioctl_poll.c
← back to finding ↓ download raw
/*
 * DF-3825 -- asr(4) unbounded polled waits reachable through I2OUSRCMD
 *
 * sys/dev/raid/asr/asr.c:
 *   ASR_reset()      (asr.c:1254):  while (ASR_resetIOP(sc) == 0);   -- spin
 *                     forever inside crit_enter() when the IOP never
 *                     re-arms; reached from asr_timeout() (callout) and
 *                     I2ORESETCMD ioctl.
 *   ASR_initOutBound()(asr.c:1892): while (*Reply_Ptr <
 *                     I2O_EXEC_OUTBOUND_INIT_REJECTED);  -- zero-timeout
 *                     busy loop on adapter DMA'd word; reached directly
 *                     from the I2OUSRCMD ioctl (function
 *                     I2O_EXEC_OUTBOUND_INIT, asr.c:3211-3219).
 *   ASR_queue_s()    (asr.c:727):   while (status == CAM_REQ_INPROG)
 *                     (void)asr_intr(sc);  -- no timeout poll loop.
 *
 * A transiently locked-up adapter (the V1.05 changelog documents exactly
 * this failure mode) wedges the calling thread forever; from asr_timeout
 * the spin happens inside a critical section.
 *
 * This trigger attempts the I2OUSRCMD route on /dev/asr0.  On the audit
 * guest (no DPT/Adaptec ZCR I2O HBA) open() fails ENXIO/ENOENT.
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define ASR_I2OUSRCMD   _IO('D', 76)   /* osd_unix.h: 0x2000444c */

int
main(int argc, char **argv)
{
	const char *dev = argc > 1 ? argv[1] : "/dev/asr0";
	struct { u_int32_t frame[16]; u_int32_t reply[16]; } msg;
	void *pkt = &msg;
	int fd;

	printf("DF-3825 trigger: device=%s\n", dev);
	fd = open(dev, O_RDWR);
	if (fd < 0) {
		printf("open(%s) failed: %s\n", dev, strerror(errno));
		printf("=> asr(4) control device absent (no I2O HBA attached);\n"
		       "   ioctl surface unreachable on this machine.\n");
		return (2);
	}
	printf("open ok; issuing I2OUSRCMD with function "
	       "I2O_EXEC_OUTBOUND_INIT\n");
	/* I2O message frame: VersionOffset=0x01, MsgFlags=0, size=3 dwords,
	 * function I2O_EXEC_OUTBOUND_INIT = 0xa1 at byte 5. */
	memset(&msg, 0, sizeof(msg));
	msg.frame[0] = 0x00030001;	/* MessageSize=3 | VersionOffset=1 */
	msg.frame[1] = 0;		/* bitfields: target/init/function */
	msg.frame[1] |= (0xa1 << 16);	/* function I2O_EXEC_OUTBOUND_INIT */
	if (ioctl(fd, ASR_I2OUSRCMD, &pkt) < 0)
		printf("I2OUSRCMD: %s\n", strerror(errno));
	else
		printf("I2OUSRCMD accepted (on flaky hardware this busies the\n"
		       "kernel in ASR_initOutBound's zero-timeout reply poll)\n");
	close(fd);
	return (0);
}