Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8bf2e8fe authored by Dmitry Torokhov's avatar Dmitry Torokhov Committed by Greg Kroah-Hartman
Browse files

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



commit 77e70d351db7de07a46ac49b87a6c3c7a60fca7e upstream.

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

Reported-by: default avatarBodong Zhao <nopitydays@gmail.com>
Tested-by: default avatarBodong Zhao <nopitydays@gmail.com>
Cc: stable@vger.kernel.org
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent f1b0b8c2
Loading
Loading
Loading
Loading
+33 −8
Original line number Original line Diff line number Diff line
@@ -111,6 +111,7 @@ static irqreturn_t sunkbd_interrupt(struct serio *serio,
	switch (data) {
	switch (data) {


	case SUNKBD_RET_RESET:
	case SUNKBD_RET_RESET:
		if (sunkbd->enabled)
			schedule_work(&sunkbd->tq);
			schedule_work(&sunkbd->tq);
		sunkbd->reset = -1;
		sunkbd->reset = -1;
		break;
		break;
@@ -212,16 +213,12 @@ static int sunkbd_initialize(struct sunkbd *sunkbd)
}
}


/*
/*
 * sunkbd_reinit() sets leds and beeps to a state the computer remembers they
 * sunkbd_set_leds_beeps() sets leds and beeps to a state the computer remembers
 * were in.
 * 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, SUNKBD_CMD_SETLED);
	serio_write(sunkbd->serio,
	serio_write(sunkbd->serio,
		(!!test_bit(LED_CAPSL,   sunkbd->dev->led) << 3) |
		(!!test_bit(LED_CAPSL,   sunkbd->dev->led) << 3) |
@@ -234,11 +231,39 @@ static void sunkbd_reinit(struct work_struct *work)
		SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
		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)
static void sunkbd_enable(struct sunkbd *sunkbd, bool enable)
{
{
	serio_pause_rx(sunkbd->serio);
	serio_pause_rx(sunkbd->serio);
	sunkbd->enabled = enable;
	sunkbd->enabled = enable;
	serio_continue_rx(sunkbd->serio);
	serio_continue_rx(sunkbd->serio);

	if (!enable) {
		wake_up_interruptible(&sunkbd->wait);
		cancel_work_sync(&sunkbd->tq);
	}
}
}


/*
/*