Double free in `ble_hci_emspi_rx_acl()` on host ACL delivery failure
- Dominant language
- C
- Stars
- 893
- Forks
- 512
- Avg merge
- 13d 31m
- Merged PRs (30d)
- 7
Description
I found a possible double free in the EMSPI HCI transport receive path when an incoming ACL packet is passed to the host and host-side queuing fails.
File: `nimble/transport/emspi/src/ble_hci_emspi.c`
Function: `ble_hci_emspi_rx_acl`
Relevant code:
```c
om = ble_transport_alloc_acl_from_ll();
assert(om != NULL);
rc = ble_hci_emspi_rx(om->om_data, BLE_HCI_DATA_HDR_SZ);
if (rc != 0) {
goto err;
}
...
rc = ble_transport_to_hs_acl(om);
if (rc != 0) {
goto err;
}
return 0;
err:
os_mbuf_free_chain(om);
return rc;
```
The error label is correct for failures that occur before ownership is passed
to the host. However, `ble_transport_to_hs_acl()` eventually calls the host
implementation:
File: `nimble/host/src/ble_hs.c`
```c
ble_transport_to_hs_acl_impl(struct os_mbuf *om)
{
return ble_hs_rx_data(om, NULL);
}
```
`ble_hs_rx_data()` documents and implements that it consumes the mbuf regardless
of the outcome:
```c
/* Called when a data packet is received from the controller. This function
* consumes the supplied mbuf, regardless of the outcome.
*/
static int
ble_hs_rx_data(struct os_mbuf *om, void *arg)
{
...
rc = ble_mqueue_put(&ble_hs_rx_q, ble_hs_evq, om);
if (rc != 0) {
os_mbuf_free_chain(om);
return BLE_HS_EOS;
}
return 0;
}
```
So when `ble_mqueue_put()` fails, `ble_hs_rx_data()` already frees `om` and
returns an error. `ble_hci_emspi_rx_acl()` then sees the non-zero return value,
jumps to `err`, and frees the same mbuf again.
This can happen under low-memory or queue-allocation failure conditions in the
host receive path.
Suggested fix: after calling `ble_transport_to_hs_acl(om)`, do not free `om` on
failure because ownership has already been transferred. One option is to return
the error directly:
```c
rc = ble_transport_to_hs_acl(om);
if (rc != 0) {
return rc;
}
```
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in nimble/transport/emspi/src/ble_hci_emspi.c at ble_hci_emspi_rx_acl(), then read ble_transport_to_hs_acl_impl() in nimble/host/src/ble_hs.c and the ownership comment in ble_hs_rx_data(). Trace the ble_mqueue_put() failure path and verify that a host delivery failure consumes the mbuf only once, while earlier receive failures retain their existing cleanup.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- embedded-iot
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100