From ed1f4de3a3740bdf15117de9b1b0b8c6cb123e33 Mon Sep 17 00:00:00 2001 From: luoxu Date: Thu, 30 Jul 2026 19:50:02 +0800 Subject: [PATCH] fix(ble_mesh): align GATTS read/write response handling with ATT bt_mesh_bta_gatts_cb did not always answer ATT Read/Write Requests: - READ: on a callback error it only logged a warning and sent nothing; a 0-byte read (Read Blob at an offset equal to the value length) also sent nothing, although it is a successful empty read. - WRITE: on a callback error it sent nothing, and a partial/zero write was treated as success. - Both: when the handle was not found or the attribute had no read/write callback, the request was silently dropped. An ATT Request must always be answered: - READ: len >= 0 is success -> Read Response (a 0-byte read yields an empty value); len < 0 -> ATT Error Response carrying the callback's error code (-len, since BLE_MESH_GATT_ERR(x) == -x). The copy length is clamped to the source buffer size as a defensive bound. If the handle is unknown or the attribute has no read callback, respond with INVALID_HANDLE / READ_NOT_PERMITTED. - WRITE: when need_rsp is set, always reply. len == write length -> Write Response; otherwise (negative ATT error, partial write, or 0) -> ATT Error Response (the negative code, or UNLIKELY for partial/0). If the handle is unknown or the attribute has no write callback, respond with INVALID_HANDLE / WRITE_NOT_PERMITTED. Write Without Response still sends no response. A non-success status passed to BTA_GATTS_SendRsp is turned into an ATT Error Response by the GATT layer (gatt_sr_process_app_rsp -> gatt_send_error_rsp). --- .../core/bluedroid_host/adapter.c | 66 ++++++++++++++++--- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/components/bt/esp_ble_mesh/core/bluedroid_host/adapter.c b/components/bt/esp_ble_mesh/core/bluedroid_host/adapter.c index f0ef239d804..4586dec4f84 100644 --- a/components/bt/esp_ble_mesh/core/bluedroid_host/adapter.c +++ b/components/bt/esp_ble_mesh/core/bluedroid_host/adapter.c @@ -1172,17 +1172,43 @@ static void bt_mesh_bta_gatts_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) BT_DBG("gatts read, handle %d", p_data->req_data.p_data->read_req.handle); if (attr != NULL && attr->read != NULL && index < ARRAY_SIZE(bt_mesh_gatts_conn)) { - if ((len = attr->read(&bt_mesh_gatts_conn[index], attr, buf, 100, - p_data->req_data.p_data->read_req.offset)) > 0) { + len = attr->read(&bt_mesh_gatts_conn[index], attr, buf, 100, + p_data->req_data.p_data->read_req.offset); + if (len >= 0) { rsp.attr_value.handle = p_data->req_data.p_data->read_req.handle; + if (len > sizeof(buf)) { + /* A read callback must not return more than the buf_len it was + * given; clamp to the source buffer size (also below the + * rsp.attr_value.value capacity) to avoid an out-of-bounds copy. */ + BT_WARN("Mesh gatts read len %d exceeds buffer", (int)len); + len = sizeof(buf); + } rsp.attr_value.len = len; memcpy(&rsp.attr_value.value[0], buf, len); BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, p_data->req_data.status, &rsp); BT_DBG("Send gatts read rsp, handle %d", attr->handle); } else { - BT_WARN("Mesh gatts read failed"); + /* Attribute read callback returned an ATT error code (negative). + * Reply with an ATT Error Response so the client does not time out. */ + BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, + (tBTA_GATT_STATUS)(-len), NULL); + BT_WARN("Mesh gatts read failed, err %d", (int)len); } + } else { + /* No matching attribute, or read not supported: respond with an ATT + * Error Response so the client does not time out. */ + tBTA_GATT_STATUS err; + if (attr == NULL) { + err = BLE_MESH_ATT_ERR_INVALID_HANDLE; + } else if (attr->read == NULL) { + err = BLE_MESH_ATT_ERR_READ_NOT_PERMITTED; + } else { + err = BLE_MESH_ATT_ERR_UNLIKELY; /* index out of range */ + } + BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, err, NULL); + BT_WARN("Mesh gatts read rejected, handle %d, err 0x%02x", + p_data->req_data.p_data->read_req.handle, (unsigned)err); } break; } @@ -1196,16 +1222,40 @@ static void bt_mesh_bta_gatts_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) bt_hex(p_data->req_data.p_data->write_req.value, p_data->req_data.p_data->write_req.len)); if (attr != NULL && attr->write != NULL && index < ARRAY_SIZE(bt_mesh_gatts_conn)) { - if ((len = attr->write(&bt_mesh_gatts_conn[index], attr, - p_data->req_data.p_data->write_req.value, - p_data->req_data.p_data->write_req.len, - p_data->req_data.p_data->write_req.offset, 0)) > 0) { - if (p_data->req_data.p_data->write_req.need_rsp) { + len = attr->write(&bt_mesh_gatts_conn[index], attr, + p_data->req_data.p_data->write_req.value, + p_data->req_data.p_data->write_req.len, + p_data->req_data.p_data->write_req.offset, 0); + if (p_data->req_data.p_data->write_req.need_rsp) { + if (len == p_data->req_data.p_data->write_req.len) { BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, p_data->req_data.status, NULL); BT_DBG("Send gatts write rsp, handle %d", attr->handle); + } else { + /* Write callback returned an ATT error code (negative), a partial + * write, or 0. Match upstream Zephyr, which requires write == len + * and otherwise replies with an ATT Error Response. */ + tBTA_GATT_STATUS err = (len < 0) ? (tBTA_GATT_STATUS)(-len) + : BLE_MESH_ATT_ERR_UNLIKELY; + BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, + err, NULL); + BT_WARN("Mesh gatts write failed, len %d", (int)len); } } + } else if (p_data->req_data.p_data->write_req.need_rsp) { + /* No matching attribute, or write not supported: respond with an ATT + * Error Response for Write Requests (Write Commands get no response). */ + tBTA_GATT_STATUS err; + if (attr == NULL) { + err = BLE_MESH_ATT_ERR_INVALID_HANDLE; + } else if (attr->write == NULL) { + err = BLE_MESH_ATT_ERR_WRITE_NOT_PERMITTED; + } else { + err = BLE_MESH_ATT_ERR_UNLIKELY; /* index out of range */ + } + BTA_GATTS_SendRsp(p_data->req_data.conn_id, p_data->req_data.trans_id, err, NULL); + BT_WARN("Mesh gatts write rejected, handle %d, err 0x%02x", + p_data->req_data.p_data->write_req.handle, (unsigned)err); } break; }