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]

Reply via email to