⬢ DragonFlyBSD Kernel Audit
DF-5463 / to5463.c
← back to finding ↓ download raw
/*
 * to5463 -- DF-5463: PTIOCSETTIMEOUT signed multiply overflow.
 *
 * ptioctl() stores  softc->io_timeout = *(int *)addr * 1000
 * (sys/bus/cam/scsi/scsi_pt.c:654).  For values > 2147483 the int
 * multiply overflows and io_timeout goes NEGATIVE (UB); the negative
 * value is then consumed as a u_int32_t CDB timeout in ptstart().
 * PTIOCGETTIMEOUT maps any io_timeout < 1000 to 0, so the wrap is
 * directly observable from userland.
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/ptio.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>

int
main(int argc, char **argv)
{
	const char *dev = argc > 1 ? argv[1] : "/dev/pt0";
	int fd, t, r = 0;

	fd = open(dev, O_RDWR);
	if (fd < 0) {
		perror(dev);
		return (2);
	}

	t = 100000;			/* sanity: in-range */
	if (ioctl(fd, PTIOCSETTIMEOUT, &t) != 0) {
		perror("SETTIMEOUT(100000)");
		r = 2;
	}
	t = -1;
	ioctl(fd, PTIOCGETTIMEOUT, &t);
	printf("set=100000     -> get=%d (expect 100000)\n", t);
	if (t != 100000)
		r |= 1;

	t = 2147483;			/* INT_MAX/1000: still fine */
	ioctl(fd, PTIOCSETTIMEOUT, &t);
	t = -1;
	ioctl(fd, PTIOCGETTIMEOUT, &t);
	printf("set=2147483    -> get=%d (expect 2147483)\n", t);
	if (t != 2147483)
		r |= 1;

	t = 2147484;			/* 2147484*1000 > INT_MAX: wraps */
	ioctl(fd, PTIOCSETTIMEOUT, &t);
	t = -1;
	ioctl(fd, PTIOCGETTIMEOUT, &t);
	printf("set=2147484    -> get=%d (expect 2147484; 0 == OVERFLOWED "
	    "to negative)\n", t);
	if (t == 0)
		printf("OVERFLOW-MANIFEST: io_timeout wrapped negative\n");

	close(fd);
	return (r);
}