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

HID: fix HID device resource race between HID core and debugging support

hid_debug_events_release releases resources bound to the HID device instance.
hid_device_release releases the underlying HID device instance potentially
before hid_debug_events_release has completed releasing debug resources bound
to the same HID device instance.

Reference count to prevent the HID device instance from being torn down
preemptively when HID debugging support is used. When count reaches zero,
release core resources of HID device instance using hiddev_free.

The crash:

[ 120.728477][ T4396] kernel BUG at lib/list_debug.c:53!
[ 120.728505][ T4396] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP
[ 120.739806][ T4396] Modules linked in: bcmdhd dhd_static_buf 8822cu pcie_mhi r8168
[ 120.747386][ T4396] CPU: 1 PID: 4396 Comm: hidt_bridge Not tainted 5.10.110 #257
[ 120.754771][ T4396] Hardware name: Rockchip RK3588 EVB4 LP4 V10 Board (DT)
[ 120.761643][ T4396] pstate: 60400089 (nZCv daIf +PAN -UAO -TCO BTYPE=--)
[ 120.768338][ T4396] pc : __list_del_entry_valid+0x98/0xac
[ 120.773730][ T4396] lr : __list_del_entry_valid+0x98/0xac
[ 120.779120][ T4396] sp : ffffffc01e62bb60
[ 120.783126][ T4396] x29: ffffffc01e62bb60 x28: ffffff818ce3a200
[ 120.789126][ T4396] x27: 0000000000000009 x26: 0000000000980000
[ 120.795126][ T4396] x25: ffffffc012431000 x24: ffffff802c6d4e00
[ 120.801125][ T4396] x23: ffffff8005c66f00 x22: ffffffc01183b5b8
[ 120.807125][ T4396] x21: ffffff819df2f100 x20: 0000000000000000
[ 120.813124][ T4396] x19: ffffff802c3f0700 x18: ffffffc01d2cd058
[ 120.819124][ T4396] x17: 0000000000000000 x16: 0000000000000000
[ 120.825124][ T4396] x15: 0000000000000004 x14: 0000000000003fff
[ 120.831123][ T4396] x13: ffffffc012085588 x12: 0000000000000003
[ 120.837123][ T4396] x11: 00000000ffffbfff x10: 0000000000000003
[ 120.843123][ T4396] x9 : 455103d46b329300 x8 : 455103d46b329300
[ 120.849124][ T4396] x7 : 74707572726f6320 x6 : ffffffc0124b8cb5
[ 120.855124][ T4396] x5 : ffffffffffffffff x4 : 0000000000000000
[ 120.861123][ T4396] x3 : ffffffc011cf4f90 x2 : ffffff81fee7b948
[ 120.867122][ T4396] x1 : ffffffc011cf4f90 x0 : 0000000000000054
[ 120.873122][ T4396] Call trace:
[ 120.876259][ T4396] __list_del_entry_valid+0x98/0xac
[ 120.881304][ T4396] hid_debug_events_release+0x48/0x12c
[ 120.886617][ T4396] full_proxy_release+0x50/0xbc
[ 120.891323][ T4396] __fput+0xdc/0x238
[ 120.895075][ T4396] ____fput+0x14/0x24
[ 120.898911][ T4396] task_work_run+0x90/0x148
[ 120.903268][ T4396] do_exit+0x1bc/0x8a4
[ 120.907193][ T4396] do_group_exit+0x8c/0xa4
[ 120.911458][ T4396] get_signal+0x468/0x744
[ 120.915643][ T4396] do_signal+0x84/0x280
[ 120.919650][ T4396] do_notify_resume+0xd0/0x218
[ 120.924262][ T4396] work_pending+0xc/0x3f0

[ Rahul Rameshbabu <sergeantsagara@protonmail.com>: rework changelog ]
Fixes: cd667ce24796 ("HID: use debugfs for events/reports dumping")
Signed-off-by: Charles Yi <be286@163.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

authored by

Charles Yi and committed by
Jiri Kosina
fc43e9c8 113f7366

+16 -2
+10 -2
drivers/hid/hid-core.c
··· 702 702 * Free a device structure, all reports, and all fields. 703 703 */ 704 704 705 - static void hid_device_release(struct device *dev) 705 + void hiddev_free(struct kref *ref) 706 706 { 707 - struct hid_device *hid = to_hid_device(dev); 707 + struct hid_device *hid = container_of(ref, struct hid_device, ref); 708 708 709 709 hid_close_report(hid); 710 710 kfree(hid->dev_rdesc); 711 711 kfree(hid); 712 + } 713 + 714 + static void hid_device_release(struct device *dev) 715 + { 716 + struct hid_device *hid = to_hid_device(dev); 717 + 718 + kref_put(&hid->ref, hiddev_free); 712 719 } 713 720 714 721 /* ··· 2853 2846 spin_lock_init(&hdev->debug_list_lock); 2854 2847 sema_init(&hdev->driver_input_lock, 1); 2855 2848 mutex_init(&hdev->ll_open_lock); 2849 + kref_init(&hdev->ref); 2856 2850 2857 2851 hid_bpf_device_init(hdev); 2858 2852
+3
drivers/hid/hid-debug.c
··· 1135 1135 goto out; 1136 1136 } 1137 1137 list->hdev = (struct hid_device *) inode->i_private; 1138 + kref_get(&list->hdev->ref); 1138 1139 file->private_data = list; 1139 1140 mutex_init(&list->read_mutex); 1140 1141 ··· 1228 1227 list_del(&list->node); 1229 1228 spin_unlock_irqrestore(&list->hdev->debug_list_lock, flags); 1230 1229 kfifo_free(&list->hid_debug_fifo); 1230 + 1231 + kref_put(&list->hdev->ref, hiddev_free); 1231 1232 kfree(list); 1232 1233 1233 1234 return 0;
+3
include/linux/hid.h
··· 679 679 struct list_head debug_list; 680 680 spinlock_t debug_list_lock; 681 681 wait_queue_head_t debug_wait; 682 + struct kref ref; 682 683 683 684 unsigned int id; /* system unique id */ 684 685 ··· 687 686 struct hid_bpf bpf; /* hid-bpf data */ 688 687 #endif /* CONFIG_BPF */ 689 688 }; 689 + 690 + void hiddev_free(struct kref *ref); 690 691 691 692 #define to_hid_device(pdev) \ 692 693 container_of(pdev, struct hid_device, dev)