kanodayo1111 opened a new issue, #19835:
URL: https://github.com/apache/nuttx/issues/19835
### Description / Steps to reproduce the issue
## Bug report
### Description
The HCI RX worker releases every dequeued buffer unconditionally after
dispatch:
```c
/* bt_hcicore.c, hci_rx_work() */
switch (buf->type)
{
case BT_ACL_IN:
hci_acl(buf);
break;
...
}
bt_buf_release(buf); /* unconditional */
```
but the same buffer is also released downstream for ACL data:
- **Complete L2CAP PDU:** `bt_conn_receive()` → `bt_l2cap_receive()` →
`bt_att_receive()` (`bt_att.c`) or `bt_smp_receive()` (`bt_smp.c`), which both
end with `bt_buf_release(buf)`. Result: double release of the same buffer
(refcount underflow; `DEBUGASSERT(buf->ref > 0)` fires in debug builds, silent
corruption / buffer-pool exhaustion in release builds).
- **Fragmented L2CAP PDU:** `bt_conn_receive()` stores the first fragment in
`conn->rx` (`bt_conn.c`: `conn->rx = buf;`) **without taking a reference**,
then the worker's unconditional release frees it. When the continuation
fragment arrives, `memcpy(bt_buf_extend(conn->rx, ...))` writes into freed
memory (use-after-free).
### Impact
- Present with any standard HCI controller; triggered by any inbound ACL
traffic.
- Debug builds assert/crash on the first complete inbound PDU; release
builds behave "mostly fine" on single-core serialized RX (the two releases
happen back-to-back) and fail randomly once the freed buffer gets reallocated
in between — exactly the "first connection works, later ones corrupt" pattern.
- Fragmented PDUs deterministically corrupt memory via the dangling
`conn->rx`.
### Regression origin
The "worker releases at end of loop" model was introduced by PR #2571
(`c6947199b21`), while downstream releases in
`bt_conn_receive`/`bt_att_receive`/`bt_smp_receive` were kept. PR #9082
(`dd5abe86914c`) removed the error-path releases in `hci_acl`/`hci_event` but
left the successful ACL path double-release in place.
### Proposed fix
Before handing the buffer to the connection layer, take an extra reference
for the downstream owner:
```c
conn = bt_conn_lookup_handle(buf->u.acl.handle);
if (!conn)
{
return;
}
bt_buf_addref(buf);
bt_conn_receive(conn, buf, flags);
bt_conn_release(conn);
```
so that the worker's loop-end release and the downstream release each own
one reference. (Alternative: remove the worker's loop-end release and require
each handler to own/release, matching Zephyr's `rx_work_handler` which does not
release after dispatch.)
### Reference
Zephyr main `rx_work_handler()` does not release the buffer after dispatch;
ownership transfers unidirectionally (error paths release, `conn->rx` takes
ownership of the first fragment, L2CAP/ATT release the completed PDU).
### On which OS does this issue occur?
[OS: Linux]
### What is the version of your OS?
Ubuntu 22.04.5 LTS
### NuttX Version
master
### Issue Architecture
[Arch: all]
### Issue Area
[Area: Other]
### Host information
_No response_
### Verification
- [x] I have verified before submitting the report.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]