Skip to content

Commit

Permalink
Input: psmouse - fix data race in __ps2_command
Browse files Browse the repository at this point in the history
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: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
  • Loading branch information
Dmitry Vyukov authored and Dmitry Torokhov committed Sep 29, 2015
1 parent 22ef28b commit 218c1f7
Showing 1 changed file with 14 additions and 8 deletions.
22 changes: 14 additions & 8 deletions drivers/input/serio/libps2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);

Expand Down

0 comments on commit 218c1f7

Please sign in to comment.