⬢ DragonFlyBSD Kernel Audit
DF-3657 / trig_abort.c
← back to finding ↓ download raw
/* DF-3657 trigger: attempt XPT_ABORT of an XPT_SCSI_IO via /dev/xpt0.
 *
 * Bug (sys/dev/disk/isp/isp_freebsd.c:4541): isp_action(XPT_ABORT) passes the
 * XPT_ABORT CCB itself to isp_control(ISPCTL_ABORT_CMD) instead of
 * ccb->cab.abort_ccb (the CCB to abort). isp_find_handle() matches by pointer
 * against isp->isp_xflist[] (isp_library.c:260-274); the XPT_ABORT CCB was
 * never started, so the handle lookup always fails and every XPT_ABORT of an
 * XPT_SCSI_IO returns CAM_UA_ABORT ("cannot find handle for command to abort").
 *
 * On real hardware the in-kernel CAM periphs (da(4)/cd(4) error recovery,
 * cam_periph_timeout) are the callers; they get CAM_UA_ABORT and must escalate
 * straight to device/bus reset.
 *
 * This program documents the userland reachability boundary: DragonFly's
 * xptioctl() CAMIOCOMMAND whitelist (sys/bus/cam/cam_xpt.c:1028+) only accepts
 * XPT_SCAN_BUS/XPT_RESET_BUS/XPT_PATH_INQ/XPT_ENG_INQ/XPT_SCAN_LUN/XPT_DEBUG/
 * XPT_DEV_MATCH - XPT_ABORT from userland returns EINVAL before any SIM sees
 * it, and with no isp(4) HBA attached there is no isp bus at all.
 */
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <bus/cam/cam.h>
#include <bus/cam/cam_ccb.h>
#include <bus/cam/scsi/scsi_pass.h>

int
main(void)
{
	union ccb ccb;
	int fd, rv;

	fd = open("/dev/xpt0", O_RDWR);
	if (fd < 0) {
		printf("open(/dev/xpt0): %s\n", strerror(errno));
		return (1);
	}

	/* Route to whatever bus would host an isp SIM; on this guest there is
	 * none (no QLogic PCI device), so even a whitelisted func code cannot
	 * reach isp_action(). */
	memset(&ccb, 0, sizeof(ccb));
	ccb.ccb_h.func_code = XPT_ABORT;
	ccb.ccb_h.path_id = 0;		/* first registered bus */
	ccb.ccb_h.target_id = 0;
	ccb.ccb_h.target_lun = 0;
	ccb.cab.abort_ccb = NULL;

	rv = ioctl(fd, CAMIOCOMMAND, &ccb);
	printf("CAMIOCOMMAND(XPT_ABORT) -> rv=%d errno=%d (%s)\n",
	    rv, errno, strerror(errno));
	if (rv == 0)
		printf("ccb status 0x%x\n", ccb.ccb_h.status);
	else
		printf("userland XPT_ABORT rejected before reaching any SIM "
		    "(not in xptioctl whitelist / no isp bus)\n");

	/* Prove no isp bus exists on this machine. */
	memset(&ccb, 0, sizeof(ccb));
	ccb.ccb_h.func_code = XPT_PATH_INQ;
	ccb.ccb_h.path_id = 0;
	ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
	ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
	rv = ioctl(fd, CAMIOCOMMAND, &ccb);
	if (rv == 0) {
		printf("bus0 path inq: sim=\"%s\" hba_vid=\"%s\" status 0x%x\n",
		    ccb.cpi.sim_vid, ccb.cpi.hba_vid, ccb.cpi.ccb_h.status);
	} else {
		printf("XPT_PATH_INQ bus0 -> rv=%d errno=%d (%s)\n",
		    rv, errno, strerror(errno));
	}
	close(fd);
	return (0);
}