CVE-2024-56687

Severity CVSS v4.0:
Pending analysis
Type:
Unavailable / Other
Publication date:
28/12/2024
Last modified:
31/01/2025

Description

In the Linux kernel, the following vulnerability has been resolved:<br /> <br /> usb: musb: Fix hardware lockup on first Rx endpoint request<br /> <br /> There is a possibility that a request&amp;#39;s callback could be invoked from<br /> usb_ep_queue() (call trace below, supplemented with missing calls):<br /> <br /> req-&gt;complete from usb_gadget_giveback_request<br /> (drivers/usb/gadget/udc/core.c:999)<br /> usb_gadget_giveback_request from musb_g_giveback<br /> (drivers/usb/musb/musb_gadget.c:147)<br /> musb_g_giveback from rxstate<br /> (drivers/usb/musb/musb_gadget.c:784)<br /> rxstate from musb_ep_restart<br /> (drivers/usb/musb/musb_gadget.c:1169)<br /> musb_ep_restart from musb_ep_restart_resume_work<br /> (drivers/usb/musb/musb_gadget.c:1176)<br /> musb_ep_restart_resume_work from musb_queue_resume_work<br /> (drivers/usb/musb/musb_core.c:2279)<br /> musb_queue_resume_work from musb_gadget_queue<br /> (drivers/usb/musb/musb_gadget.c:1241)<br /> musb_gadget_queue from usb_ep_queue<br /> (drivers/usb/gadget/udc/core.c:300)<br /> <br /> According to the docstring of usb_ep_queue(), this should not happen:<br /> <br /> "Note that @req&amp;#39;s -&gt;complete() callback must never be called from within<br /> usb_ep_queue() as that can create deadlock situations."<br /> <br /> In fact, a hardware lockup might occur in the following sequence:<br /> <br /> 1. The gadget is initialized using musb_gadget_enable().<br /> 2. Meanwhile, a packet arrives, and the RXPKTRDY flag is set, raising an<br /> interrupt.<br /> 3. If IRQs are enabled, the interrupt is handled, but musb_g_rx() finds an<br /> empty queue (next_request() returns NULL). The interrupt flag has<br /> already been cleared by the glue layer handler, but the RXPKTRDY flag<br /> remains set.<br /> 4. The first request is enqueued using usb_ep_queue(), leading to the call<br /> of req-&gt;complete(), as shown in the call trace above.<br /> 5. If the callback enables IRQs and another packet is waiting, step (3)<br /> repeats. The request queue is empty because usb_g_giveback() removes the<br /> request before invoking the callback.<br /> 6. The endpoint remains locked up, as the interrupt triggered by hardware<br /> setting the RXPKTRDY flag has been handled, but the flag itself remains<br /> set.<br /> <br /> For this scenario to occur, it is only necessary for IRQs to be enabled at<br /> some point during the complete callback. This happens with the USB Ethernet<br /> gadget, whose rx_complete() callback calls netif_rx(). If called in the<br /> task context, netif_rx() disables the bottom halves (BHs). When the BHs are<br /> re-enabled, IRQs are also enabled to allow soft IRQs to be processed. The<br /> gadget itself is initialized at module load (or at boot if built-in), but<br /> the first request is enqueued when the network interface is brought up,<br /> triggering rx_complete() in the task context via ioctl(). If a packet<br /> arrives while the interface is down, it can prevent the interface from<br /> receiving any further packets from the USB host.<br /> <br /> The situation is quite complicated with many parties involved. This<br /> particular issue can be resolved in several possible ways:<br /> <br /> 1. Ensure that callbacks never enable IRQs. This would be difficult to<br /> enforce, as discovering how netif_rx() interacts with interrupts was<br /> already quite challenging and u_ether is not the only function driver.<br /> Similar "bugs" could be hidden in other drivers as well.<br /> 2. Disable MUSB interrupts in musb_g_giveback() before calling the callback<br /> and re-enable them afterwars (by calling musb_{dis,en}able_interrupts(),<br /> for example). This would ensure that MUSB interrupts are not handled<br /> during the callback, even if IRQs are enabled. In fact, it would allow<br /> IRQs to be enabled when releasing the lock. However, this feels like an<br /> inelegant hack.<br /> 3. Modify the interrupt handler to clear the RXPKTRDY flag if the request<br /> queue is empty. While this approach also feels like a hack, it wastes<br /> CPU time by attempting to handle incoming packets when the software is<br /> not ready to process them.<br /> 4. Flush the Rx FIFO instead of calling rxstate() in musb_ep_restart().<br /> This ensures that the hardware can receive packets when there is at<br /> least one request in the queue. Once I<br /> ---truncated---

Vulnerable products and versions

CPE From Up to
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 5.18 (including) 6.1.120 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 6.2 (including) 6.6.64 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 6.7 (including) 6.11.11 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 6.12 (including) 6.12.2 (excluding)