--- a/sys/dev/misc/kbd/kbd.c +++ b/sys/dev/misc/kbd/kbd.c @@ -582,6 +582,7 @@ kbd_detach(keyboard_t *kbd) { cdev_t dev; + genkbd_softc_t dsc; lwkt_gettoken(&kbd_token); @@ -595,8 +596,23 @@ } if ((dev = kbd->kb_dev) != NULL) { - if (dev->si_drv1) { - kfree(dev->si_drv1, M_DEVBUF); + if ((dsc = dev->si_drv1) != NULL) { + /* + * DF-4297: an open /dev/kbdN may still hold this + * genkbd_softc: genkbdread sleeps on it (see the + * tsleep below) and kbd_allocate registered it as + * kb_callback.kc_arg. Release the callback first + * so a later kbd_unregister cannot feed the stale + * sc to genkbd_event (KNOTE on freed memory), then + * wake blocked readers (they re-fetch dev->si_drv1 + * after wakeup and exit ENXIO once it is NULL), and + * only then free the sc. + */ + if (KBD_IS_BUSY(kbd)) + kbd_release(kbd, kbd->kb_token); + KNOTE(&dsc->gkb_rkq.ki_note, 0); + wakeup(dsc); + kfree(dsc, M_DEVBUF); dev->si_drv1 = NULL; } kbd->kb_dev = NULL; @@ -748,6 +764,11 @@ } sc->gkb_flags |= KB_ASLEEP; error = tsleep((caddr_t)sc, PCATCH, "kbdrea", 0); + sc = dev->si_drv1; /* DF-4297: kbd_detach may free sc */ + if (sc == NULL) { + lwkt_reltoken(&kbd_token); + return (ENXIO); + } kbd = kbd_get_keyboard(KBD_INDEX(dev)); if ((kbd == NULL) || !KBD_IS_VALID(kbd)) { lwkt_reltoken(&kbd_token); @@ -759,6 +780,11 @@ return error; } } + sc = dev->si_drv1; /* DF-4297: re-fetch under token */ + if (sc == NULL) { + lwkt_reltoken(&kbd_token); + return (ENXIO); + } lwkt_reltoken(&kbd_token); /* copy as much input as possible */