From: Greg Kroah-Hartman Date: Wed, 13 Feb 2019 14:50:03 +0000 (+0100) Subject: 3.18-stable patches X-Git-Tag: v4.9.157~15 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9abc65a34e42c4f77bbb3a2665534b696f972ed3;p=thirdparty%2Fkernel%2Fstable-queue.git 3.18-stable patches added patches: hid-debug-fix-the-ring-buffer-implementation.patch --- diff --git a/queue-3.18/hid-debug-fix-the-ring-buffer-implementation.patch b/queue-3.18/hid-debug-fix-the-ring-buffer-implementation.patch new file mode 100644 index 00000000000..38caa6b1f5c --- /dev/null +++ b/queue-3.18/hid-debug-fix-the-ring-buffer-implementation.patch @@ -0,0 +1,275 @@ +From 13054abbaa4f1fd4e6f3b4b63439ec033b4c8035 Mon Sep 17 00:00:00 2001 +From: Vladis Dronov +Date: Tue, 29 Jan 2019 11:58:35 +0100 +Subject: HID: debug: fix the ring buffer implementation + +From: Vladis Dronov + +commit 13054abbaa4f1fd4e6f3b4b63439ec033b4c8035 upstream. + +Ring buffer implementation in hid_debug_event() and hid_debug_events_read() +is strange allowing lost or corrupted data. After commit 717adfdaf147 +("HID: debug: check length before copy_to_user()") it is possible to enter +an infinite loop in hid_debug_events_read() by providing 0 as count, this +locks up a system. Fix this by rewriting the ring buffer implementation +with kfifo and simplify the code. + +This fixes CVE-2019-3819. + +v2: fix an execution logic and add a comment +v3: use __set_current_state() instead of set_current_state() + +Backport to v3.18: some (tree-wide) patches are missing in v3.18 so +cherry-pick relevant pieces from: + * 6396bb221514 ("treewide: kzalloc() -> kcalloc()") + * a9a08845e9ac ("vfs: do bulk POLL* -> EPOLL* replacement") + * 92529623d242 ("HID: debug: improve hid_debug_event()") + * 174cd4b1e5fb ("sched/headers: Prepare to move signal wakeup & sigpending + methods from into ") + * 8fec02a73e31 ("HID: debug: fix error handling in hid_debug_events_read()") + + +Link: https://bugzilla.redhat.com/show_bug.cgi?id=1669187 +Cc: stable@vger.kernel.org # v4.18+ +Fixes: cd667ce24796 ("HID: use debugfs for events/reports dumping") +Fixes: 717adfdaf147 ("HID: debug: check length before copy_to_user()") +Signed-off-by: Vladis Dronov +Reviewed-by: Oleg Nesterov +Signed-off-by: Benjamin Tissoires +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/hid/hid-debug.c | 121 ++++++++++++++++++---------------------------- + include/linux/hid-debug.h | 9 +-- + 2 files changed, 52 insertions(+), 78 deletions(-) + +--- a/drivers/hid/hid-debug.c ++++ b/drivers/hid/hid-debug.c +@@ -30,6 +30,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -454,7 +455,7 @@ static char *resolv_usage_page(unsigned + char *buf = NULL; + + if (!f) { +- buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC); ++ buf = kzalloc(HID_DEBUG_BUFSIZE, GFP_ATOMIC); + if (!buf) + return ERR_PTR(-ENOMEM); + } +@@ -658,17 +659,12 @@ EXPORT_SYMBOL_GPL(hid_dump_device); + /* enqueue string to 'events' ring buffer */ + void hid_debug_event(struct hid_device *hdev, char *buf) + { +- int i; + struct hid_debug_list *list; + unsigned long flags; + + spin_lock_irqsave(&hdev->debug_list_lock, flags); +- list_for_each_entry(list, &hdev->debug_list, node) { +- for (i = 0; i < strlen(buf); i++) +- list->hid_debug_buf[(list->tail + i) % HID_DEBUG_BUFSIZE] = +- buf[i]; +- list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE; +- } ++ list_for_each_entry(list, &hdev->debug_list, node) ++ kfifo_in(&list->hid_debug_fifo, buf, strlen(buf)); + spin_unlock_irqrestore(&hdev->debug_list_lock, flags); + + wake_up_interruptible(&hdev->debug_wait); +@@ -719,8 +715,7 @@ void hid_dump_input(struct hid_device *h + hid_debug_event(hdev, buf); + + kfree(buf); +- wake_up_interruptible(&hdev->debug_wait); +- ++ wake_up_interruptible(&hdev->debug_wait); + } + EXPORT_SYMBOL_GPL(hid_dump_input); + +@@ -1085,8 +1080,8 @@ static int hid_debug_events_open(struct + goto out; + } + +- if (!(list->hid_debug_buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_KERNEL))) { +- err = -ENOMEM; ++ err = kfifo_alloc(&list->hid_debug_fifo, HID_DEBUG_FIFOSIZE, GFP_KERNEL); ++ if (err) { + kfree(list); + goto out; + } +@@ -1106,76 +1101,57 @@ static ssize_t hid_debug_events_read(str + size_t count, loff_t *ppos) + { + struct hid_debug_list *list = file->private_data; +- int ret = 0, len; ++ int ret = 0, copied; + DECLARE_WAITQUEUE(wait, current); + + mutex_lock(&list->read_mutex); +- while (ret == 0) { +- if (list->head == list->tail) { +- add_wait_queue(&list->hdev->debug_wait, &wait); +- set_current_state(TASK_INTERRUPTIBLE); +- +- while (list->head == list->tail) { +- if (file->f_flags & O_NONBLOCK) { +- ret = -EAGAIN; +- break; +- } +- if (signal_pending(current)) { +- ret = -ERESTARTSYS; +- break; +- } +- +- if (!list->hdev || !list->hdev->debug) { +- ret = -EIO; +- break; +- } +- +- /* allow O_NONBLOCK from other threads */ +- mutex_unlock(&list->read_mutex); +- schedule(); +- mutex_lock(&list->read_mutex); +- set_current_state(TASK_INTERRUPTIBLE); ++ if (kfifo_is_empty(&list->hid_debug_fifo)) { ++ add_wait_queue(&list->hdev->debug_wait, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ while (kfifo_is_empty(&list->hid_debug_fifo)) { ++ if (file->f_flags & O_NONBLOCK) { ++ ret = -EAGAIN; ++ break; + } + +- set_current_state(TASK_RUNNING); +- remove_wait_queue(&list->hdev->debug_wait, &wait); +- } +- +- if (ret) +- goto out; +- +- /* pass the ringbuffer contents to userspace */ +-copy_rest: +- if (list->tail == list->head) +- goto out; +- if (list->tail > list->head) { +- len = list->tail - list->head; +- if (len > count) +- len = count; +- +- if (copy_to_user(buffer + ret, &list->hid_debug_buf[list->head], len)) { +- ret = -EFAULT; +- goto out; ++ if (signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ break; + } +- ret += len; +- list->head += len; +- } else { +- len = HID_DEBUG_BUFSIZE - list->head; +- if (len > count) +- len = count; + +- if (copy_to_user(buffer, &list->hid_debug_buf[list->head], len)) { +- ret = -EFAULT; ++ /* if list->hdev is NULL we cannot remove_wait_queue(). ++ * if list->hdev->debug is 0 then hid_debug_unregister() ++ * was already called and list->hdev is being destroyed. ++ * if we add remove_wait_queue() here we can hit a race. ++ */ ++ if (!list->hdev || !list->hdev->debug) { ++ ret = -EIO; ++ set_current_state(TASK_RUNNING); + goto out; + } +- list->head = 0; +- ret += len; +- count -= len; +- if (count > 0) +- goto copy_rest; ++ ++ /* allow O_NONBLOCK from other threads */ ++ mutex_unlock(&list->read_mutex); ++ schedule(); ++ mutex_lock(&list->read_mutex); ++ set_current_state(TASK_INTERRUPTIBLE); + } + ++ __set_current_state(TASK_RUNNING); ++ remove_wait_queue(&list->hdev->debug_wait, &wait); ++ ++ if (ret) ++ goto out; + } ++ ++ /* pass the fifo content to userspace, locking is not needed with only ++ * one concurrent reader and one concurrent writer ++ */ ++ ret = kfifo_to_user(&list->hid_debug_fifo, buffer, count, &copied); ++ if (ret) ++ goto out; ++ ret = copied; + out: + mutex_unlock(&list->read_mutex); + return ret; +@@ -1186,7 +1162,7 @@ static unsigned int hid_debug_events_pol + struct hid_debug_list *list = file->private_data; + + poll_wait(file, &list->hdev->debug_wait, wait); +- if (list->head != list->tail) ++ if (!kfifo_is_empty(&list->hid_debug_fifo)) + return POLLIN | POLLRDNORM; + if (!list->hdev->debug) + return POLLERR | POLLHUP; +@@ -1201,7 +1177,7 @@ static int hid_debug_events_release(stru + spin_lock_irqsave(&list->hdev->debug_list_lock, flags); + list_del(&list->node); + spin_unlock_irqrestore(&list->hdev->debug_list_lock, flags); +- kfree(list->hid_debug_buf); ++ kfifo_free(&list->hid_debug_fifo); + kfree(list); + + return 0; +@@ -1252,4 +1228,3 @@ void hid_debug_exit(void) + { + debugfs_remove_recursive(hid_debug_root); + } +- +--- a/include/linux/hid-debug.h ++++ b/include/linux/hid-debug.h +@@ -24,7 +24,10 @@ + + #ifdef CONFIG_DEBUG_FS + ++#include ++ + #define HID_DEBUG_BUFSIZE 512 ++#define HID_DEBUG_FIFOSIZE 512 + + void hid_dump_input(struct hid_device *, struct hid_usage *, __s32); + void hid_dump_report(struct hid_device *, int , u8 *, int); +@@ -37,11 +40,8 @@ void hid_debug_init(void); + void hid_debug_exit(void); + void hid_debug_event(struct hid_device *, char *); + +- + struct hid_debug_list { +- char *hid_debug_buf; +- int head; +- int tail; ++ DECLARE_KFIFO_PTR(hid_debug_fifo, char); + struct fasync_struct *fasync; + struct hid_device *hdev; + struct list_head node; +@@ -64,4 +64,3 @@ struct hid_debug_list { + #endif + + #endif +- diff --git a/queue-3.18/series b/queue-3.18/series index 3eb91e2d6b6..4382c7cbc0f 100644 --- a/queue-3.18/series +++ b/queue-3.18/series @@ -82,3 +82,4 @@ arm-iop32x-n2100-fix-pci-irq-mapping.patch drm-modes-prevent-division-by-zero-htotal.patch drm-vmwgfx-fix-setting-of-dma-masks.patch drm-vmwgfx-return-error-code-from-vmw_execbuf_copy_fence_user.patch +hid-debug-fix-the-ring-buffer-implementation.patch