ANDROID: input: keychord: Fix a slab out-of-bounds read.
authorMohan Srinivasan <srmohan@google.com>
Wed, 26 Jul 2017 19:14:41 +0000 (12:14 -0700)
committerAmit Pundir <amit.pundir@linaro.org>
Mon, 18 Dec 2017 15:41:22 +0000 (21:11 +0530)
Fix a slab out of bounds read in keychord_write(), detected by KASAN.

Signed-off-by: Mohan Srinivasan <srmohan@google.com>
Bug: 63962952
Change-Id: Iafef48b5d7283750ac0f39f5aaa767b1c3bf2004

[AmitP: Folded following android-4.9 commit changes into this patch
        a1e4c795e1b6 ("Use %zu to print resid (size_t).")]
Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
drivers/input/misc/keychord.c

index 9e568b0a09013699faf2c26531dc1c8a76913ece..a3ae4586850c9f381e1db0a2cfb5be641b655756 100644 (file)
@@ -232,9 +232,11 @@ static ssize_t keychord_write(struct file *file, const char __user *buffer,
 {
        struct keychord_device *kdev = file->private_data;
        struct input_keychord *keychords = 0;
-       struct input_keychord *keychord, *next, *end;
+       struct input_keychord *keychord;
        int ret, i, key;
        unsigned long flags;
+       size_t resid = count;
+       size_t key_bytes;
 
        if (count < sizeof(struct input_keychord))
                return -EINVAL;
@@ -265,15 +267,29 @@ static ssize_t keychord_write(struct file *file, const char __user *buffer,
        kdev->head = kdev->tail = 0;
 
        keychord = keychords;
-       end = (struct input_keychord *)((char *)keychord + count);
 
-       while (keychord < end) {
-               next = NEXT_KEYCHORD(keychord);
-               if (keychord->count <= 0 || next > end) {
+       while (resid > 0) {
+               /* Is the entire keychord entry header present ? */
+               if (resid < sizeof(struct input_keychord)) {
+                       pr_err("keychord: Insufficient bytes present for header %zu\n",
+                              resid);
+                       goto err_unlock_return;
+               }
+               resid -= sizeof(struct input_keychord);
+               if (keychord->count <= 0) {
                        pr_err("keychord: invalid keycode count %d\n",
                                keychord->count);
                        goto err_unlock_return;
                }
+               key_bytes = keychord->count * sizeof(keychord->keycodes[0]);
+               /* Do we have all the expected keycodes ? */
+               if (resid < key_bytes) {
+                       pr_err("keychord: Insufficient bytes present for keycount %zu\n",
+                              resid);
+                       goto err_unlock_return;
+               }
+               resid -= key_bytes;
+
                if (keychord->version != KEYCHORD_VERSION) {
                        pr_err("keychord: unsupported version %d\n",
                                keychord->version);
@@ -292,7 +308,7 @@ static ssize_t keychord_write(struct file *file, const char __user *buffer,
                }
 
                kdev->keychord_count++;
-               keychord = next;
+               keychord = NEXT_KEYCHORD(keychord);
        }
 
        kdev->keychords = keychords;