/*
 * DF-5779 rig C — the %s variant.  Same seq_puts() shim contract, but the
 * dynamic data string contains %s: the kernel printf core pulls a pointer
 * out of a vararg slot that was never set (subr_prf.c:751) and runs
 * strlen()/copy on it -> wild kernel read.  Expected: panic or garbage
 * memory leak into m->buf.  Load LAST; expect to reset the guest after.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/module.h>

struct seq_file {
	char   *buf;
	size_t  size;
};
#define seq_puts(m, str)	ksnprintf((m)->buf, (m)->size, str)

static void
rig_run(void)
{
	struct seq_file m;
	char backing[256];
	/* 9 conversions: burns rcx/r8/r9 register-save slots, then walks the
	 * stack overflow area (return address, saved regs, locals) of the
	 * ksnprintf frame -> strlen()/copy on wild kernel pointers. */
	const char *data = "m=%s s=%s x=%s y=%s z=%s w=%s v=%s u=%s t=%s\n";

	m.buf = backing;
	m.size = sizeof(backing);

	kprintf("seqputs_rigc: firing %%s-walk on unset varargs...\n");
	seq_puts(&m, data);
	kprintf("seqputs_rigc: survived; buf='%s'\n", backing);
}

static int
seqputs_rigc_load(struct module *m __unused, int what, void *arg __unused)
{
	switch (what) {
	case MOD_LOAD:
		rig_run();
		return (0);
	default:
		return (0);
	}
}

static moduledata_t seqputs_rigc_mod = {
	"seqputs_rigc",
	seqputs_rigc_load,
	NULL
};
DECLARE_MODULE(seqputs_rigc, seqputs_rigc_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
MODULE_VERSION(seqputs_rigc, 1);
