CVE-2026-0095 AI-generated analysis
platform/packages/modules/Bluetooth, L2CAP flow control / retransmission (system/stack/l2cap/l2c_fcr.cc)
Widens the local buf_size in l2c_fcr_clone_buf (system/stack/l2cap/l2c_fcr.cc) from uint16_t to uint32_t so the allocation size for the cloned L2CAP buffer is no longer truncated before osi_malloc, preventing a heap buffer overflow in the following memcpy.
Root cause
l2c_fcr_clone_buf(BT_HDR* p_buf, uint16_t new_offset, uint16_t no_of_bytes) computes the size of the new buffer as no_of_bytes + sizeof(BT_HDR) + new_offset + L2CAP_FCS_LEN and stored it in a uint16_t buf_size. Both no_of_bytes and new_offset are 16-bit, so their sum plus the header and FCS bytes can exceed 65535. The assignment truncated the value, osi_malloc(buf_size) then returned a buffer smaller than required, and the subsequent memcpy of no_of_bytes bytes to ((uint8_t*)(p_buf2 + 1)) + new_offset wrote past the end of the allocation.
How the fix works
- system/stack/l2cap/l2c_fcr.cc:l2c_fcr_clone_buf: the single changed line declares buf_size as uint32_t instead of uint16_t. The expression itself is unchanged. It is evaluated in size_t (because of sizeof(BT_HDR)) and is now stored without truncation, so osi_malloc receives the full required size (at most roughly 2*65535 + sizeof(BT_HDR) + L2CAP_FCS_LEN, which fits in 32 bits).
- No other logic is changed: p_buf2->offset, p_buf2->len and the memcpy are identical before and after the fix. The allocation size now matches what the memcpy and the later FCS write can use.
Completeness
- Covers: the destination-side truncation in l2c_fcr_clone_buf for every caller, since the fix is inside the function.
- Leaves out (from the code): no_of_bytes is still not validated against the source buffer (p_buf->len). The memcpy reads no_of_bytes from ((uint8_t*)(p_buf + 1)) + p_buf->offset without checking that the source holds that many bytes. The commit only addresses the undersized allocation, not source bounds.
- The return value of osi_malloc is not checked, and the fix does not change this (osi_malloc aborts on allocation failure rather than returning NULL (general platform knowledge)).
Behavior changes
- None functionally visible in the normal case. When no_of_bytes + new_offset + header + FCS exceeds 65535, the function now allocates the correct (larger) buffer instead of a truncated one.
Per major
- Android 14: same fix: commit 006620f1caaa in android-security-14.0.0_r28, identical one-line uint16_t -> uint32_t change in l2c_fcr_clone_buf. Present at android-security-14.0.0_r29 (uint32_t buf_size at line 244).
- Android 15: same fix: commit f82bbb37a852 in android-security-15.0.0_r16, identical one-line change (line offset 242).
- Android 16: same fix: commit abc8b696b615 in android-security-16.0.0_r7, identical one-line change (function signature on one line, hunk at 229).
- Android 17: same fix: commit 554513a00597 in android-17.0.0_r1 (base), identical one-line change.
Unknowns
- Which callers of l2c_fcr_clone_buf can supply large no_of_bytes/new_offset values and whether they are remotely reachable. Per index search, no callers appeared among the top results.
- Why the bulletin classifies it as EoP (the material does not describe the trigger or attack surface).
- Whether callers enforce the source-side bound (no_of_bytes vs p_buf->len).