Input: add safety guards to input_set_keycode()
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 13 Dec 2019 22:56:16 +0000 (14:56 -0800)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 13 Dec 2019 23:00:42 +0000 (15:00 -0800)
If we happen to have a garbage in input device's keycode table with values
too big we'll end up doing clear_bit() with offset way outside of our
bitmaps, damaging other objects within an input device or even outside of
it. Let's add sanity checks to the returned old keycodes.

Reported-by: syzbot+c769968809f9359b07aa@syzkaller.appspotmail.com
Reported-by: syzbot+76f3a30e88d256644c78@syzkaller.appspotmail.com
Link: https://lore.kernel.org/r/20191207212757.GA245964@dtor-ws
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/input.c

index 55086279d044e752e2e009639d36bb89e2462f4d..ee6c3234df3634800f257207b3970517d5e68384 100644 (file)
@@ -878,16 +878,18 @@ static int input_default_setkeycode(struct input_dev *dev,
                }
        }
 
-       __clear_bit(*old_keycode, dev->keybit);
-       __set_bit(ke->keycode, dev->keybit);
-
-       for (i = 0; i < dev->keycodemax; i++) {
-               if (input_fetch_keycode(dev, i) == *old_keycode) {
-                       __set_bit(*old_keycode, dev->keybit);
-                       break; /* Setting the bit twice is useless, so break */
+       if (*old_keycode <= KEY_MAX) {
+               __clear_bit(*old_keycode, dev->keybit);
+               for (i = 0; i < dev->keycodemax; i++) {
+                       if (input_fetch_keycode(dev, i) == *old_keycode) {
+                               __set_bit(*old_keycode, dev->keybit);
+                               /* Setting the bit twice is useless, so break */
+                               break;
+                       }
                }
        }
 
+       __set_bit(ke->keycode, dev->keybit);
        return 0;
 }
 
@@ -943,9 +945,13 @@ int input_set_keycode(struct input_dev *dev,
         * Simulate keyup event if keycode is not present
         * in the keymap anymore
         */
-       if (test_bit(EV_KEY, dev->evbit) &&
-           !is_event_supported(old_keycode, dev->keybit, KEY_MAX) &&
-           __test_and_clear_bit(old_keycode, dev->key)) {
+       if (old_keycode > KEY_MAX) {
+               dev_warn(dev->dev.parent ?: &dev->dev,
+                        "%s: got too big old keycode %#x\n",
+                        __func__, old_keycode);
+       } else if (test_bit(EV_KEY, dev->evbit) &&
+                  !is_event_supported(old_keycode, dev->keybit, KEY_MAX) &&
+                  __test_and_clear_bit(old_keycode, dev->key)) {
                struct input_value vals[] =  {
                        { EV_KEY, old_keycode, 0 },
                        input_value_sync