/*
 * dconsnull.c -- DF-4781 / DF-4782 PoC (TEST ONLY, never for sys/).
 *
 * Proves the NULL-deref sinks reached by two unchecked wiring paths in
 * sys/dev/misc/dcons/dcons_os.c:
 *
 *   DF-4781: dcons_drv_init() (dcons_os.c:462-464) does not check
 *            contigmalloc() for NULL before dcons_init(dg.buf, ...).
 *            dcons_init() immediately writes buf->osize[0] (dcons.c:147)
 *            => NULL+offset store -> Fatal trap 12 at kldload time.
 *
 *   DF-4782: in a kernel built with `device dcons`, /dev/dcons is created
 *            by dcons_cninit_fini() (dcons_os.c:385) but si_drv1 is only
 *            wired by dcons_attach_port() (dcons_os.c:493) on the module
 *            path.  First write to the tty runs dcons_tty_start()
 *            (dcons_os.c:320) which does
 *                dc = tp->t_dev->si_drv1;      // NULL
 *                dcons_os_putc(dc, c);         // dcons_os.c:329
 *            => dcons_putc(NULL, c) reads ch->buf at NULL+16 (dcons.c:118)
 *            => Fatal trap 12.
 *
 * Modes (kenv dconsnull.mode=N before kldload):
 *   0 = negative control: init a real 16KB buffer, putc/checkc, print OK.
 *   1 = dcons_init(NULL, 16384, sc)  [DF-4781 sink]
 *   2 = dcons_putc(NULL, 'A')        [DF-4782 sink]
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/conf.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kenv.h>

#include "dcons.h"

static struct dcons_softc dsc[DCONS_NPORT];
static char nbuf[16 * 1024] __aligned(4);

static int
dconsnull_modevent(module_t mod, int type, void *data)
{
	char *v;
	int mode;

	switch (type) {
	case MOD_LOAD:
		if ((v = kgetenv("dconsnull.mode")) == NULL)
			mode = 0;
		else
			mode = v[0] - '0';

		kprintf("dconsnull: loaded, mode=%d\n", mode);

		/* Negative control: the same calls with a valid buffer. */
		if (mode == 0) {
			dcons_init((struct dcons_buf *)nbuf, sizeof(nbuf), dsc);
			dcons_putc(&dsc[DCONS_CON], 'A');
			kprintf("dconsnull: NEGCTRL init+putc OK, checkc=%c\n",
			    dcons_checkc(&dsc[DCONS_CON]));
			break;
		}
		if (mode == 1) {
			kprintf("dconsnull: DF-4781: calling dcons_init(NULL, "
			    "16384, sc) -- unchecked contigmalloc sink\n");
			dcons_init(NULL, 16 * 1024, dsc);
			kprintf("dconsnull: NOT REACHED\n");
			break;
		}
		if (mode == 2) {
			kprintf("dconsnull: DF-4782: calling dcons_putc(NULL, "
			    "'A') -- static-console si_drv1==NULL sink\n");
			dcons_putc(NULL, 'A');
			kprintf("dconsnull: NOT REACHED\n");
			break;
		}
		break;
	default:
		break;
	}
	return (0);
}

DEV_MODULE(dconsnull, dconsnull_modevent, NULL);
MODULE_DEPEND(dconsnull, dcons, DCONS_VERSION, DCONS_VERSION,
    DCONS_VERSION);
