summaryrefslogtreecommitdiffstats
path: root/drivers/input/serio
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2015-09-29 18:54:58 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2015-09-29 19:08:29 -0400
commit218c1f76b8b25d6dc9d01443f071cb618e206b0c (patch)
treeecd48ba4dadb9da65b5ccb3cb86afc8f692b1fdd /drivers/input/serio
parent22ef28b43f2c70edf5618918a49cbda84795c0a5 (diff)
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: Dmitry Vyukov <dvyukov@google.com> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input/serio')
-rw-r--r--drivers/input/serio/libps2.c22
1 files changed, 14 insertions, 8 deletions
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
index 75516996db20..316f2c897101 100644
--- a/drivers/input/serio/libps2.c
+++ b/drivers/input/serio/libps2.c
@@ -212,12 +212,17 @@ int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
212 * time before the ACK arrives. 212 * time before the ACK arrives.
213 */ 213 */
214 if (ps2_sendbyte(ps2dev, command & 0xff, 214 if (ps2_sendbyte(ps2dev, command & 0xff,
215 command == PS2_CMD_RESET_BAT ? 1000 : 200)) 215 command == PS2_CMD_RESET_BAT ? 1000 : 200)) {
216 goto out; 216 serio_pause_rx(ps2dev->serio);
217 goto out_reset_flags;
218 }
217 219
218 for (i = 0; i < send; i++) 220 for (i = 0; i < send; i++) {
219 if (ps2_sendbyte(ps2dev, param[i], 200)) 221 if (ps2_sendbyte(ps2dev, param[i], 200)) {
220 goto out; 222 serio_pause_rx(ps2dev->serio);
223 goto out_reset_flags;
224 }
225 }
221 226
222 /* 227 /*
223 * The reset command takes a long time to execute. 228 * The reset command takes a long time to execute.
@@ -234,17 +239,18 @@ int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
234 !(ps2dev->flags & PS2_FLAG_CMD), timeout); 239 !(ps2dev->flags & PS2_FLAG_CMD), timeout);
235 } 240 }
236 241
242 serio_pause_rx(ps2dev->serio);
243
237 if (param) 244 if (param)
238 for (i = 0; i < receive; i++) 245 for (i = 0; i < receive; i++)
239 param[i] = ps2dev->cmdbuf[(receive - 1) - i]; 246 param[i] = ps2dev->cmdbuf[(receive - 1) - i];
240 247
241 if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1)) 248 if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1))
242 goto out; 249 goto out_reset_flags;
243 250
244 rc = 0; 251 rc = 0;
245 252
246 out: 253 out_reset_flags:
247 serio_pause_rx(ps2dev->serio);
248 ps2dev->flags = 0; 254 ps2dev->flags = 0;
249 serio_continue_rx(ps2dev->serio); 255 serio_continue_rx(ps2dev->serio);
250 256