From 747c9adeaa3a145fd1d4f365fa6fbd66d2673bda Mon Sep 17 00:00:00 2001 From: zhanghaipeng Date: Mon, 20 Apr 2026 17:09:44 +0800 Subject: [PATCH] fix(bt/bluedroid): validate handle in esp_ble_gattc_register_for_notify --- .../bluedroid/api/include/api/esp_gattc_api.h | 16 +++++++++++++ .../host/bluedroid/bta/gatt/bta_gattc_act.c | 14 +++++++---- .../host/bluedroid/bta/gatt/bta_gattc_api.c | 24 +++++++++++++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/components/bt/host/bluedroid/api/include/api/esp_gattc_api.h b/components/bt/host/bluedroid/api/include/api/esp_gattc_api.h index 3426e330000..70953609ee6 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_gattc_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_gattc_api.h @@ -997,6 +997,22 @@ esp_err_t esp_ble_gattc_execute_write (esp_gatt_if_t gattc_if, uint16_t conn_id, * 1. This function triggers `ESP_GATTC_REG_FOR_NOTIFY_EVT`. * 2. You should call `esp_ble_gattc_write_char_descr()` after this API to write Client Characteristic Configuration (CCC) descriptor to the value of 1 (Enable Notification) or 2 (Enable Indication). * 3. `handle` must be greater than 0. + * 4. This API should be invoked only after service discovery for the target + * peer has completed (i.e. after `ESP_GATTC_SEARCH_CMPL_EVT` has been + * received, or after a previously persisted cache has been loaded). The + * recommended way to obtain `handle` is via the cache accessor APIs such + * as `esp_ble_gattc_get_all_char` or `esp_ble_gattc_get_char_by_uuid`. + * 5. When the GATT cache for `server_bda` is ready, the stack will validate + * `handle` against the cache: it must refer to a characteristic value + * whose properties include Notify or Indicate. If this validation fails, + * `ESP_GATTC_REG_FOR_NOTIFY_EVT` reports `ESP_GATT_ILLEGAL_PARAMETER` + * and the registration is rejected. If the cache is not yet ready (for + * example the API is called before service discovery completes), the + * stack skips this validation for backward compatibility, but in that + * case any incoming notification whose handle does not match a prior + * registration will be silently dropped by the host. Calling the API + * with an invalid handle in that window is therefore strongly + * discouraged. * * @return * - ESP_OK: Success diff --git a/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c b/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c index 9aa81e375e2..fbc87a3c7b8 100644 --- a/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c +++ b/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c @@ -2208,10 +2208,16 @@ void bta_gattc_process_indicate(UINT16 conn_id, tGATTC_OPTYPE op, tGATT_CL_COMPL if (p_clcb != NULL) { bta_gattc_proc_other_indication(p_clcb, op, p_data, ¬ify); } - } else if (op == GATTC_OPTYPE_INDICATION) { - /* no one interested and need ack? */ - APPL_TRACE_DEBUG("%s no one interested, ack now", __func__); - GATTC_SendHandleValueConfirm(conn_id, handle); + } else { + /* No app registered for this handle: drop the PDU, but log it + * so the common "registered a wrong handle" bug is visible + * instead of silently failing. */ + APPL_TRACE_WARNING("drop %s, handle 0x%04x not registered", + (op == GATTC_OPTYPE_INDICATION) ? "ind" : "notif", + handle); + if (op == GATTC_OPTYPE_INDICATION) { + GATTC_SendHandleValueConfirm(conn_id, handle); + } } } } diff --git a/components/bt/host/bluedroid/bta/gatt/bta_gattc_api.c b/components/bt/host/bluedroid/bta/gatt/bta_gattc_api.c index 29df6772748..b72c62c9ec2 100644 --- a/components/bt/host/bluedroid/bta/gatt/bta_gattc_api.c +++ b/components/bt/host/bluedroid/bta/gatt/bta_gattc_api.c @@ -917,6 +917,7 @@ tBTA_GATT_STATUS BTA_GATTC_RegisterForNotifications (tBTA_GATTC_IF client_if, BD_ADDR bda, UINT16 handle) { tBTA_GATTC_RCB *p_clreg; + tBTA_GATTC_SERV *p_srcb; tBTA_GATT_STATUS status = BTA_GATT_ILLEGAL_PARAMETER; UINT8 i; @@ -926,6 +927,29 @@ tBTA_GATT_STATUS BTA_GATTC_RegisterForNotifications (tBTA_GATTC_IF client_if, return status; } + /* If cache is ready, validate handle is a notify/indicate-capable + * characteristic value handle; otherwise skip (legacy behaviour). */ + p_srcb = bta_gattc_find_srcb(bda); + if (p_srcb != NULL && p_srcb->p_srvc_cache != NULL && + p_srcb->state == BTA_GATTC_SERV_IDLE) { + tBTA_GATTC_CHARACTERISTIC *p_char = + bta_gattc_get_characteristic_srcb(p_srcb, handle); + + if (p_char == NULL) { + APPL_TRACE_ERROR("reg notif: bad handle 0x%04x", handle); + return BTA_GATT_ILLEGAL_PARAMETER; + } + + if ((p_char->properties & (BTA_GATT_CHAR_PROP_BIT_NOTIFY | + BTA_GATT_CHAR_PROP_BIT_INDICATE)) == 0) { + APPL_TRACE_ERROR("reg notif: handle 0x%04x prop 0x%02x not notif/ind", + handle, p_char->properties); + return BTA_GATT_ILLEGAL_PARAMETER; + } + } else { + APPL_TRACE_WARNING("reg notif: cache not ready, skip check"); + } + if ((p_clreg = bta_gattc_cl_get_regcb(client_if)) != NULL) { for (i = 0; i < BTA_GATTC_NOTIF_REG_MAX; i ++) { if ( p_clreg->notif_reg[i].in_use &&