Skip to content

Commit 77e70d3

Browse files
committed
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 <[email protected]> Tested-by: Bodong Zhao <[email protected]> Cc: [email protected] Signed-off-by: Dmitry Torokhov <[email protected]>
1 parent b188458 commit 77e70d3

File tree

1 file changed

+33
-8
lines changed

1 file changed

+33
-8
lines changed

drivers/input/keyboard/sunkbd.c

+33-8
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ static irqreturn_t sunkbd_interrupt(struct serio *serio,
9999
switch (data) {
100100

101101
case SUNKBD_RET_RESET:
102-
schedule_work(&sunkbd->tq);
102+
if (sunkbd->enabled)
103+
schedule_work(&sunkbd->tq);
103104
sunkbd->reset = -1;
104105
break;
105106

@@ -200,16 +201,12 @@ static int sunkbd_initialize(struct sunkbd *sunkbd)
200201
}
201202

202203
/*
203-
* sunkbd_reinit() sets leds and beeps to a state the computer remembers they
204-
* were in.
204+
* sunkbd_set_leds_beeps() sets leds and beeps to a state the computer remembers
205+
* they were in.
205206
*/
206207

207-
static void sunkbd_reinit(struct work_struct *work)
208+
static void sunkbd_set_leds_beeps(struct sunkbd *sunkbd)
208209
{
209-
struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq);
210-
211-
wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
212-
213210
serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
214211
serio_write(sunkbd->serio,
215212
(!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) |
@@ -222,11 +219,39 @@ static void sunkbd_reinit(struct work_struct *work)
222219
SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
223220
}
224221

222+
223+
/*
224+
* sunkbd_reinit() wait for the keyboard reset to complete and restores state
225+
* of leds and beeps.
226+
*/
227+
228+
static void sunkbd_reinit(struct work_struct *work)
229+
{
230+
struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq);
231+
232+
/*
233+
* It is OK that we check sunkbd->enabled without pausing serio,
234+
* as we only want to catch true->false transition that will
235+
* happen once and we will be woken up for it.
236+
*/
237+
wait_event_interruptible_timeout(sunkbd->wait,
238+
sunkbd->reset >= 0 || !sunkbd->enabled,
239+
HZ);
240+
241+
if (sunkbd->reset >= 0 && sunkbd->enabled)
242+
sunkbd_set_leds_beeps(sunkbd);
243+
}
244+
225245
static void sunkbd_enable(struct sunkbd *sunkbd, bool enable)
226246
{
227247
serio_pause_rx(sunkbd->serio);
228248
sunkbd->enabled = enable;
229249
serio_continue_rx(sunkbd->serio);
250+
251+
if (!enable) {
252+
wake_up_interruptible(&sunkbd->wait);
253+
cancel_work_sync(&sunkbd->tq);
254+
}
230255
}
231256

232257
/*

0 commit comments

Comments
 (0)