From 0defbd9aca4620a135f08b0f9073c168e8263c80 Mon Sep 17 00:00:00 2001 From: Taylor R Campbell <riastradh@NetBSD.org> Date: Sat, 25 Dec 2021 14:04:55 +0000 Subject: [PATCH] ukbd(4): Avoid races in LED setting on attach. - Don't reuse sc_delay for LED task -- a keyboard interrupt shortly after attach might reset sc_delay so that the LEDs never get turned back off. - Don't turn the LEDs back off after attach if something else has already changed them by the time the callout fires. (And make sure to callout_halt before done this time!) --- sys/dev/usb/ukbd.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sys/dev/usb/ukbd.c b/sys/dev/usb/ukbd.c index 90db0bccf05d..45a92535c38c 100644 --- a/sys/dev/usb/ukbd.c +++ b/sys/dev/usb/ukbd.c @@ -270,6 +270,8 @@ struct ukbd_softc { struct hid_location sc_compose; int sc_leds; struct usb_task sc_ledtask; + struct callout sc_ledreset; + int sc_leds_set; device_t sc_wskbddev; #if defined(WSDISPLAY_COMPAT_RAWKBD) @@ -483,11 +485,13 @@ ukbd_attach(device_t parent, device_t self, void *aux) sc->sc_data_r = 0; usb_init_task(&sc->sc_ledtask, ukbd_set_leds_task, sc, 0); + callout_init(&sc->sc_ledreset, 0); /* Flash the leds; no real purpose, just shows we're alive. */ ukbd_set_leds(sc, WSKBD_LED_SCROLL | WSKBD_LED_NUM | WSKBD_LED_CAPS | WSKBD_LED_COMPOSE); - callout_reset(&sc->sc_delay, mstohz(400), ukbd_delayed_leds_off, sc); + callout_reset(&sc->sc_ledreset, mstohz(400), ukbd_delayed_leds_off, + sc); sc->sc_wskbddev = config_found(self, &a, wskbddevprint, CFARGS_NONE); @@ -573,6 +577,7 @@ ukbd_detach(device_t self, int flags) rv = config_detach(sc->sc_wskbddev, flags); callout_halt(&sc->sc_delay, NULL); + callout_halt(&sc->sc_ledreset, NULL); usb_rem_task_wait(sc->sc_hdev.sc_parent->sc_udev, &sc->sc_ledtask, USB_TASKQ_DRIVER, NULL); @@ -715,6 +720,13 @@ ukbd_delayed_leds_off(void *addr) { struct ukbd_softc *sc = addr; + /* + * If the LEDs have already been set after attach, other than + * by our initial flashing of them, leave them be. + */ + if (sc->sc_leds_set) + return; + ukbd_set_leds(sc, 0); } @@ -880,6 +892,8 @@ ukbd_set_leds(void *v, int leds) if (sc->sc_dying) return; + sc->sc_leds_set = 1; + if (sc->sc_leds == leds) return;