CVE-2024-49748 AI-generated analysis
platform/packages/modules/Bluetooth, GATT server (system/stack/gatt/gatt_sr.cc)
Adds an early return in four GATT server request handlers in system/stack/gatt/gatt_sr.cc when the per-channel transmit payload size is 0 (channel already closed). As a result, a response buffer with effectively zero payload space is no longer allocated and written into.
Root cause
In gatt_sr.cc, the handlers gatts_process_primary_service_req, gatts_process_find_info, gatts_process_read_by_type_req and gatts_process_read_req get payload_size from gatt_tcb_get_payload_size_tx(tcb, cid). They immediately use it to size a response buffer (sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET, allocated with osi_calloc). Per the commit message, if the channel is already closed when the function is called, the length is zero. The buffer is then only sizeof(BT_HDR) + L2CAP_MIN_OFFSET bytes, and the response-building code that follows writes past the end of the allocation. For example, gatt_build_primary_service_rsp writes the opcode and length bytes via *p++ at (p_msg + 1) + L2CAP_MIN_OFFSET before it compares p_msg->len + p_msg->offset against payload_size.
How the fix works
- gatt_sr.cc:gatts_process_primary_service_req: after payload_size = gatt_tcb_get_payload_size_tx(tcb, cid), return if payload_size == 0, before msg_len is computed and osi_calloc is called.
- gatt_sr.cc:gatts_process_find_info: the same check is inserted after the payload_size fetch and before buf_len is computed.
- gatt_sr.cc:gatts_process_read_by_type_req: the same check runs before msg_len is computed, osi_calloc is called and the pointer p into the buffer is set up.
- gatt_sr.cc:gatts_process_read_req: the same check runs before buf_len is computed.
- Single commit, 21 added lines, no other logic changed. No error response is sent on the new early return.
Completeness
- Covers the four request handlers in gatt_sr.cc that size a response buffer from the payload size. The commit message says the unchecked use happens at 'various points' in gatt_sr.cc and says 'Fix all of these'.
- gatt_build_primary_service_rsp also calls gatt_tcb_get_payload_size_tx and writes into the caller-supplied buffer before its bounds comparison. It gets no check of its own. It is protected only if every caller is guarded, and the material does not show its callers.
- The fix checks only the zero case. It does not change how gatt_tcb_get_payload_size_tx derives the size for a closed channel (that body is not in the material).
Behavior changes
- When the transmit payload size for the cid is 0, primary service discovery, Find Information, Read By Type and Read requests are silently dropped: neither a response nor an ATT error response is sent.
Per major
- Android 14: Same fix: commit c7468e64bb5e in android-security-14.0.0_r15 (cherry-pick of 7de5617f7d52). All 8 patched-region lines are present at android-security-14.0.0_r29. Uses gatt_tcb_get_payload_size_tx.
- Android 15: Adapted fix: commit 339ea2fc1905 in android-security-15.0.0_r3. Same four checks, but the callee is named gatt_tcb_get_payload_size (no _tx suffix) and the line offsets differ. All 8 lines are present at android-security-15.0.0_r17.
- Android 16: Not affected per the bulletin. Only 5/8 patched-region lines are present at android-security-16.0.0_r8. The reason is not shown in the material.
- Android 17: Not affected per the bulletin. 5/8 patched-region lines are present at android-security-17.0.0_r1. The reason is not shown in the material.
Unknowns
- How gatt_tcb_get_payload_size_tx produces 0 for a closed channel (its body is not in the material).
- The callers of gatt_build_primary_service_rsp (the index search returned no caller chunks).
- The exact timing or sequence the researcher POC used to have the channel closed during request processing.
- Whether code outside gatt_sr.cc uses the payload size in a similar unchecked way. The commit only touches gatt_sr.cc.
- Why only 5/8 patched-region lines are present on Android 16/17, and why the bulletin does not list those versions.