From 9d12f6ccc62f5cd44b466d546fcab1ed8e97ee6f Mon Sep 17 00:00:00 2001 From: Alan Carvalho de Assis Date: Thu, 17 Sep 2026 10:15:46 -0300 Subject: [PATCH] wireless/bluetooth: Validate the L2CAP header on the first ACL fragment. bt_conn_receive() read the 4-octet L2CAP header out of the first fragment of a PDU without checking that 4 octets had been received, and then computed the outstanding length by subtracting the fragment length from the declared PDU length. Two problems follow. A fragment shorter than the header was parsed from whatever happened to follow it in the buffer. And a fragment carrying more data than the PDU it declares made the subtraction wrap, because conn->rx_len is 16 bits: the connection was then left expecting up to 65535 further octets, holding the partial PDU and accumulating later fragments against an expectation that could never be satisfied. Check that the fragment is long enough to hold a header before reading it, and that it does not exceed the PDU it declares before computing what remains. Drop the fragment and reset the reassembly state otherwise. Ref: Core v6.0, Vol 3, Part A, 3.1 (B-frame format) Ref: Core v6.0, Vol 4, Part E, 5.4.2 (HCI ACL Data packets) Testing: builds for sim:bluetooth with Make; every commit in this series verified to build individually. Not yet exercised at runtime - the scriptable controller adds the truncated and oversized fragment cases separately. Signed-off-by: Alan C. Assis Assisted-by: Claude Code Opus 5 --- wireless/bluetooth/bt_conn.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/wireless/bluetooth/bt_conn.c b/wireless/bluetooth/bt_conn.c index 752054dfc17..17afac483df 100644 --- a/wireless/bluetooth/bt_conn.c +++ b/wireless/bluetooth/bt_conn.c @@ -294,7 +294,18 @@ void bt_conn_receive(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf, { case BT_HCI_ACL_NEW: - /* First packet */ + /* First packet. The L2CAP header is read from the fragment, so + * the fragment has to be long enough to hold one. + */ + + if (buf->len < sizeof(*hdr)) + { + wlerr("ERROR: First L2CAP frame too short for a header (%u)\n", + buf->len); + bt_conn_reset_rx_state(conn); + bt_buf_release(buf); + return; + } hdr = (FAR void *)buf->data; len = BT_LE162HOST(hdr->len); @@ -307,6 +318,21 @@ void bt_conn_receive(FAR struct bt_conn_s *conn, FAR struct bt_buf_s *buf, bt_conn_reset_rx_state(conn); } + /* The fragment must not carry more than the PDU it declares. If + * it does, the outstanding length below underflows and the + * connection is parked waiting for a remainder that cannot come, + * holding the partial PDU until some later error clears it. + */ + + if (buf->len > sizeof(*hdr) + len) + { + wlerr("ERROR: First L2CAP frame exceeds its PDU (%u > %zu)\n", + buf->len, sizeof(*hdr) + len); + bt_conn_reset_rx_state(conn); + bt_buf_release(buf); + return; + } + conn->rx_len = (sizeof(*hdr) + len) - buf->len; wlinfo("rx_len %u\n", conn->rx_len); if (conn->rx_len)