Skip to content

{bp-20003} wireless/bluetooth: fix inverted MTU cap in bt_conn_send() - #20077

Merged
xiaoxiang781216 merged 1 commit into
apache:releases/13.0from
jerpelea:bp-20003
Sep 7, 2026
Merged

{bp-20003} wireless/bluetooth: fix inverted MTU cap in bt_conn_send()#20077
xiaoxiang781216 merged 1 commit into
apache:releases/13.0from
jerpelea:bp-20003

Conversation

@jerpelea

@jerpelea jerpelea commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Summary

bt_conn_send() splits an outgoing L2CAP PDU into HCI ACL fragments no larger than g_btdev.le_mtu, the controller's HCI ACL data packet length. The first fragment caps its length correctly:

len = remaining;
if (len > g_btdev.le_mtu)
{
len = g_btdev.le_mtu;
}

The continuation loop below uses '<' instead of '>', so a continuation shorter than le_mtu has its length raised to le_mtu rather than left alone. Both len and remaining are uint16_t, which turns a wrong length into an underflow:

With le_mtu 251 and a 300-byte PDU, the first fragment takes 251 bytes and leaves remaining == 49. The loop then raises len from 49 to 251, so

memcpy(bt_buf_extend(buf, len), ptr, len);

reads 202 bytes past the end of the source, and

remaining -= len;

evaluates 49 - 251 as a uint16_t, wrapping to 65334. On the next iteration len is 65334, which is not less than le_mtu, so it survives the cap. bt_buf_extend() carries only a DEBUGASSERT on tailroom, so with assertions disabled it adds 65334 to buf->len and returns, and the memcpy writes 64 KB into a pooled buffer sized for a few hundred bytes.

Only the last fragment of a multi-fragment PDU is normally shorter than le_mtu, so the first fragmented transmission triggers it.

Impact

RELEASE

Testing

CI

bt_conn_send() splits an outgoing L2CAP PDU into HCI ACL fragments no
larger than g_btdev.le_mtu, the controller's HCI ACL data packet length.
The first fragment caps its length correctly:

  len = remaining;
  if (len > g_btdev.le_mtu)
    {
      len = g_btdev.le_mtu;
    }

The continuation loop below uses '<' instead of '>', so a continuation
shorter than le_mtu has its length raised to le_mtu rather than left
alone.  Both len and remaining are uint16_t, which turns a wrong length
into an underflow:

With le_mtu 251 and a 300-byte PDU, the first fragment takes 251 bytes
and leaves remaining == 49.  The loop then raises len from 49 to 251, so

  memcpy(bt_buf_extend(buf, len), ptr, len);

reads 202 bytes past the end of the source, and

  remaining -= len;

evaluates 49 - 251 as a uint16_t, wrapping to 65334.  On the next
iteration len is 65334, which is not less than le_mtu, so it survives
the cap.  bt_buf_extend() carries only a DEBUGASSERT on tailroom, so
with assertions disabled it adds 65334 to buf->len and returns, and the
memcpy writes 64 KB into a pooled buffer sized for a few hundred bytes.

Only the last fragment of a multi-fragment PDU is normally shorter than
le_mtu, so the first fragmented transmission triggers it.

Signed-off-by: AlmAck <gluca86@gmail.com>
@github-actions github-actions Bot added Area: Bluetooth Size: XS The size of the change in this PR is very small labels Sep 7, 2026
@jerpelea

jerpelea commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

CI fix
apache/nuttx-apps#3775

@xiaoxiang781216
xiaoxiang781216 merged commit 0a4eff5 into apache:releases/13.0 Sep 7, 2026
21 of 41 checks passed
@jerpelea
jerpelea deleted the bp-20003 branch September 7, 2026 11:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: Bluetooth Size: XS The size of the change in this PR is very small

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants