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

Commit 218c1f76 authored by Dmitry Vyukov's avatar Dmitry Vyukov Committed by Dmitry Torokhov
Browse files

Input: psmouse - fix data race in __ps2_command



The data race happens on ps2dev->cmdcnt and ps2dev->cmdbuf contents.
__ps2_command reads that data concurrently with the interrupt handler. As
the result, for example, if a response arrives just after the timeout,
__ps2_command can copy out garbage from ps2dev->cmdbuf but then see that
ps2dev->cmdcnt is 0 and return success.

Stop the interrupt handler with serio_pause_rx() before reading the
results.

The data race was found with KernelThreadSanitizer (KTSAN).

Signed-off-by: default avatarDmitry Vyukov <dvyukov@google.com>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 22ef28b4
Loading
Loading
Loading
Loading
+14 −8
Original line number Diff line number Diff line
@@ -212,12 +212,17 @@ int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
	 * time before the ACK arrives.
	 */
	if (ps2_sendbyte(ps2dev, command & 0xff,
			 command == PS2_CMD_RESET_BAT ? 1000 : 200))
		goto out;
			 command == PS2_CMD_RESET_BAT ? 1000 : 200)) {
		serio_pause_rx(ps2dev->serio);
		goto out_reset_flags;
	}

	for (i = 0; i < send; i++)
		if (ps2_sendbyte(ps2dev, param[i], 200))
			goto out;
	for (i = 0; i < send; i++) {
		if (ps2_sendbyte(ps2dev, param[i], 200)) {
			serio_pause_rx(ps2dev->serio);
			goto out_reset_flags;
		}
	}

	/*
	 * The reset command takes a long time to execute.
@@ -234,17 +239,18 @@ int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
				   !(ps2dev->flags & PS2_FLAG_CMD), timeout);
	}

	serio_pause_rx(ps2dev->serio);

	if (param)
		for (i = 0; i < receive; i++)
			param[i] = ps2dev->cmdbuf[(receive - 1) - i];

	if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1))
		goto out;
		goto out_reset_flags;

	rc = 0;

 out:
	serio_pause_rx(ps2dev->serio);
 out_reset_flags:
	ps2dev->flags = 0;
	serio_continue_rx(ps2dev->serio);