Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HID: debug: fix the ring buffer implementation

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()

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 <vdronov@redhat.com>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

authored by

Vladis Dronov and committed by
Benjamin Tissoires
13054abb 1950f462

+52 -79
+48 -74
drivers/hid/hid-debug.c
··· 30 30 31 31 #include <linux/debugfs.h> 32 32 #include <linux/seq_file.h> 33 + #include <linux/kfifo.h> 33 34 #include <linux/sched/signal.h> 34 35 #include <linux/export.h> 35 36 #include <linux/slab.h> ··· 662 661 /* enqueue string to 'events' ring buffer */ 663 662 void hid_debug_event(struct hid_device *hdev, char *buf) 664 663 { 665 - unsigned i; 666 664 struct hid_debug_list *list; 667 665 unsigned long flags; 668 666 669 667 spin_lock_irqsave(&hdev->debug_list_lock, flags); 670 - list_for_each_entry(list, &hdev->debug_list, node) { 671 - for (i = 0; buf[i]; i++) 672 - list->hid_debug_buf[(list->tail + i) % HID_DEBUG_BUFSIZE] = 673 - buf[i]; 674 - list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE; 675 - } 668 + list_for_each_entry(list, &hdev->debug_list, node) 669 + kfifo_in(&list->hid_debug_fifo, buf, strlen(buf)); 676 670 spin_unlock_irqrestore(&hdev->debug_list_lock, flags); 677 671 678 672 wake_up_interruptible(&hdev->debug_wait); ··· 718 722 hid_debug_event(hdev, buf); 719 723 720 724 kfree(buf); 721 - wake_up_interruptible(&hdev->debug_wait); 722 - 725 + wake_up_interruptible(&hdev->debug_wait); 723 726 } 724 727 EXPORT_SYMBOL_GPL(hid_dump_input); 725 728 ··· 1078 1083 goto out; 1079 1084 } 1080 1085 1081 - if (!(list->hid_debug_buf = kzalloc(HID_DEBUG_BUFSIZE, GFP_KERNEL))) { 1082 - err = -ENOMEM; 1086 + err = kfifo_alloc(&list->hid_debug_fifo, HID_DEBUG_FIFOSIZE, GFP_KERNEL); 1087 + if (err) { 1083 1088 kfree(list); 1084 1089 goto out; 1085 1090 } ··· 1099 1104 size_t count, loff_t *ppos) 1100 1105 { 1101 1106 struct hid_debug_list *list = file->private_data; 1102 - int ret = 0, len; 1107 + int ret = 0, copied; 1103 1108 DECLARE_WAITQUEUE(wait, current); 1104 1109 1105 1110 mutex_lock(&list->read_mutex); 1106 - while (ret == 0) { 1107 - if (list->head == list->tail) { 1108 - add_wait_queue(&list->hdev->debug_wait, &wait); 1109 - set_current_state(TASK_INTERRUPTIBLE); 1111 + if (kfifo_is_empty(&list->hid_debug_fifo)) { 1112 + add_wait_queue(&list->hdev->debug_wait, &wait); 1113 + set_current_state(TASK_INTERRUPTIBLE); 1110 1114 1111 - while (list->head == list->tail) { 1112 - if (file->f_flags & O_NONBLOCK) { 1113 - ret = -EAGAIN; 1114 - break; 1115 - } 1116 - if (signal_pending(current)) { 1117 - ret = -ERESTARTSYS; 1118 - break; 1119 - } 1120 - 1121 - if (!list->hdev || !list->hdev->debug) { 1122 - ret = -EIO; 1123 - set_current_state(TASK_RUNNING); 1124 - goto out; 1125 - } 1126 - 1127 - /* allow O_NONBLOCK from other threads */ 1128 - mutex_unlock(&list->read_mutex); 1129 - schedule(); 1130 - mutex_lock(&list->read_mutex); 1131 - set_current_state(TASK_INTERRUPTIBLE); 1115 + while (kfifo_is_empty(&list->hid_debug_fifo)) { 1116 + if (file->f_flags & O_NONBLOCK) { 1117 + ret = -EAGAIN; 1118 + break; 1132 1119 } 1133 1120 1134 - set_current_state(TASK_RUNNING); 1135 - remove_wait_queue(&list->hdev->debug_wait, &wait); 1121 + if (signal_pending(current)) { 1122 + ret = -ERESTARTSYS; 1123 + break; 1124 + } 1125 + 1126 + /* if list->hdev is NULL we cannot remove_wait_queue(). 1127 + * if list->hdev->debug is 0 then hid_debug_unregister() 1128 + * was already called and list->hdev is being destroyed. 1129 + * if we add remove_wait_queue() here we can hit a race. 1130 + */ 1131 + if (!list->hdev || !list->hdev->debug) { 1132 + ret = -EIO; 1133 + set_current_state(TASK_RUNNING); 1134 + goto out; 1135 + } 1136 + 1137 + /* allow O_NONBLOCK from other threads */ 1138 + mutex_unlock(&list->read_mutex); 1139 + schedule(); 1140 + mutex_lock(&list->read_mutex); 1141 + set_current_state(TASK_INTERRUPTIBLE); 1136 1142 } 1143 + 1144 + __set_current_state(TASK_RUNNING); 1145 + remove_wait_queue(&list->hdev->debug_wait, &wait); 1137 1146 1138 1147 if (ret) 1139 1148 goto out; 1140 - 1141 - /* pass the ringbuffer contents to userspace */ 1142 - copy_rest: 1143 - if (list->tail == list->head) 1144 - goto out; 1145 - if (list->tail > list->head) { 1146 - len = list->tail - list->head; 1147 - if (len > count) 1148 - len = count; 1149 - 1150 - if (copy_to_user(buffer + ret, &list->hid_debug_buf[list->head], len)) { 1151 - ret = -EFAULT; 1152 - goto out; 1153 - } 1154 - ret += len; 1155 - list->head += len; 1156 - } else { 1157 - len = HID_DEBUG_BUFSIZE - list->head; 1158 - if (len > count) 1159 - len = count; 1160 - 1161 - if (copy_to_user(buffer, &list->hid_debug_buf[list->head], len)) { 1162 - ret = -EFAULT; 1163 - goto out; 1164 - } 1165 - list->head = 0; 1166 - ret += len; 1167 - count -= len; 1168 - if (count > 0) 1169 - goto copy_rest; 1170 - } 1171 - 1172 1149 } 1150 + 1151 + /* pass the fifo content to userspace, locking is not needed with only 1152 + * one concurrent reader and one concurrent writer 1153 + */ 1154 + ret = kfifo_to_user(&list->hid_debug_fifo, buffer, count, &copied); 1155 + if (ret) 1156 + goto out; 1157 + ret = copied; 1173 1158 out: 1174 1159 mutex_unlock(&list->read_mutex); 1175 1160 return ret; ··· 1160 1185 struct hid_debug_list *list = file->private_data; 1161 1186 1162 1187 poll_wait(file, &list->hdev->debug_wait, wait); 1163 - if (list->head != list->tail) 1188 + if (!kfifo_is_empty(&list->hid_debug_fifo)) 1164 1189 return EPOLLIN | EPOLLRDNORM; 1165 1190 if (!list->hdev->debug) 1166 1191 return EPOLLERR | EPOLLHUP; ··· 1175 1200 spin_lock_irqsave(&list->hdev->debug_list_lock, flags); 1176 1201 list_del(&list->node); 1177 1202 spin_unlock_irqrestore(&list->hdev->debug_list_lock, flags); 1178 - kfree(list->hid_debug_buf); 1203 + kfifo_free(&list->hid_debug_fifo); 1179 1204 kfree(list); 1180 1205 1181 1206 return 0; ··· 1221 1246 { 1222 1247 debugfs_remove_recursive(hid_debug_root); 1223 1248 } 1224 -
+4 -5
include/linux/hid-debug.h
··· 24 24 25 25 #ifdef CONFIG_DEBUG_FS 26 26 27 + #include <linux/kfifo.h> 28 + 27 29 #define HID_DEBUG_BUFSIZE 512 30 + #define HID_DEBUG_FIFOSIZE 512 28 31 29 32 void hid_dump_input(struct hid_device *, struct hid_usage *, __s32); 30 33 void hid_dump_report(struct hid_device *, int , u8 *, int); ··· 40 37 void hid_debug_exit(void); 41 38 void hid_debug_event(struct hid_device *, char *); 42 39 43 - 44 40 struct hid_debug_list { 45 - char *hid_debug_buf; 46 - int head; 47 - int tail; 41 + DECLARE_KFIFO_PTR(hid_debug_fifo, char); 48 42 struct fasync_struct *fasync; 49 43 struct hid_device *hdev; 50 44 struct list_head node; ··· 64 64 #endif 65 65 66 66 #endif 67 -