DF-2681 / dfrec.c
/* * DF-2681 PoC: bus driver that installs bus_generic_delete_resource() * (subr_bus.c:3144) as its bus_delete_resource method -- an exported * NEWBUS default intended for exactly this use -- and then deletes a * resource on one of its children. bus_generic_delete_resource() * dispatches BUS_DELETE_RESOURCE() on ITSELF instead of dev->parent, * so the call recurses forever until the kernel stack is exhausted. * * Load as root: kldload ./dfrec.ko -> panic during attach. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/bus.h> #include <sys/systm.h> struct dfrec_softc { device_t child; }; static void dfrec_identify(driver_t *, device_t); static int dfrec_probe(device_t); static int dfrec_attach(device_t); static int dfrec_detach(device_t); static device_method_t dfrec_methods[] = { /* Device interface */ DEVMETHOD(device_identify, dfrec_identify), DEVMETHOD(device_probe, dfrec_probe), DEVMETHOD(device_attach, dfrec_attach), DEVMETHOD(device_detach, dfrec_detach), /* Bus interface: the shipped "generic" default that recurses */ DEVMETHOD(bus_delete_resource, bus_generic_delete_resource), DEVMETHOD_END }; static driver_t dfrec_driver = { "dfrec", dfrec_methods, sizeof(struct dfrec_softc) }; static devclass_t dfrec_devclass; DRIVER_MODULE(dfrec, nexus, dfrec_driver, dfrec_devclass, NULL, NULL); static void dfrec_identify(driver_t *driver, device_t parent) { if (device_find_child(parent, "dfrec", -1) == NULL) BUS_ADD_CHILD(parent, parent, 0, "dfrec", -1); } static int dfrec_probe(device_t dev) { return (0); } static int dfrec_attach(device_t dev) { struct dfrec_softc *sc = device_get_softc(dev); sc->child = device_add_child(dev, "dfrecchild", 0); kprintf("dfrec: attached, calling bus_delete_resource() on child\n"); /* dispatch on dfrec bus -> bus_generic_delete_resource -> itself */ bus_delete_resource(sc->child, SYS_RES_IRQ, 0); kprintf("dfrec: returned (no bug)\n"); return (0); } static int dfrec_detach(device_t dev) { struct dfrec_softc *sc = device_get_softc(dev); if (sc->child) device_delete_child(dev, sc->child); return (0); } |