| From: Dmitry Torokhov <dmitry.torokhov@gmail.com> |
| Date: Fri, 13 Dec 2019 14:56:16 -0800 |
| Subject: Input: add safety guards to input_set_keycode() |
| |
| commit cb222aed03d798fc074be55e59d9a112338ee784 upstream. |
| |
| 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> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| drivers/input/input.c | 26 ++++++++++++++++---------- |
| 1 file changed, 16 insertions(+), 10 deletions(-) |
| |
| --- a/drivers/input/input.c |
| +++ b/drivers/input/input.c |
| @@ -841,16 +841,18 @@ static int input_default_setkeycode(stru |
| } |
| } |
| |
| - __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; |
| } |
| |
| @@ -906,9 +908,13 @@ int input_set_keycode(struct input_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 |