CVE-2024-56687
Publication date:
28/12/2024
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&#39;s callback could be invoked from<br />
usb_ep_queue() (call trace below, supplemented with missing calls):<br />
<br />
req->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&#39;s ->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->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---
Severity CVSS v4.0: Pending analysis
Last modification:
28/12/2024