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

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
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment