Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: sunkbd - avoid use-after-free in teardown paths

We need to make sure we cancel the reinit work before we tear down the
driver structures.

Reported-by: Bodong Zhao <nopitydays@gmail.com>
Tested-by: Bodong Zhao <nopitydays@gmail.com>
Cc: stable@vger.kernel.org
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

+33 -8
+33 -8
drivers/input/keyboard/sunkbd.c
··· 99 99 switch (data) { 100 100 101 101 case SUNKBD_RET_RESET: 102 - schedule_work(&sunkbd->tq); 102 + if (sunkbd->enabled) 103 + schedule_work(&sunkbd->tq); 103 104 sunkbd->reset = -1; 104 105 break; 105 106 ··· 201 200 } 202 201 203 202 /* 204 - * sunkbd_reinit() sets leds and beeps to a state the computer remembers they 205 - * were in. 203 + * sunkbd_set_leds_beeps() sets leds and beeps to a state the computer remembers 204 + * they were in. 206 205 */ 207 206 208 - static void sunkbd_reinit(struct work_struct *work) 207 + static void sunkbd_set_leds_beeps(struct sunkbd *sunkbd) 209 208 { 210 - struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq); 211 - 212 - wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ); 213 - 214 209 serio_write(sunkbd->serio, SUNKBD_CMD_SETLED); 215 210 serio_write(sunkbd->serio, 216 211 (!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) | ··· 219 222 SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd)); 220 223 } 221 224 225 + 226 + /* 227 + * sunkbd_reinit() wait for the keyboard reset to complete and restores state 228 + * of leds and beeps. 229 + */ 230 + 231 + static void sunkbd_reinit(struct work_struct *work) 232 + { 233 + struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq); 234 + 235 + /* 236 + * It is OK that we check sunkbd->enabled without pausing serio, 237 + * as we only want to catch true->false transition that will 238 + * happen once and we will be woken up for it. 239 + */ 240 + wait_event_interruptible_timeout(sunkbd->wait, 241 + sunkbd->reset >= 0 || !sunkbd->enabled, 242 + HZ); 243 + 244 + if (sunkbd->reset >= 0 && sunkbd->enabled) 245 + sunkbd_set_leds_beeps(sunkbd); 246 + } 247 + 222 248 static void sunkbd_enable(struct sunkbd *sunkbd, bool enable) 223 249 { 224 250 serio_pause_rx(sunkbd->serio); 225 251 sunkbd->enabled = enable; 226 252 serio_continue_rx(sunkbd->serio); 253 + 254 + if (!enable) { 255 + wake_up_interruptible(&sunkbd->wait); 256 + cancel_work_sync(&sunkbd->tq); 257 + } 227 258 } 228 259 229 260 /*