| From bippy-5f407fcff5a0 Mon Sep 17 00:00:00 2001 |
| From: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| To: <linux-cve-announce@vger.kernel.org> |
| Reply-to: <cve@kernel.org>, <linux-kernel@vger.kernel.org> |
| Subject: CVE-2023-52478: HID: logitech-hidpp: Fix kernel crash on receiver USB disconnect |
| |
| Description |
| =========== |
| |
| In the Linux kernel, the following vulnerability has been resolved: |
| |
| HID: logitech-hidpp: Fix kernel crash on receiver USB disconnect |
| |
| hidpp_connect_event() has *four* time-of-check vs time-of-use (TOCTOU) |
| races when it races with itself. |
| |
| hidpp_connect_event() primarily runs from a workqueue but it also runs |
| on probe() and if a "device-connected" packet is received by the hw |
| when the thread running hidpp_connect_event() from probe() is waiting on |
| the hw, then a second thread running hidpp_connect_event() will be |
| started from the workqueue. |
| |
| This opens the following races (note the below code is simplified): |
| |
| 1. Retrieving + printing the protocol (harmless race): |
| |
| if (!hidpp->protocol_major) { |
| hidpp_root_get_protocol_version() |
| hidpp->protocol_major = response.rap.params[0]; |
| } |
| |
| We can actually see this race hit in the dmesg in the abrt output |
| attached to rhbz#2227968: |
| |
| [ 3064.624215] logitech-hidpp-device 0003:046D:4071.0049: HID++ 4.5 device connected. |
| [ 3064.658184] logitech-hidpp-device 0003:046D:4071.0049: HID++ 4.5 device connected. |
| |
| Testing with extra logging added has shown that after this the 2 threads |
| take turn grabbing the hw access mutex (send_mutex) so they ping-pong |
| through all the other TOCTOU cases managing to hit all of them: |
| |
| 2. Updating the name to the HIDPP name (harmless race): |
| |
| if (hidpp->name == hdev->name) { |
| ... |
| hidpp->name = new_name; |
| } |
| |
| 3. Initializing the power_supply class for the battery (problematic!): |
| |
| hidpp_initialize_battery() |
| { |
| if (hidpp->battery.ps) |
| return 0; |
| |
| probe_battery(); /* Blocks, threads take turns executing this */ |
| |
| hidpp->battery.desc.properties = |
| devm_kmemdup(dev, hidpp_battery_props, cnt, GFP_KERNEL); |
| |
| hidpp->battery.ps = |
| devm_power_supply_register(&hidpp->hid_dev->dev, |
| &hidpp->battery.desc, cfg); |
| } |
| |
| 4. Creating delayed input_device (potentially problematic): |
| |
| if (hidpp->delayed_input) |
| return; |
| |
| hidpp->delayed_input = hidpp_allocate_input(hdev); |
| |
| The really big problem here is 3. Hitting the race leads to the following |
| sequence: |
| |
| hidpp->battery.desc.properties = |
| devm_kmemdup(dev, hidpp_battery_props, cnt, GFP_KERNEL); |
| |
| hidpp->battery.ps = |
| devm_power_supply_register(&hidpp->hid_dev->dev, |
| &hidpp->battery.desc, cfg); |
| |
| ... |
| |
| hidpp->battery.desc.properties = |
| devm_kmemdup(dev, hidpp_battery_props, cnt, GFP_KERNEL); |
| |
| hidpp->battery.ps = |
| devm_power_supply_register(&hidpp->hid_dev->dev, |
| &hidpp->battery.desc, cfg); |
| |
| So now we have registered 2 power supplies for the same battery, |
| which looks a bit weird from userspace's pov but this is not even |
| the really big problem. |
| |
| Notice how: |
| |
| 1. This is all devm-maganaged |
| 2. The hidpp->battery.desc struct is shared between the 2 power supplies |
| 3. hidpp->battery.desc.properties points to the result from the second |
| devm_kmemdup() |
| |
| This causes a use after free scenario on USB disconnect of the receiver: |
| 1. The last registered power supply class device gets unregistered |
| 2. The memory from the last devm_kmemdup() call gets freed, |
| hidpp->battery.desc.properties now points to freed memory |
| 3. The first registered power supply class device gets unregistered, |
| this involves sending a remove uevent to userspace which invokes |
| power_supply_uevent() to fill the uevent data |
| 4. power_supply_uevent() uses hidpp->battery.desc.properties which |
| now points to freed memory leading to backtraces like this one: |
| |
| Sep 22 20:01:35 eric kernel: BUG: unable to handle page fault for address: ffffb2140e017f08 |
| ... |
| Sep 22 20:01:35 eric kernel: Workqueue: usb_hub_wq hub_event |
| Sep 22 20:01:35 eric kernel: RIP: 0010:power_supply_uevent+0xee/0x1d0 |
| ... |
| Sep 22 20:01:35 eric kernel: ? asm_exc_page_fault+0x26/0x30 |
| Sep 22 20:01:35 eric kernel: ? power_supply_uevent+0xee/0x1d0 |
| Sep 22 20:01:35 eric kernel: ? power_supply_uevent+0x10d/0x1d0 |
| Sep 22 20:01:35 eric kernel: dev_uevent+0x10f/0x2d0 |
| Sep 22 20:01:35 eric kernel: kobject_uevent_env+0x291/0x680 |
| Sep 22 20:01:35 eric kernel: power_supply_unregister+0x8e/0xa0 |
| Sep 22 20:01:35 eric kernel: release_nodes+0x3d/0xb0 |
| Sep 22 20:01:35 eric kernel: devres_release_group+0xfc/0x130 |
| Sep 22 20:01:35 eric kernel: hid_device_remove+0x56/0xa0 |
| Sep 22 20:01:35 eric kernel: device_release_driver_internal+0x19f/0x200 |
| Sep 22 20:01:35 eric kernel: bus_remove_device+0xc6/0x130 |
| Sep 22 20:01:35 eric kernel: device_del+0x15c/0x3f0 |
| Sep 22 20:01:35 eric kernel: ? __queue_work+0x1df/0x440 |
| Sep 22 20:01:35 eric kernel: hid_destroy_device+0x4b/0x60 |
| Sep 22 20:01:35 eric kernel: logi_dj_remove+0x9a/0x100 [hid_logitech_dj 5c91534a0ead2b65e04dd799a0437e3b99b21bc4] |
| Sep 22 20:01:35 eric kernel: hid_device_remove+0x44/0xa0 |
| Sep 22 20:01:35 eric kernel: device_release_driver_internal+0x19f/0x200 |
| Sep 22 20:01:35 eric kernel: bus_remove_device+0xc6/0x130 |
| Sep 22 20:01:35 eric kernel: device_del+0x15c/0x3f0 |
| Sep 22 20:01:35 eric kernel: ? __queue_work+0x1df/0x440 |
| Sep 22 20:01:35 eric kernel: hid_destroy_device+0x4b/0x60 |
| Sep 22 20:01:35 eric kernel: usbhid_disconnect+0x47/0x60 [usbhid 727dcc1c0b94e6b4418727a468398ac3bca492f3] |
| Sep 22 20:01:35 eric kernel: usb_unbind_interface+0x90/0x270 |
| Sep 22 20:01:35 eric kernel: device_release_driver_internal+0x19f/0x200 |
| Sep 22 20:01:35 eric kernel: bus_remove_device+0xc6/0x130 |
| Sep 22 20:01:35 eric kernel: device_del+0x15c/0x3f0 |
| Sep 22 20:01:35 eric kernel: ? kobject_put+0xa0/0x1d0 |
| Sep 22 20:01:35 eric kernel: usb_disable_device+0xcd/0x1e0 |
| Sep 22 20:01:35 eric kernel: usb_disconnect+0xde/0x2c0 |
| Sep 22 20:01:35 eric kernel: usb_disconnect+0xc3/0x2c0 |
| Sep 22 20:01:35 eric kernel: hub_event+0xe80/0x1c10 |
| |
| There have been quite a few bug reports (see Link tags) about this crash. |
| |
| Fix all the TOCTOU issues, including the really bad power-supply related |
| system crash on USB disconnect, by making probe() use the workqueue for |
| running hidpp_connect_event() too, so that it can never run more then once. |
| |
| The Linux kernel CVE team has assigned CVE-2023-52478 to this issue. |
| |
| |
| Affected and fixed versions |
| =========================== |
| |
| Fixed in 4.14.328 with commit ca0c4cc1d215dc22ab0e738c9f017c650f3183f5 |
| Fixed in 4.19.297 with commit 44481b244fcaa2b895a53081d6204c574720c38c |
| Fixed in 5.4.259 with commit cd0e2bf7fb22fe9b989c59c42dca06367fd10e6b |
| Fixed in 5.10.199 with commit 093af62c023537f097d2ebdfaa0bc7c1a6e874e1 |
| Fixed in 5.15.136 with commit 28ddc1e0b898291323b62d770b1b931de131a528 |
| Fixed in 6.1.59 with commit fd72ac9556a473fc7daf54efb6ca8a97180d621d |
| Fixed in 6.5.8 with commit f7b2c7d9831af99369fe8ad9b2a68d78942f414e |
| Fixed in 6.6 with commit dac501397b9d81e4782232c39f94f4307b137452 |
| |
| Please see https://www.kernel.org for a full list of currently supported |
| kernel versions by the kernel community. |
| |
| Unaffected versions might change over time as fixes are backported to |
| older supported kernel versions. The official CVE entry at |
| https://cve.org/CVERecord/?id=CVE-2023-52478 |
| will be updated if fixes are backported, please check that for the most |
| up to date information about this issue. |
| |
| |
| Affected files |
| ============== |
| |
| The file(s) affected by this issue are: |
| drivers/hid/hid-logitech-hidpp.c |
| |
| |
| Mitigation |
| ========== |
| |
| The Linux kernel CVE team recommends that you update to the latest |
| stable kernel version for this, and many other bugfixes. Individual |
| changes are never tested alone, but rather are part of a larger kernel |
| release. Cherry-picking individual commits is not recommended or |
| supported by the Linux kernel community at all. If however, updating to |
| the latest release is impossible, the individual changes to resolve this |
| issue can be found at these commits: |
| https://git.kernel.org/stable/c/ca0c4cc1d215dc22ab0e738c9f017c650f3183f5 |
| https://git.kernel.org/stable/c/44481b244fcaa2b895a53081d6204c574720c38c |
| https://git.kernel.org/stable/c/cd0e2bf7fb22fe9b989c59c42dca06367fd10e6b |
| https://git.kernel.org/stable/c/093af62c023537f097d2ebdfaa0bc7c1a6e874e1 |
| https://git.kernel.org/stable/c/28ddc1e0b898291323b62d770b1b931de131a528 |
| https://git.kernel.org/stable/c/fd72ac9556a473fc7daf54efb6ca8a97180d621d |
| https://git.kernel.org/stable/c/f7b2c7d9831af99369fe8ad9b2a68d78942f414e |
| https://git.kernel.org/stable/c/dac501397b9d81e4782232c39f94f4307b137452 |