| From 77e70d351db7de07a46ac49b87a6c3c7a60fca7e Mon Sep 17 00:00:00 2001 |
| From: Dmitry Torokhov <dmitry.torokhov@gmail.com> |
| Date: Mon, 26 Oct 2020 13:36:17 -0700 |
| Subject: Input: sunkbd - avoid use-after-free in teardown paths |
| |
| From: Dmitry Torokhov <dmitry.torokhov@gmail.com> |
| |
| commit 77e70d351db7de07a46ac49b87a6c3c7a60fca7e upstream. |
| |
| 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> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/input/keyboard/sunkbd.c | 41 ++++++++++++++++++++++++++++++++-------- |
| 1 file changed, 33 insertions(+), 8 deletions(-) |
| |
| --- a/drivers/input/keyboard/sunkbd.c |
| +++ b/drivers/input/keyboard/sunkbd.c |
| @@ -99,7 +99,8 @@ static irqreturn_t sunkbd_interrupt(stru |
| switch (data) { |
| |
| case SUNKBD_RET_RESET: |
| - schedule_work(&sunkbd->tq); |
| + if (sunkbd->enabled) |
| + schedule_work(&sunkbd->tq); |
| sunkbd->reset = -1; |
| break; |
| |
| @@ -200,16 +201,12 @@ static int sunkbd_initialize(struct sunk |
| } |
| |
| /* |
| - * sunkbd_reinit() sets leds and beeps to a state the computer remembers they |
| - * were in. |
| + * sunkbd_set_leds_beeps() sets leds and beeps to a state the computer remembers |
| + * they were in. |
| */ |
| |
| -static void sunkbd_reinit(struct work_struct *work) |
| +static void sunkbd_set_leds_beeps(struct sunkbd *sunkbd) |
| { |
| - struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq); |
| - |
| - wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ); |
| - |
| serio_write(sunkbd->serio, SUNKBD_CMD_SETLED); |
| serio_write(sunkbd->serio, |
| (!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) | |
| @@ -222,11 +219,39 @@ static void sunkbd_reinit(struct work_st |
| SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd)); |
| } |
| |
| + |
| +/* |
| + * sunkbd_reinit() wait for the keyboard reset to complete and restores state |
| + * of leds and beeps. |
| + */ |
| + |
| +static void sunkbd_reinit(struct work_struct *work) |
| +{ |
| + struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq); |
| + |
| + /* |
| + * It is OK that we check sunkbd->enabled without pausing serio, |
| + * as we only want to catch true->false transition that will |
| + * happen once and we will be woken up for it. |
| + */ |
| + wait_event_interruptible_timeout(sunkbd->wait, |
| + sunkbd->reset >= 0 || !sunkbd->enabled, |
| + HZ); |
| + |
| + if (sunkbd->reset >= 0 && sunkbd->enabled) |
| + sunkbd_set_leds_beeps(sunkbd); |
| +} |
| + |
| static void sunkbd_enable(struct sunkbd *sunkbd, bool enable) |
| { |
| serio_pause_rx(sunkbd->serio); |
| sunkbd->enabled = enable; |
| serio_continue_rx(sunkbd->serio); |
| + |
| + if (!enable) { |
| + wake_up_interruptible(&sunkbd->wait); |
| + cancel_work_sync(&sunkbd->tq); |
| + } |
| } |
| |
| /* |