/*
 * DF-5777 PoC: splash() calls splash_decoder->splash without a NULL guard.
 *
 * sys/dev/video/fb/splash.c:201-206
 *
 *   int
 *   splash(video_adapter_t *adp, int on)
 *   {
 *           if (splash_decoder != NULL)
 *                   return (*splash_decoder->splash)(adp, on);
 *           return ENODEV;
 *   }
 *
 * `init` (splash.c:83 `if (*decoder->init && ...)`) and `term`
 * (splash.c:193 `if (error == 0 && splash_decoder->term)`) are both
 * NULL-guarded; `->splash` is not.  Every in-tree registrant (bmp, pcx and
 * the 10 syscons savers) fills .splash, so the gap is reachable via a
 * root-loaded decoder module that leaves it NULL -- this module.
 *
 * .data_type is NULL so splash_find_data() returns 0 (splash.c:59-60) and
 * .init returns 0 so splash_test() succeeds; after kldload the module's
 * decoder becomes the active splash_decoder (splash_new, splash.c:94-99).
 *
 * The sysctl trigger then performs exactly the call the in-tree caller
 * scsplash_saver() makes (syscons.c:2560): splash(sc->adp, TRUE) --
 * using the real video_adapter_t captured during decoder init.
 */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/sysctl.h>
#include <sys/fbio.h>

#include <dev/video/fb/fbreg.h>
#include <dev/video/fb/splashreg.h>

static video_adapter_t *real_adp;

static int
nullsplash_init(struct video_adapter *adp)
{
	real_adp = adp;
	kprintf("nullsplash: decoder init, adp=%p (.splash left NULL)\n", adp);
	return (0);
}

static int
nullsplash_term(struct video_adapter *adp)
{
	return (0);
}

/* .splash deliberately NULL */
static splash_decoder_t nullsplash_decoder = {
	"nullsplash", nullsplash_init, nullsplash_term, NULL, NULL,
};

static int
sysctl_nullsplash_fire(SYSCTL_HANDLER_ARGS)
{
	int fire = 0, error;

	error = sysctl_handle_int(oidp, &fire, 0, req);
	if (error != 0 || req->newptr == NULL)
		return (error);
	if (fire) {
		kprintf("nullsplash: calling splash(adp=%p, TRUE); "
		    "->splash == NULL and splash.c:205 has no guard\n",
		    real_adp);
		/* mirrors scsplash_saver() at syscons.c:2560 */
		splash(real_adp, TRUE);
		kprintf("nullsplash: returned -- guard present?\n");
	}
	return (0);
}

SYSCTL_PROC(_hw, OID_AUTO, nullsplash_fire, CTLTYPE_INT | CTLFLAG_RW,
    0, 0, sysctl_nullsplash_fire, "I",
    "call splash() while active decoder has .splash == NULL");

SPLASH_DECODER(nullsplash, nullsplash_decoder);
