/*
 * DF-5779 differential rig — proves the seq_puts() shim contract divergence
 * on the live DragonFly kernel.
 *
 * The shim (sys/dev/drm/include/linux/seq_file.h:44) is:
 *     #define seq_puts(m, str)  ksnprintf((m)->buf, (m)->size, str)
 * i.e. the DATA string `str` occupies ksnprintf's FORMAT parameter.
 *
 * Rig A: controlled %n  -> is the write-through-pointer conversion live in
 *        the kernel printf core (sys/kern/subr_prf.c:688-701)?
 * Rig B: exact macro expansion with a dynamic data string (the thing a
 *        ported Linux caller would pass) containing %d — the kernel
 *        interprets it against varargs that were NEVER passed.
 *
 * Build: see build.sh.  Load with kldload(8); evidence lands in dmesg.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/module.h>

/* exact replica of sys/dev/drm/include/linux/seq_file.h:37-44 */
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];
	char snap[256];
	int written = -1;
	const char *data = "conn=DP-1 link=%d ok=100%%\n";

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

	/* --- Rig A: is %n live? (controlled, safe) --- */
	backing[0] = '\0';
	ksnprintf(m.buf, m.size, "AA%nBB\n", &written);
	kprintf("seqputs_rig A: %%n wrote %d (2 == LIVE write conv)\n",
		written);

	/* --- Rig B: seq_puts(m, dynamic_data) — the shim contract --- */
	seq_puts(&m, data);			/* == ksnprintf(buf,size,data) */
	bcopy(m.buf, snap, sizeof(snap) - 1);
	snap[sizeof(snap) - 1] = '\0';
	kprintf("seqputs_rig B: seq_puts(m,data) produced: '%s'\n", snap);
	kprintf("seqputs_rig B: Linux contract would print the literal "
		"data string verbatim (no interpretation)\n");
}

static int
seqputs_rig_load(struct module *m __unused, int what, void *arg __unused)
{
	switch (what) {
	case MOD_LOAD:
		kprintf("seqputs_rig: loaded\n");
		rig_run();
		return (0);
	default:
		return (0);
	}
}

static moduledata_t seqputs_rig_mod = {
	"seqputs_rig",
	seqputs_rig_load,
	NULL
};
DECLARE_MODULE(seqputs_rig, seqputs_rig_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
MODULE_VERSION(seqputs_rig, 1);
