Disclaimer: Our finding of this issue has been reproduced in our fuzzer on a target built from commit e1dddf7befa7309bd2afc567b2e00d2e7362f7c4
. The original target was CVE-2021-3329 (which can be built via this script). Based on reviewing the source code, we concluded that the issue still applies as of the latest commit at time of writing (commit f19e6d481164420ed758f5c07d879aa75da93eed
).
Processing of priority packets in the bluetooth HCI drivers may lead to a blocking buffer allocation (with a timeout K_FOREVER
). In an ISR, this may lead to an unexpected failed allocation followed by a nullptr
deref.
The bluetooth h4 ISR handles sending and receiving packets from the bluetooth controller over the HCI. Incoming packets are received read from the controller and written to a FIFO list for further processing in the rx-thread. For high priority packets the processing can take place in the ISR. Incoming packets can may also trigger new outgoing packets, which are allocated from a pool. When the allocation pool is exhausted the current thread waits until a new buffer is available. However this does not work within the ISR context, as the PendSV
interrupt is not immediately raised and therefore an nullptr
is returned instead. This is in violation of the allocation API as the timeout K_FOREVER
garanties a valid buffer is returned and the code therefore omits nullptr
checks leading to a crash.
Processing of the incoming packets happens in the following call chain:
bt_uart_isr
=> process_rx
=> read_payload
=> bt_recv_prio
=> hci_event_prio
=> handle_event
The packets can contain a command done (hci_cmd_status
=> hci_cmd_done
), which can trigger a bt_hci_host_num_completed_packets
(report_completed_packet
in old version) due to the defined unref-handler.
This will result in the creation of a new command BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS
with bt_hci_cmd_create
. This allocates a new buffer from hci_cmd_pool
with timeout K_FOREVER
using net_buf_alloc
. When the pool is empty the previously mentioned behaviour of arch_swap
unable to raise a PendSV interrupt happens and the issue unfolds. In contrast allocations made "directly" in the ISR use K_NO_WAIT
and perform NULL
checks, e.g. in read_payload
.
We did observe crashes in net_buf_simple_reserve
during buf->data
update during fuzzing:
Frame net_buf_simple_reserve [ pc: 0000a582, ra: 000022d2 (bt_buf_set_type) ]
└─ ROM Access (Write) [ pc: 0000a586 (net_buf_simple_reserve), size: 4, address: 00000008, value: 00001985 (z_arm_hard_fault)]
A malicious / malfunctioning Bluetooth HCI Controller layer may be able to crash the host layer by sending a set of packets which trigger allocations in the host layer and then sending an affected priority event, resulting in a Denial of Service (DoS) condition.
We see two alternative approaches to fixing this issue
- rewrite all buffer allocations which are reachable via priority event handling from an ISR to use timeout 0 and handle exhausted buffer pools.
- remove the high priority handling of events in the ISR to adhere to implicit assumptions in the event handling code that buffer allocations with timeout
K_FOREVER
cannot fail
Note: We targeted a build against the h4 driver during fuzz testing. However, the other HCI implementations (h5, ...) seem to also be affected. In case hci driver-specific code changes are used to fix the issue, then the different driver implementations likely require changes as well.