Skip to content

Commit

Permalink
Input: sunkbd - avoid use-after-free in teardown paths
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
dtor committed Nov 9, 2020
1 parent b188458 commit 77e70d3
Showing 1 changed file with 33 additions and 8 deletions.
41 changes: 33 additions & 8 deletions drivers/input/keyboard/sunkbd.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ static irqreturn_t sunkbd_interrupt(struct serio *serio,
switch (data) {

case SUNKBD_RET_RESET:
schedule_work(&sunkbd->tq);
if (sunkbd->enabled)
schedule_work(&sunkbd->tq);
sunkbd->reset = -1;
break;

Expand Down Expand Up @@ -200,16 +201,12 @@ static int sunkbd_initialize(struct sunkbd *sunkbd)
}

/*
* 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) |
Expand All @@ -222,11 +219,39 @@ static void sunkbd_reinit(struct work_struct *work)
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);
}
}

/*
Expand Down

0 comments on commit 77e70d3

Please sign in to comment.