From 94f0c1837a3b2111cd5660d804d05c035485008e Mon Sep 17 00:00:00 2001 From: zhiweijian Date: Fri, 27 Mar 2026 17:47:24 +0800 Subject: [PATCH] fix(ble): Fixed AI review bugs in bt common --- components/bt/common/btc/core/btc_manage.c | 8 +- components/bt/common/btc/core/btc_task.c | 32 ++- .../esp/blufi/bluedroid_host/esp_blufi.c | 80 +++++- .../common/btc/profile/esp/blufi/blufi_prf.c | 259 ++++++++++++++---- .../btc/profile/esp/blufi/blufi_protocol.c | 25 +- .../btc/profile/esp/blufi/include/esp_blufi.h | 3 +- .../btc/profile/esp/include/btc_blufi_prf.h | 3 +- components/bt/common/hci_log/bt_hci_log.c | 57 +++- components/bt/common/include/esp_assume.h | 18 ++ components/bt/common/osi/alarm.c | 17 +- components/bt/common/osi/config.c | 191 +++++++++---- components/bt/common/osi/fixed_queue.c | 7 + components/bt/common/osi/hash_map.c | 37 ++- components/bt/common/osi/list.c | 12 +- components/bt/common/osi/mutex.c | 8 + .../bt/host/bluedroid/api/esp_bluedroid_hci.c | 4 +- .../bt/host/bluedroid/api/esp_bt_main.c | 4 +- .../bt/host/bluedroid/btc/core/btc_config.c | 13 +- .../btc/profile/std/gap/btc_gap_ble.c | 31 ++- .../bt/host/bluedroid/stack/btu/btu_task.c | 55 ++-- 20 files changed, 676 insertions(+), 188 deletions(-) create mode 100644 components/bt/common/include/esp_assume.h diff --git a/components/bt/common/btc/core/btc_manage.c b/components/bt/common/btc/core/btc_manage.c index 4b63f1efbab..9ead68c8767 100644 --- a/components/bt/common/btc/core/btc_manage.c +++ b/components/bt/common/btc/core/btc_manage.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -51,15 +51,17 @@ int btc_profile_cb_set(btc_pid_t profile_id, void *cb) void *btc_profile_cb_get(btc_pid_t profile_id) { #if BTC_DYNAMIC_MEMORY == TRUE - void *p = btc_profile_cb_tab; + void **p = btc_profile_cb_tab; if (p == NULL) { return NULL; } +#else + void **p = btc_profile_cb_tab; #endif if (profile_id < 0 || profile_id >= BTC_PID_NUM) { return NULL; } - return btc_profile_cb_tab[profile_id]; + return p[profile_id]; } diff --git a/components/bt/common/btc/core/btc_task.c b/components/bt/common/btc/core/btc_task.c index 297157b3c2d..3da01b697bb 100644 --- a/components/bt/common/btc/core/btc_task.c +++ b/components/bt/common/btc/core/btc_task.c @@ -292,6 +292,8 @@ static void btc_thread_handler(void *arg) btc_msg_t *msg = (btc_msg_t *)arg; BTC_TRACE_DEBUG("%s msg %u %u %u %p\n", __func__, msg->sig, msg->pid, msg->act, msg->arg); + /* msg->pid is validated at btc_transfer_context() entry; any message that + * reaches this handler is guaranteed to carry a valid pid. */ switch (msg->sig) { case BTC_SIG_API_CALL: profile_tab[msg->pid].btc_call(msg); @@ -331,7 +333,12 @@ bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg btc_msg_t* lmsg; bt_status_t ret; // arg XOR arg_len - if ((msg == NULL) || ((arg == NULL) == !(arg_len == 0))) { + if ((msg == NULL) || ((arg == NULL) == !(arg_len == 0)) || + (msg->pid >= BTC_PID_NUM)) { + /* Reject invalid pid here, before any deep_copy runs, so the caller's + * arg is not yet duplicated into lmsg and there is nothing to free. + * This keeps the trust boundary at the single public entry point and + * makes the downstream handler unable to encounter an invalid pid. */ BTC_TRACE_WARNING("%s Invalid parameters\n", __func__); return BT_STATUS_PARM_INVALID; } @@ -564,22 +571,31 @@ bt_status_t btc_init(void) void btc_deinit(void) { - osi_thread_t *thread = btc_thread; - if (!thread) { + if (!btc_thread) { return; } - osi_thread_free(thread); - btc_thread = NULL; + /* Reverse order of btc_init(): + * 1) BLE GAP deinit must run BEFORE btc_deinit_mem(), otherwise under + * BTC_DYNAMIC_MEMORY the gl_bta_adv_data macro expands to + * *(NULL) and btc_cleanup_adv_data() early-returns, leaking the + * inner adv-data fields (p_manu / p_proprietary / p_services...). + * 2) BT classic GAP deinit follows. + * 3) Then release the dynamic-memory pool. + * 4) Finally tear down the BTC worker thread. + */ +#if (BLE_INCLUDED == TRUE) + btc_gap_ble_deinit(); +#endif ///BLE_INCLUDED == TRUE #if BTC_GAP_BT_INCLUDED btc_gap_bt_deinit(); #endif #if BTC_DYNAMIC_MEMORY btc_deinit_mem(); #endif -#if (BLE_INCLUDED == TRUE) - btc_gap_ble_deinit(); -#endif ///BLE_INCLUDED == TRUE + + osi_thread_free(btc_thread); + btc_thread = NULL; } int get_btc_work_queue_size(void) diff --git a/components/bt/common/btc/profile/esp/blufi/bluedroid_host/esp_blufi.c b/components/bt/common/btc/profile/esp/blufi/bluedroid_host/esp_blufi.c index 9fe47212bcb..5bb99ac8165 100644 --- a/components/bt/common/btc/profile/esp/blufi/bluedroid_host/esp_blufi.c +++ b/components/bt/common/btc/profile/esp/blufi/bluedroid_host/esp_blufi.c @@ -160,6 +160,9 @@ static bool blufi_rx_enqueue(const uint8_t *data, uint16_t len) static uint8_t server_if; static uint16_t conn_id; + +/* Forward declaration used by the GATTS event handler. */ +esp_err_t esp_blufi_close(esp_gatt_if_t gatts_if, uint16_t conn_id); static uint8_t blufi_service_uuid128[32] = { /* LSB <--------------------------------------------------------------------------------> MSB */ //first uuid, 16bit, [12],[13] is the value @@ -274,6 +277,10 @@ void esp_blufi_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_pa BLUFI_TRACE_DEBUG("GAP_EVT, event %d", event); switch (event) { case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + if (param->adv_data_cmpl.status != ESP_BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("Advertising data set failed, status %x", param->adv_data_cmpl.status); + break; + } esp_ble_gap_start_advertising(&blufi_adv_params); break; case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: @@ -303,8 +310,15 @@ void esp_blufi_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_pa case ESP_GAP_BLE_NC_REQ_EVT: /* The app will receive this evt when the IO has DisplayYesNO capability and the peer device IO also has DisplayYesNo capability. show the passkey number to the user to confirm it with the number displayed by peer device. */ - esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, true); - BLUFI_TRACE_WARNING("Numeric Comparison request, passkey %" PRIu32, param->ble_security.key_notif.passkey); + /* + * Security note: + * Auto-accepting Numeric Comparison would bypass the user confirmation step and + * effectively weaken MITM protection. Reject by default; applications that want + * MITM protection must implement explicit user confirmation logic. + */ + esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, false); + BLUFI_TRACE_WARNING("Numeric Comparison request rejected by default, passkey %" PRIu32, + param->ble_security.key_notif.passkey); break; case ESP_GAP_BLE_SEC_REQ_EVT: /* send the positive(true) security response to the peer device to accept the security request. @@ -414,7 +428,7 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) break; } case BTA_GATTS_READ_EVT: - memset(&rsp, 0, sizeof(tBTA_GATTS_API_RSP)); + memset(&rsp, 0, sizeof(rsp)); rsp.attr_value.handle = p_data->req_data.p_data->read_req.handle; rsp.attr_value.len = 1; rsp.attr_value.value[0] = 0x00; @@ -441,7 +455,9 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) status = GATT_INVALID_OFFSET; break; } - blufi_env.prepare_buf = osi_malloc(BLUFI_PREPARE_BUF_MAX_SIZE); + /* Use calloc so any unwritten gaps are deterministic (0) if peer sends + * out-of-order/overlapping fragments and later executes the write. */ + blufi_env.prepare_buf = osi_calloc(BLUFI_PREPARE_BUF_MAX_SIZE); blufi_env.prepare_len = 0; if (blufi_env.prepare_buf == NULL) { BLUFI_TRACE_ERROR("Blufi prep no mem\n"); @@ -474,7 +490,14 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) memcpy(blufi_env.prepare_buf + p_data->req_data.p_data->write_req.offset, p_data->req_data.p_data->write_req.value, p_data->req_data.p_data->write_req.len); - blufi_env.prepare_len += p_data->req_data.p_data->write_req.len; + /* Maintain the maximum written extent rather than summing lengths. + * Summation breaks on retransmissions/overlaps and can exceed the actual + * written range, causing parsing of unwritten bytes. */ + const uint16_t end = + (uint16_t)(p_data->req_data.p_data->write_req.offset + p_data->req_data.p_data->write_req.len); + if (end > blufi_env.prepare_len) { + blufi_env.prepare_len = end; + } return; } else { @@ -548,7 +571,11 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) blufi_env.handle_char_p2e = p_data->add_result.attr_id; BTA_GATTS_AddCharacteristic(blufi_env.handle_srvc, &blufi_char_uuid_e2p, + #if CONFIG_BT_BLUFI_BLE_SMP_ENABLE + (GATT_PERM_READ_ENC_MITM), + #else (GATT_PERM_READ), + #endif (GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY), NULL, NULL); break; @@ -556,7 +583,11 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) blufi_env.handle_char_e2p = p_data->add_result.attr_id; BTA_GATTS_AddCharDescriptor (blufi_env.handle_srvc, + #if CONFIG_BT_BLUFI_BLE_SMP_ENABLE + (GATT_PERM_READ_ENC_MITM | GATT_PERM_WRITE_ENC_MITM), + #else (GATT_PERM_READ | GATT_PERM_WRITE), + #endif &blufi_descr_uuid_e2p, NULL, NULL); break; @@ -585,6 +616,13 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) btc_msg_t msg; esp_blufi_cb_param_t param; + if (blufi_env.is_connected) { + BLUFI_TRACE_WARNING("BLUFI already connected; rejecting new connection from "BT_BD_ADDR_STR", connect_id=%d", + BT_BD_ADDR_HEX(p_data->conn.remote_bda), p_data->conn.conn_id); + (void)esp_blufi_close(p_data->conn.server_if, BTC_GATT_GET_CONN_ID(p_data->conn.conn_id)); + break; + } + //set the connection flag to true BLUFI_TRACE_API("\ndevice is connected "BT_BD_ADDR_STR", server_if=%d,reason=0x%x,connect_id=%d\n", BT_BD_ADDR_HEX(p_data->conn.remote_bda), p_data->conn.server_if, @@ -609,6 +647,18 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) btc_msg_t msg; esp_blufi_cb_param_t param; + /* Ignore disconnects that do not belong to the active BLUFI connection. + * Even though we reject concurrent connections, the stack may still + * deliver disconnect events for transient/previous connections; blindly + * resetting the global env would corrupt the active session. */ + if (!blufi_env.is_connected || blufi_env.conn_id != p_data->conn.conn_id) { + BLUFI_TRACE_WARNING("Ignoring BLUFI disconnect for non-active conn_id=%d (active=%d, is_connected=%d) from " + BT_BD_ADDR_STR, + p_data->conn.conn_id, blufi_env.conn_id, blufi_env.is_connected, + BT_BD_ADDR_HEX(p_data->conn.remote_bda)); + break; + } + blufi_env.is_connected = false; //set the connection flag to true BLUFI_TRACE_API("\ndevice is disconnected "BT_BD_ADDR_STR", server_if=%d,reason=0x%x,connect_id=%d\n", @@ -626,6 +676,12 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) blufi_env.aggr_buf = NULL; } + if (blufi_env.prepare_buf) { + osi_free(blufi_env.prepare_buf); + blufi_env.prepare_buf = NULL; + blufi_env.prepare_len = 0; + } + msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_BLUFI; msg.act = ESP_BLUFI_EVENT_BLE_DISCONNECT; @@ -660,6 +716,18 @@ void esp_blufi_deinit(void) #if !SOC_MPI_SUPPORTED blufi_rx_worker_stop(); #endif + if (blufi_env.prepare_buf) { + osi_free(blufi_env.prepare_buf); + blufi_env.prepare_buf = NULL; + blufi_env.prepare_len = 0; + } + if (blufi_env.aggr_buf) { + osi_free(blufi_env.aggr_buf); + blufi_env.aggr_buf = NULL; + } + blufi_env.offset = 0; + blufi_env.total_len = 0; + BTA_GATTS_StopService(blufi_env.handle_srvc); BTA_GATTS_DeleteService(blufi_env.handle_srvc); /* register the BLUFI profile to the BTA_GATTS module*/ @@ -720,6 +788,8 @@ esp_err_t esp_blufi_close(esp_gatt_if_t gatts_if, uint16_t conn_id) ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED); btc_msg_t msg; btc_ble_gatts_args_t arg; + memset(&msg, 0, sizeof(msg)); + memset(&arg, 0, sizeof(arg)); msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_CLOSE; diff --git a/components/bt/common/btc/profile/esp/blufi/blufi_prf.c b/components/bt/common/btc/profile/esp/blufi/blufi_prf.c index 304b162555d..d1fc67ea33e 100644 --- a/components/bt/common/btc/profile/esp/blufi/blufi_prf.c +++ b/components/bt/common/btc/profile/esp/blufi/blufi_prf.c @@ -5,10 +5,10 @@ */ #include +#include #include #include -#include #include "osi/allocator.h" @@ -34,6 +34,22 @@ tBLUFI_ENV *blufi_env_ptr; // static functions declare static void btc_blufi_send_ack(uint8_t seq); +static inline void btc_blufi_env_free_buffers(void) +{ + if (blufi_env.aggr_buf) { + osi_free(blufi_env.aggr_buf); + blufi_env.aggr_buf = NULL; + } + blufi_env.total_len = 0; + blufi_env.offset = 0; + + if (blufi_env.prepare_buf) { + osi_free(blufi_env.prepare_buf); + blufi_env.prepare_buf = NULL; + } + blufi_env.prepare_len = 0; +} + inline void btc_blufi_cb_to_app(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param) { esp_blufi_event_cb_t btc_blufi_cb = (esp_blufi_event_cb_t)btc_profile_cb_get(BTC_PID_BLUFI); @@ -52,6 +68,7 @@ static uint8_t btc_blufi_profile_init(void) return ESP_BLUFI_ERROR; } + btc_blufi_env_free_buffers(); memset(&blufi_env, 0x0, sizeof(blufi_env)); blufi_env.cbs = store_p; /* if set callback prior, restore the point */ blufi_env.frag_size = BLUFI_FRAG_DATA_DEFAULT_LEN; @@ -138,6 +155,7 @@ void btc_blufi_recv_handler(uint8_t *data, int len) blufi_env.aggr_buf = NULL; \ } \ blufi_env.offset = 0; \ + blufi_env.total_len = 0; \ } while (0) // first step, decrypt @@ -170,11 +188,14 @@ void btc_blufi_recv_handler(uint8_t *data, int len) } if (BLUFI_FC_IS_FRAG(hdr->fc)) { - if(hdr->data_len<2) { - BTC_TRACE_ERROR("%s: Invalid fragment data length: %d", __func__, hdr->data_len); - btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); - return; + if (hdr->data_len < 2) { + BTC_TRACE_ERROR("%s: Invalid fragment data length: %d", __func__, hdr->data_len); + BLUFI_RESET_AGGR_BUF(); + btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); + return; } + uint16_t frag_payload = (uint16_t)(hdr->data_len - 2); + if (blufi_env.offset == 0) { /* blufi_env.aggr_buf should be NULL if blufi_env.offset is 0. @@ -183,17 +204,16 @@ void btc_blufi_recv_handler(uint8_t *data, int len) */ if (blufi_env.aggr_buf) { BTC_TRACE_ERROR("%s msg error, blufi_env.aggr_buf is not freed\n", __func__); - osi_free(blufi_env.aggr_buf); - blufi_env.aggr_buf = NULL; + BLUFI_RESET_AGGR_BUF(); btc_blufi_report_error(ESP_BLUFI_MSG_STATE_ERROR); return; } - if (hdr->data_len < 2) { - BTC_TRACE_ERROR("%s frag header too short: data_len=%d\n", __func__, hdr->data_len); + blufi_env.total_len = hdr->data[0] | (((uint16_t) hdr->data[1]) << 8); + if (blufi_env.total_len == 0) { + BTC_TRACE_ERROR("%s zero total_len\n", __func__); btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); return; } - blufi_env.total_len = hdr->data[0] | (((uint16_t) hdr->data[1]) << 8); blufi_env.aggr_buf = osi_malloc(blufi_env.total_len); if (blufi_env.aggr_buf == NULL) { BTC_TRACE_ERROR("%s no mem, len %d\n", __func__, blufi_env.total_len); @@ -201,15 +221,15 @@ void btc_blufi_recv_handler(uint8_t *data, int len) return; } } - if (blufi_env.offset + hdr->data_len - 2 <= blufi_env.total_len){ - memcpy(blufi_env.aggr_buf + blufi_env.offset, hdr->data + 2, hdr->data_len - 2); - blufi_env.offset += (hdr->data_len - 2); - } else { - BTC_TRACE_ERROR("%s payload is longer than packet length, len %d \n", __func__, blufi_env.total_len); + + if ((uint32_t)blufi_env.offset + frag_payload > blufi_env.total_len) { + BTC_TRACE_ERROR("%s payload is longer than packet length, len %d\n", __func__, blufi_env.total_len); BLUFI_RESET_AGGR_BUF(); btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); return; } + memcpy(blufi_env.aggr_buf + blufi_env.offset, hdr->data + 2, frag_payload); + blufi_env.offset += frag_payload; } else { if (blufi_env.offset > 0) { /* if previous pkt is frag */ @@ -255,6 +275,16 @@ void btc_blufi_send_encap(uint8_t type, uint8_t *data, int total_data_len) return; } + if (total_data_len <= 0 || data == NULL) { + BTC_TRACE_ERROR("%s invalid data or length %d\n", __func__, total_data_len); + return; + } + if (total_data_len > UINT16_MAX) { + BTC_TRACE_ERROR("%s total_data_len too large %d\n", __func__, total_data_len); + btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); + return; + } + while (remain_len > 0) { if (remain_len > blufi_env.frag_size) { hdr = osi_malloc(sizeof(struct blufi_hdr) + 2 + blufi_env.frag_size + 2); @@ -264,8 +294,8 @@ void btc_blufi_send_encap(uint8_t type, uint8_t *data, int total_data_len) } hdr->fc = 0x0; hdr->data_len = blufi_env.frag_size + 2; - hdr->data[0] = remain_len & 0xff; - hdr->data[1] = (remain_len >> 8) & 0xff; + hdr->data[0] = (uint8_t)(remain_len & 0xff); + hdr->data[1] = (uint8_t)((remain_len >> 8) & 0xff); memcpy(hdr->data + 2, &data[total_data_len - remain_len], blufi_env.frag_size); //copy first, easy for check sum hdr->fc |= BLUFI_FC_FRAG; } else { @@ -325,15 +355,99 @@ void btc_blufi_send_encap(uint8_t type, uint8_t *data, int total_data_len) } } +/* IEEE 802.11 SSID / passphrase upper bounds for BLUFI encoding */ +#define BLUFI_WIFI_SSID_MAX 32 +#define BLUFI_WIFI_PASS_MAX 64 +#define BLUFI_WIFI_CONN_REP_MAX 512 + +static size_t blufi_extra_field_len(int len, size_t max_len) +{ + if (len <= 0) { + return 0; + } + return (size_t)len > max_len ? max_len : (size_t)len; +} + +static bool btc_blufi_wifi_conn_report_add(size_t *acc, size_t add) +{ + if (*acc > SIZE_MAX - add) { + return false; + } + *acc += add; + return *acc <= BLUFI_WIFI_CONN_REP_MAX; +} + +static size_t btc_blufi_wifi_conn_report_need(esp_blufi_extra_info_t *info) +{ + size_t n = 3; + + if (info == NULL) { + return n; + } + if (info->sta_bssid_set && !btc_blufi_wifi_conn_report_add(&n, 8)) { + return 0; + } + if (info->sta_ssid) { + size_t sl = blufi_extra_field_len(info->sta_ssid_len, BLUFI_WIFI_SSID_MAX); + if (!btc_blufi_wifi_conn_report_add(&n, 2 + sl)) { + return 0; + } + } + if (info->sta_passwd) { + size_t pl = blufi_extra_field_len(info->sta_passwd_len, BLUFI_WIFI_PASS_MAX); + if (!btc_blufi_wifi_conn_report_add(&n, 2 + pl)) { + return 0; + } + } + if (info->softap_ssid) { + size_t sl = blufi_extra_field_len(info->softap_ssid_len, BLUFI_WIFI_SSID_MAX); + if (!btc_blufi_wifi_conn_report_add(&n, 2 + sl)) { + return 0; + } + } + if (info->softap_passwd) { + size_t pl = blufi_extra_field_len(info->softap_passwd_len, BLUFI_WIFI_PASS_MAX); + if (!btc_blufi_wifi_conn_report_add(&n, 2 + pl)) { + return 0; + } + } + if (info->softap_authmode_set && !btc_blufi_wifi_conn_report_add(&n, 3)) { + return 0; + } + if (info->softap_max_conn_num_set && !btc_blufi_wifi_conn_report_add(&n, 3)) { + return 0; + } + if (info->softap_channel_set && !btc_blufi_wifi_conn_report_add(&n, 3)) { + return 0; + } + if (info->sta_max_conn_retry_set && !btc_blufi_wifi_conn_report_add(&n, 3)) { + return 0; + } + if (info->sta_conn_end_reason_set && !btc_blufi_wifi_conn_report_add(&n, 3)) { + return 0; + } + if (info->sta_conn_rssi_set && !btc_blufi_wifi_conn_report_add(&n, 3)) { + return 0; + } + return n; +} + static void btc_blufi_wifi_conn_report(uint8_t opmode, uint8_t sta_conn_state, uint8_t softap_conn_num, esp_blufi_extra_info_t *info, int info_len) { uint8_t type; uint8_t *data; - int data_len; uint8_t *p; + size_t buf_len; - data_len = info_len + 3; - p = data = osi_malloc(data_len); + (void)info_len; + + buf_len = btc_blufi_wifi_conn_report_need(info); + if (buf_len == 0 || buf_len > BLUFI_WIFI_CONN_REP_MAX) { + BTC_TRACE_ERROR("%s invalid buffer size %u\n", __func__, (unsigned)buf_len); + return; + } + + p = data = osi_malloc(buf_len); if (data == NULL) { BTC_TRACE_ERROR("%s no mem\n", __func__); return; @@ -352,28 +466,32 @@ static void btc_blufi_wifi_conn_report(uint8_t opmode, uint8_t sta_conn_state, u p += 6; } if (info->sta_ssid) { + size_t sl = blufi_extra_field_len(info->sta_ssid_len, BLUFI_WIFI_SSID_MAX); *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_SSID; - *p++ = info->sta_ssid_len; - memcpy(p, info->sta_ssid, info->sta_ssid_len); - p += info->sta_ssid_len; + *p++ = (uint8_t)sl; + memcpy(p, info->sta_ssid, sl); + p += sl; } if (info->sta_passwd) { + size_t pl = blufi_extra_field_len(info->sta_passwd_len, BLUFI_WIFI_PASS_MAX); *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD; - *p++ = info->sta_passwd_len; - memcpy(p, info->sta_passwd, info->sta_passwd_len); - p += info->sta_passwd_len; + *p++ = (uint8_t)pl; + memcpy(p, info->sta_passwd, pl); + p += pl; } if (info->softap_ssid) { + size_t sl = blufi_extra_field_len(info->softap_ssid_len, BLUFI_WIFI_SSID_MAX); *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID; - *p++ = info->softap_ssid_len; - memcpy(p, info->softap_ssid, info->softap_ssid_len); - p += info->softap_ssid_len; + *p++ = (uint8_t)sl; + memcpy(p, info->softap_ssid, sl); + p += sl; } if (info->softap_passwd) { + size_t pl = blufi_extra_field_len(info->softap_passwd_len, BLUFI_WIFI_PASS_MAX); *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD; - *p++ = info->softap_passwd_len; - memcpy(p, info->softap_passwd, info->softap_passwd_len); - p += info->softap_passwd_len; + *p++ = (uint8_t)pl; + memcpy(p, info->softap_passwd, pl); + p += pl; } if (info->softap_authmode_set) { *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE; @@ -403,14 +521,16 @@ static void btc_blufi_wifi_conn_report(uint8_t opmode, uint8_t sta_conn_state, u if (info->sta_conn_rssi_set) { *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_RSSI; *p++ = 1; - *p++ = info->sta_conn_rssi; + *p++ = (uint8_t)info->sta_conn_rssi; } } - if (p - data > data_len) { - BTC_TRACE_ERROR("%s len error %d %d\n", __func__, (int)(p - data), data_len); + if ((size_t)(p - data) != buf_len) { + BTC_TRACE_ERROR("%s len mismatch %d %u\n", __func__, (int)(p - data), (unsigned)buf_len); + osi_free(data); + return; } - btc_blufi_send_encap(type, data, data_len); + btc_blufi_send_encap(type, data, (int)buf_len); osi_free(data); } @@ -420,30 +540,48 @@ void btc_blufi_send_wifi_list(uint16_t apCount, esp_blufi_ap_record_t *list) uint8_t *data; int data_len; uint8_t *p; - // malloc size: (len + RSSI + ssid buffer) * apCount; - uint32_t malloc_size = (1 + 1 + sizeof(list->ssid)) * apCount; + uint32_t entry_sz; + uint32_t malloc_size; + + if (list == NULL || apCount == 0) { + return; + } + entry_sz = 1u + 1u + (uint32_t)sizeof(list[0].ssid); + + /* The encoded WiFi list is later wrapped by btc_blufi_send_encap(), + * which rejects total_data_len > UINT16_MAX (single BLUFI frame limit). + * Cap apCount against that real ceiling here instead of the previous + * (uint16_t)(UINT32_MAX / entry_sz) check, which the narrowing cast + * silently truncated to 18439 entries (~645 KB) and let the buffer + * be allocated only to be discarded later by send_encap. + * The apCount value returned by WiFi is very small in practice, but + * we still want to fail fast and avoid a pointless large allocation. */ + if ((uint32_t)apCount > (UINT16_MAX / entry_sz)) { + BTC_TRACE_ERROR("%s apCount %u exceeds BLUFI single-frame limit\n", + __func__, (unsigned)apCount); + return; + } + malloc_size = (uint32_t)apCount * entry_sz; p = data = osi_malloc(malloc_size); if (data == NULL) { BTC_TRACE_ERROR("malloc error\n"); return; } type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_WIFI_LIST); - for (int i = 0; i < apCount; ++i) - { - uint32_t len = strlen((const char *)list[i].ssid); - data_len = (p - data); - //current_len + ssid + rssi + total_len_value - if((data_len + len + 1 + 1) > malloc_size) { + for (int i = 0; i < apCount; ++i) { + size_t len = strnlen((const char *)list[i].ssid, sizeof(list[i].ssid)); + data_len = (int)(p - data); + if ((uint32_t)data_len + len + 1u + 1u > malloc_size) { BTC_TRACE_ERROR("%s len error", __func__); osi_free(data); return; } - *p++ = len + 1; // length of ssid + rssi - *p++ = list[i].rssi; + *p++ = (uint8_t)(len + 1); /* length of ssid + rssi */ + *p++ = (uint8_t)list[i].rssi; memcpy(p, list[i].ssid, len); - p = p + len; + p += len; } - data_len = (p - data); + data_len = (int)(p - data); btc_blufi_send_encap(type, data, data_len); osi_free(data); } @@ -506,76 +644,97 @@ void btc_blufi_cb_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) switch (msg->act) { case ESP_BLUFI_EVENT_RECV_STA_SSID: + dst->sta_ssid.ssid = NULL; dst->sta_ssid.ssid = osi_malloc(src->sta_ssid.ssid_len); if (dst->sta_ssid.ssid == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->sta_ssid.ssid, src->sta_ssid.ssid, src->sta_ssid.ssid_len); break; case ESP_BLUFI_EVENT_RECV_STA_PASSWD: + dst->sta_passwd.passwd = NULL; dst->sta_passwd.passwd = osi_malloc(src->sta_passwd.passwd_len); if (dst->sta_passwd.passwd == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->sta_passwd.passwd, src->sta_passwd.passwd, src->sta_passwd.passwd_len); break; case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID: + dst->softap_ssid.ssid = NULL; dst->softap_ssid.ssid = osi_malloc(src->softap_ssid.ssid_len); if (dst->softap_ssid.ssid == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->softap_ssid.ssid, src->softap_ssid.ssid, src->softap_ssid.ssid_len); break; case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD: + dst->softap_passwd.passwd = NULL; dst->softap_passwd.passwd = osi_malloc(src->softap_passwd.passwd_len); if (dst->softap_passwd.passwd == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->softap_passwd.passwd, src->softap_passwd.passwd, src->softap_passwd.passwd_len); break; case ESP_BLUFI_EVENT_RECV_USERNAME: + dst->username.name = NULL; dst->username.name = osi_malloc(src->username.name_len); if (dst->username.name == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->username.name, src->username.name, src->username.name_len); break; case ESP_BLUFI_EVENT_RECV_CA_CERT: + dst->ca.cert = NULL; dst->ca.cert = osi_malloc(src->ca.cert_len); if (dst->ca.cert == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->ca.cert, src->ca.cert, src->ca.cert_len); break; case ESP_BLUFI_EVENT_RECV_CLIENT_CERT: + dst->client_cert.cert = NULL; dst->client_cert.cert = osi_malloc(src->client_cert.cert_len); if (dst->client_cert.cert == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->client_cert.cert, src->client_cert.cert, src->client_cert.cert_len); break; case ESP_BLUFI_EVENT_RECV_SERVER_CERT: + dst->server_cert.cert = NULL; dst->server_cert.cert = osi_malloc(src->server_cert.cert_len); if (dst->server_cert.cert == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->server_cert.cert, src->server_cert.cert, src->server_cert.cert_len); break; case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY: + dst->client_pkey.pkey = NULL; dst->client_pkey.pkey = osi_malloc(src->client_pkey.pkey_len); if (dst->client_pkey.pkey == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->client_pkey.pkey, src->client_pkey.pkey, src->client_pkey.pkey_len); break; case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY: + dst->server_pkey.pkey = NULL; dst->server_pkey.pkey = osi_malloc(src->server_pkey.pkey_len); if (dst->server_pkey.pkey == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->server_pkey.pkey, src->server_pkey.pkey, src->server_pkey.pkey_len); break; case ESP_BLUFI_EVENT_RECV_CUSTOM_DATA: + dst->custom_data.data = NULL; dst->custom_data.data = osi_malloc(src->custom_data.data_len); if (dst->custom_data.data == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); @@ -831,8 +990,8 @@ void btc_blufi_call_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) } case BTC_BLUFI_ACT_SEND_WIFI_LIST:{ esp_blufi_ap_record_t *list = src->wifi_list.list; - src->wifi_list.list = NULL; - if (list == NULL || src->wifi_list.apCount <= 0) { + dst->wifi_list.list = NULL; + if (list == NULL || src->wifi_list.apCount == 0) { break; } dst->wifi_list.list = (esp_blufi_ap_record_t *)osi_malloc(sizeof(esp_blufi_ap_record_t) * src->wifi_list.apCount); diff --git a/components/bt/common/btc/profile/esp/blufi/blufi_protocol.c b/components/bt/common/btc/profile/esp/blufi/blufi_protocol.c index 0b0c08981e0..53a570258cc 100644 --- a/components/bt/common/btc/profile/esp/blufi/blufi_protocol.c +++ b/components/bt/common/btc/profile/esp/blufi/blufi_protocol.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,6 +13,7 @@ #include "btc_blufi_prf.h" #include "btc/btc_task.h" #include "btc/btc_manage.h" +#include "osi/allocator.h" #include "blufi_int.h" @@ -27,6 +28,14 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) int output_len = 0; bool need_free = false; +#define BLUFI_REQUIRE_LEN_AT_LEAST(_n) \ + if (len < (_n)) { \ + BTC_TRACE_ERROR("%s pkt %02x len too short: %d\n", \ + __func__, type, len); \ + btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); \ + break; \ + } + switch (BLUFI_GET_TYPE(type)) { case BLUFI_TYPE_CTRL: switch (BLUFI_GET_SUBTYPE(type)) { @@ -34,9 +43,11 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) /* TODO: check sequence */ break; case BLUFI_TYPE_CTRL_SUBTYPE_SET_SEC_MODE: + BLUFI_REQUIRE_LEN_AT_LEAST(1); blufi_env.sec_mode = data[0]; break; case BLUFI_TYPE_CTRL_SUBTYPE_SET_WIFI_OPMODE: + BLUFI_REQUIRE_LEN_AT_LEAST(1); msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_BLUFI; msg.act = ESP_BLUFI_EVENT_SET_WIFI_OPMODE; @@ -109,11 +120,16 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_NEG), output_data, output_len); } + if (need_free && output_data) { + osi_free(output_data); + output_data = NULL; + } break; case BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID: if (len < 6) { BTC_TRACE_ERROR("%s STA_BSSID data too short: %d\n", __func__, len); - return; + btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); + break; } msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_BLUFI; @@ -159,6 +175,7 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free); break; case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM: + BLUFI_REQUIRE_LEN_AT_LEAST(1); msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_BLUFI; msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM; @@ -167,6 +184,7 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL, NULL); break; case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE: + BLUFI_REQUIRE_LEN_AT_LEAST(1); msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_BLUFI; msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE; @@ -175,6 +193,7 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL, NULL); break; case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL: + BLUFI_REQUIRE_LEN_AT_LEAST(1); msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_BLUFI; msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL; @@ -252,6 +271,8 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) default: break; } + +#undef BLUFI_REQUIRE_LEN_AT_LEAST } #endif ///BLUFI_INCLUDED == TRUE diff --git a/components/bt/common/btc/profile/esp/blufi/include/esp_blufi.h b/components/bt/common/btc/profile/esp/blufi/include/esp_blufi.h index 96d97591df9..5e800db9b10 100644 --- a/components/bt/common/btc/profile/esp/blufi/include/esp_blufi.h +++ b/components/bt/common/btc/profile/esp/blufi/include/esp_blufi.h @@ -18,6 +18,7 @@ #ifdef CONFIG_BT_BLUEDROID_ENABLED #include "esp_gap_ble_api.h" +#include "esp_gatt_defs.h" #endif #ifdef __cplusplus @@ -61,7 +62,7 @@ void esp_blufi_btc_deinit(void); * - other : failed * */ -esp_err_t esp_blufi_close(uint8_t gatts_if, uint16_t conn_id); +esp_err_t esp_blufi_close(esp_gatt_if_t gatts_if, uint16_t conn_id); void esp_blufi_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); #endif diff --git a/components/bt/common/btc/profile/esp/include/btc_blufi_prf.h b/components/bt/common/btc/profile/esp/include/btc_blufi_prf.h index 1174f01ee0b..9112d75aa78 100644 --- a/components/bt/common/btc/profile/esp/include/btc_blufi_prf.h +++ b/components/bt/common/btc/profile/esp/include/btc_blufi_prf.h @@ -30,7 +30,7 @@ extern "C" { #define BT_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] #define GATT_UUID_CHAR_CLIENT_CONFIG 0x2902 /* Client Characteristic Configuration */ -//define the blufi serivce uuid +//define the blufi service uuid #define BLUFI_SERVICE_UUID 0xFFFF //define the blufi Char uuid (PHONE to ESP32) #define BLUFI_CHAR_P2E_UUID 0xFF01 @@ -98,6 +98,7 @@ void btc_blufi_cb_handler(btc_msg_t *msg); void btc_blufi_call_handler(btc_msg_t *msg); void btc_blufi_set_callbacks(esp_blufi_callbacks_t *callbacks); +void btc_blufi_report_error(esp_blufi_error_state_t state); void btc_blufi_recv_handler(uint8_t *data, int len); void btc_blufi_send_notify(uint8_t *pkt, int pkt_len); void btc_blufi_call_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); diff --git a/components/bt/common/hci_log/bt_hci_log.c b/components/bt/common/hci_log/bt_hci_log.c index fef3760b3f2..5840adab856 100644 --- a/components/bt/common/hci_log/bt_hci_log.c +++ b/components/bt/common/hci_log/bt_hci_log.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,9 +13,14 @@ #include "esp_timer.h" #if (BT_HCI_LOG_INCLUDED == TRUE) + +static const char *const TAG = "bt_hci_log"; + #define BT_HCI_LOG_PRINT_TAG (1) #define BT_HCI_LOG_DATA_BUF_SIZE (1024 * HCI_LOG_DATA_BUFFER_SIZE) #define BT_HCI_LOG_ADV_BUF_SIZE (1024 * HCI_LOG_ADV_BUFFER_SIZE) +/* Max payload per HCI log line; larger inputs are dropped (not logged). */ +#define BT_HCI_LOG_RECORD_PAYLOAD_MAX (2048U) typedef struct { osi_mutex_t mutex_lock; @@ -64,8 +69,14 @@ esp_err_t bt_hci_log_init(void) g_bt_hci_log_adv_ctl.buf_size = BT_HCI_LOG_ADV_BUF_SIZE; g_bt_hci_log_adv_ctl.p_hci_log_buffer = g_bt_hci_log_adv_buffer; - osi_mutex_new((osi_mutex_t *)&g_bt_hci_log_data_ctl.mutex_lock); - osi_mutex_new((osi_mutex_t *)&g_bt_hci_log_adv_ctl.mutex_lock); + if (osi_mutex_new((osi_mutex_t *)&g_bt_hci_log_data_ctl.mutex_lock) != 0) { + bt_hci_log_deinit(); + return ESP_FAIL; + } + if (osi_mutex_new((osi_mutex_t *)&g_bt_hci_log_adv_ctl.mutex_lock) != 0) { + bt_hci_log_deinit(); + return ESP_FAIL; + } return ESP_OK; } @@ -191,9 +202,16 @@ void bt_hci_log_record_string(bt_hci_log_t *p_hci_log_ctl, char *string) g_hci_log_buffer = p_hci_log_ctl->p_hci_log_buffer; - while (*string != '\0') { - g_hci_log_buffer[p_hci_log_ctl->log_record_in] = *string; - ++string; + if (string == NULL) { + return; + } + + /* Avoid unbounded memory scan if string is not NUL-terminated. */ + const size_t max_len = 256; + size_t len = strnlen(string, max_len); + + for (size_t i = 0; i < len; i++) { + g_hci_log_buffer[p_hci_log_ctl->log_record_in] = (uint8_t)string[i]; if (++p_hci_log_ctl->log_record_in >= p_hci_log_ctl->buf_size) { p_hci_log_ctl->log_record_in = 0; @@ -211,6 +229,7 @@ esp_err_t IRAM_ATTR bt_hci_log_record_data(bt_hci_log_t *p_hci_log_ctl, char *st uint8_t *g_hci_log_buffer; int64_t ts; uint8_t *temp_buf; + uint16_t record_len; if (!p_hci_log_ctl->p_hci_log_buffer) { return ESP_FAIL; @@ -221,21 +240,34 @@ esp_err_t IRAM_ATTR bt_hci_log_record_data(bt_hci_log_t *p_hci_log_ctl, char *st if (!g_hci_log_buffer) { return ESP_FAIL; } + if (p_hci_log_ctl->mutex_lock == NULL) { + return ESP_FAIL; + } + + if ((data_len == 0) || (data == NULL)) { + return ESP_ERR_INVALID_ARG; + } ts = esp_timer_get_time(); - temp_buf = (uint8_t *)malloc(data_len + 8); + if (data_len > BT_HCI_LOG_RECORD_PAYLOAD_MAX) { + ESP_LOGW(TAG, "HCI log record dropped: data_len=%u (max %u)", + (unsigned)data_len, (unsigned)BT_HCI_LOG_RECORD_PAYLOAD_MAX); + return ESP_FAIL; + } + + record_len = (uint16_t)((uint32_t)data_len + 8U); + + temp_buf = (uint8_t *)malloc((size_t)record_len); if (!temp_buf) { return ESP_ERR_NO_MEM; } - memset(temp_buf, 0x0, data_len + 8); + memset(temp_buf, 0x0, (size_t)record_len); memcpy(temp_buf, &ts, 8); memcpy(temp_buf + 8, data, data_len); - data_len += 8; - mutex_lock = p_hci_log_ctl->mutex_lock; osi_mutex_lock(&mutex_lock, OSI_MUTEX_MAX_TIMEOUT); @@ -267,7 +299,7 @@ esp_err_t IRAM_ATTR bt_hci_log_record_data(bt_hci_log_t *p_hci_log_ctl, char *st bt_hci_log_record_string(p_hci_log_ctl, str); } - bt_hci_log_record_hex(p_hci_log_ctl, temp_buf, data_len); + bt_hci_log_record_hex(p_hci_log_ctl, temp_buf, record_len); g_hci_log_buffer[p_hci_log_ctl->log_record_in] = '\n'; @@ -296,6 +328,9 @@ void bt_hci_log_data_show(bt_hci_log_t *p_hci_log_ctl) if (!p_hci_log_ctl->p_hci_log_buffer) { return; } + if (p_hci_log_ctl->mutex_lock == NULL) { + return; + } osi_mutex_t mutex_lock = p_hci_log_ctl->mutex_lock; diff --git a/components/bt/common/include/esp_assume.h b/components/bt/common/include/esp_assume.h new file mode 100644 index 00000000000..7ef41004445 --- /dev/null +++ b/components/bt/common/include/esp_assume.h @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +#if !defined(ESP_ASSUME_NONNULL) +#if defined(CONFIG_COMPILER_OPTIMIZATION_DEBUG) || defined(CONFIG_BT_NIMBLE_DEBUG) || !defined(NDEBUG) +/* Debug: fail fast if upstream contract is broken */ +#define ESP_ASSUME_NONNULL(p) do { assert((p) != NULL); } while (0) +#else +/* Release: contract only (no extra code size) */ +#define ESP_ASSUME_NONNULL(p) do { (void)(p); } while (0) +#endif +#endif diff --git a/components/bt/common/osi/alarm.c b/components/bt/common/osi/alarm.c index 464bbc2c897..6fe46625e74 100644 --- a/components/bt/common/osi/alarm.c +++ b/components/bt/common/osi/alarm.c @@ -323,9 +323,20 @@ bool osi_alarm_is_active(osi_alarm_t *alarm) { assert(alarm != NULL); - if (alarm->alarm_hdl != NULL) { - return esp_timer_is_active(alarm->alarm_hdl); + assert(alarm_mutex != NULL); + + bool active = false; + osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT); + if (alarm_state != ALARM_STATE_OPEN) { + active = false; + goto end; } - return false; + if (alarm->alarm_hdl != NULL) { + active = esp_timer_is_active(alarm->alarm_hdl); + } + +end: + osi_mutex_unlock(&alarm_mutex); + return active; } diff --git a/components/bt/common/osi/config.c b/components/bt/common/osi/config.c index fb6727a7a3d..38c981bc7b8 100644 --- a/components/bt/common/osi/config.c +++ b/components/bt/common/osi/config.c @@ -16,6 +16,7 @@ #include #include "bt_common.h" +#include "esp_assume.h" #include "osi/allocator.h" #include "osi/config.h" #include "osi/list.h" @@ -40,7 +41,7 @@ struct config_t { // Empty definition; this type is aliased to list_node_t. struct config_section_iter_t {}; -static void config_parse(nvs_handle_t fp, config_t *config); +static bool config_parse(nvs_handle_t fp, config_t *config); static section_t *section_new(const char *name); static void section_free(void *ptr); @@ -94,8 +95,12 @@ config_t *config_new(const char *filename) return NULL; } - config_parse(fp, config); + bool ok = config_parse(fp, config); nvs_close(fp); + if (!ok) { + config_free(config); + return NULL; + } return config; } @@ -136,7 +141,7 @@ bool config_has_key_in_section(config_t *config, const char *key, char *key_valu entry_t *entry = list_node(node); OSI_TRACE_DEBUG("entry->key = %s, entry->value = %s", entry->key, entry->value); if (!strcmp(entry->key, key) && !strcmp(entry->value, key_value)) { - OSI_TRACE_DEBUG("%s, the irk aready in the flash.", __func__); + OSI_TRACE_DEBUG("%s, the irk already in the flash.", __func__); return true; } } @@ -221,6 +226,10 @@ void config_set_string(config_t *config, const char *section, const char *key, c section_t *sec = section_find(config, section); if (!sec) { sec = section_new(section); + if (sec == NULL) { + OSI_TRACE_ERROR("%s unable to allocate section\n", __func__); + return; + } if (insert_back) { list_append(config->sections, sec); } else { @@ -231,13 +240,22 @@ void config_set_string(config_t *config, const char *section, const char *key, c for (const list_node_t *node = list_begin(sec->entries); node != list_end(sec->entries); node = list_next(node)) { entry_t *entry = list_node(node); if (!strcmp(entry->key, key)) { + char *new_value = osi_strdup(value); + if (new_value == NULL) { + OSI_TRACE_ERROR("%s unable to allocate entry value\n", __func__); + return; + } osi_free(entry->value); - entry->value = osi_strdup(value); + entry->value = new_value; return; } } entry_t *entry = entry_new(key, value); + if (entry == NULL) { + OSI_TRACE_ERROR("%s unable to allocate entry\n", __func__); + return; + } list_append(sec->entries, entry); } @@ -327,32 +345,57 @@ const char *config_section_name(const config_section_node_t *node) return section->name; } -static int get_config_size(const config_t *config) +static bool get_config_size(const config_t *config, size_t *out_size) { assert(config != NULL); + assert(out_size != NULL); - int w_len = 0, total_size = 0; + size_t total_size = 0; for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) { const section_t *section = (const section_t *)list_node(node); - w_len = strlen(section->name) + strlen("[]\n");// format "[section->name]\n" + /* CONTRACT / PRECONDITION: + * - section->name is guaranteed non-NULL. + * - Upstream guard: section_new() stores osi_strdup(name) from config_set_string(). + */ + ESP_ASSUME_NONNULL(section->name); + size_t w_len = strlen(section->name) + strlen("[]\n"); // format "[section->name]\n" + if (total_size > SIZE_MAX - w_len) { + return false; + } total_size += w_len; for (const list_node_t *enode = list_begin(section->entries); enode != list_end(section->entries); enode = list_next(enode)) { const entry_t *entry = (const entry_t *)list_node(enode); - w_len = strlen(entry->key) + strlen(entry->value) + strlen(" = \n");// format "entry->key = entry->value\n" + /* CONTRACT / PRECONDITION: + * - entry->key/value are guaranteed non-NULL. + * - Upstream guard: entry_new() stores osi_strdup(key/value) from config_set_string(). + */ + ESP_ASSUME_NONNULL(entry->key); + ESP_ASSUME_NONNULL(entry->value); + w_len = strlen(entry->key) + strlen(entry->value) + strlen(" = \n"); // format "entry->key = entry->value\n" + if (total_size > SIZE_MAX - w_len) { + return false; + } total_size += w_len; } // Only add a separating newline if there are more sections. if (list_next(node) != list_end(config->sections)) { - total_size ++; //'\n' + if (total_size == SIZE_MAX) { + return false; + } + total_size++; // '\n' } else { break; } } - total_size ++; //'\0' - return total_size; + if (total_size == SIZE_MAX) { + return false; + } + total_size++; // '\0' + *out_size = total_size; + return true; } static int get_config_size_from_flash(nvs_handle_t fp) @@ -364,7 +407,7 @@ static int get_config_size_from_flash(nvs_handle_t fp) char *keyname = osi_calloc(keyname_bufsz); if (!keyname){ OSI_TRACE_ERROR("%s, malloc error\n", __func__); - return 0; + return -1; } size_t length = CONFIG_FILE_DEFAULE_LENGTH; size_t total_length = 0; @@ -378,7 +421,7 @@ static int get_config_size_from_flash(nvs_handle_t fp) if (err != ESP_OK) { OSI_TRACE_ERROR("%s, error %d\n", __func__, err); osi_free(keyname); - return 0; + return -2; } total_length += length; while (length == CONFIG_FILE_MAX_SIZE) { @@ -392,7 +435,7 @@ static int get_config_size_from_flash(nvs_handle_t fp) if (err != ESP_OK) { OSI_TRACE_ERROR("%s, error %d\n", __func__, err); osi_free(keyname); - return 0; + return -3; } total_length += length; } @@ -409,12 +452,21 @@ bool config_save(const config_t *config, const char *filename) esp_err_t err; int err_code = 0; nvs_handle_t fp; - char *line = osi_calloc(1024); + char *buf = NULL; + bool nvs_opened = false; const size_t keyname_bufsz = sizeof(CONFIG_KEY) + 5 + 1; // including log10(sizeof(i)) char *keyname = osi_calloc(keyname_bufsz); - int config_size = get_config_size(config); - char *buf = osi_calloc(config_size); - if (!line || !buf || !keyname) { + if (!keyname) { + err_code |= 0x01; + goto error; + } + size_t config_size = 0; + if (!get_config_size(config, &config_size) || config_size == 0) { + err_code |= 0x01; + goto error; + } + buf = osi_calloc(config_size); + if (!buf) { err_code |= 0x01; goto error; } @@ -428,63 +480,67 @@ bool config_save(const config_t *config, const char *filename) err_code |= 0x02; goto error; } + nvs_opened = true; - int w_cnt, w_cnt_total = 0; + int w_cnt; + size_t w_cnt_total = 0; for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) { const section_t *section = (const section_t *)list_node(node); - w_cnt = snprintf(line, 1024, "[%s]\n", section->name); - if(w_cnt < 0) { - OSI_TRACE_ERROR("snprintf error w_cnt %d.",w_cnt); - err_code |= 0x10; - goto error; - } - if(w_cnt_total + w_cnt > config_size) { - OSI_TRACE_ERROR("%s, memcpy size (w_cnt + w_cnt_total = %d) is larger than buffer size (config_size = %d).", __func__, (w_cnt + w_cnt_total), config_size); + size_t remaining = (w_cnt_total < config_size) ? (config_size - w_cnt_total) : 0; + if (remaining == 0) { err_code |= 0x20; goto error; } - OSI_TRACE_DEBUG("section name: %s, w_cnt + w_cnt_total = %d\n", section->name, w_cnt + w_cnt_total); - memcpy(buf + w_cnt_total, line, w_cnt); - w_cnt_total += w_cnt; + w_cnt = snprintf(buf + w_cnt_total, remaining, "[%s]\n", section->name); + if (w_cnt < 0 || (size_t)w_cnt >= remaining) { + err_code |= 0x20; + goto error; + } + OSI_TRACE_DEBUG("section name: %s, w_cnt + w_cnt_total = %d\n", section->name, (int)(w_cnt + w_cnt_total)); + w_cnt_total += (size_t)w_cnt; for (const list_node_t *enode = list_begin(section->entries); enode != list_end(section->entries); enode = list_next(enode)) { const entry_t *entry = (const entry_t *)list_node(enode); OSI_TRACE_DEBUG("(key, val): (%s, %s)\n", entry->key, entry->value); - w_cnt = snprintf(line, 1024, "%s = %s\n", entry->key, entry->value); - if(w_cnt < 0) { - OSI_TRACE_ERROR("snprintf error w_cnt %d.",w_cnt); - err_code |= 0x10; - goto error; - } - if(w_cnt_total + w_cnt > config_size) { - OSI_TRACE_ERROR("%s, memcpy size (w_cnt + w_cnt_total = %d) is larger than buffer size.(config_size = %d)", __func__, (w_cnt + w_cnt_total), config_size); + remaining = (w_cnt_total < config_size) ? (config_size - w_cnt_total) : 0; + if (remaining == 0) { err_code |= 0x20; goto error; } - OSI_TRACE_DEBUG("%s, w_cnt + w_cnt_total = %d", __func__, w_cnt + w_cnt_total); - memcpy(buf + w_cnt_total, line, w_cnt); - w_cnt_total += w_cnt; + w_cnt = snprintf(buf + w_cnt_total, remaining, "%s = %s\n", entry->key, entry->value); + if (w_cnt < 0 || (size_t)w_cnt >= remaining) { + err_code |= 0x20; + goto error; + } + OSI_TRACE_DEBUG("%s, w_cnt + w_cnt_total = %d", __func__, (int)(w_cnt + w_cnt_total)); + w_cnt_total += (size_t)w_cnt; } // Only add a separating newline if there are more sections. if (list_next(node) != list_end(config->sections)) { - buf[w_cnt_total] = '\n'; - w_cnt_total += 1; + if (w_cnt_total + 1 >= config_size) { + err_code |= 0x20; + goto error; + } + buf[w_cnt_total++] = '\n'; } else { break; } } + if (w_cnt_total >= config_size) { + err_code |= 0x20; + goto error; + } buf[w_cnt_total] = '\0'; if (w_cnt_total < CONFIG_FILE_MAX_SIZE) { snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0); err = nvs_set_blob(fp, keyname, buf, w_cnt_total); if (err != ESP_OK) { - nvs_close(fp); err_code |= 0x04; goto error; } }else { - int count = (w_cnt_total / CONFIG_FILE_MAX_SIZE); + int count = (int)(w_cnt_total / CONFIG_FILE_MAX_SIZE); assert(count <= 0xFF); for (uint8_t i = 0; i <= count; i++) { @@ -497,7 +553,6 @@ bool config_save(const config_t *config, const char *filename) OSI_TRACE_DEBUG("save keyname = %s, i = %d, %d\n", keyname, i, CONFIG_FILE_MAX_SIZE); } if (err != ESP_OK) { - nvs_close(fp); err_code |= 0x04; goto error; } @@ -506,24 +561,23 @@ bool config_save(const config_t *config, const char *filename) err = nvs_commit(fp); if (err != ESP_OK) { - nvs_close(fp); err_code |= 0x08; goto error; } nvs_close(fp); - osi_free(line); + nvs_opened = false; osi_free(buf); osi_free(keyname); return true; error: + if (nvs_opened) { + nvs_close(fp); + } if (buf) { osi_free(buf); } - if (line) { - osi_free(line); - } if (keyname) { osi_free(keyname); } @@ -552,7 +606,7 @@ static char *trim(char *str) return str; } -static void config_parse(nvs_handle_t fp, config_t *config) +static bool config_parse(nvs_handle_t fp, config_t *config) { assert(fp != 0); assert(config != NULL); @@ -570,9 +624,14 @@ static void config_parse(nvs_handle_t fp, config_t *config) int buf_size = get_config_size_from_flash(fp); char *buf = NULL; - if(buf_size == 0) { //First use nvs + if (buf_size < 0) { + err_code |= 0x04; goto error; } + + if(buf_size == 0) { //First use nvs + goto ok; + } buf = osi_calloc(buf_size); if (!line || !section || !buf || !keyname) { err_code |= 0x01; @@ -581,7 +640,7 @@ static void config_parse(nvs_handle_t fp, config_t *config) snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0); err = nvs_get_blob(fp, keyname, buf, &length); if (err == ESP_ERR_NVS_NOT_FOUND) { - goto error; + goto ok; } if (err != ESP_OK) { err_code |= 0x02; @@ -644,6 +703,21 @@ static void config_parse(nvs_handle_t fp, config_t *config) } } +ok: + if (buf) { + osi_free(buf); + } + if (line) { + osi_free(line); + } + if (section) { + osi_free(section); + } + if (keyname) { + osi_free(keyname); + } + return true; + error: if (buf) { osi_free(buf); @@ -660,6 +734,7 @@ error: if (err_code) { OSI_TRACE_ERROR("%s returned with err code: %d\n", __func__, err_code); } + return false; } static section_t *section_new(const char *name) @@ -671,6 +746,10 @@ static section_t *section_new(const char *name) section->name = osi_strdup(name); section->entries = list_new(entry_free); + if (section->name == NULL || section->entries == NULL) { + section_free(section); + return NULL; + } return section; } @@ -707,6 +786,10 @@ static entry_t *entry_new(const char *key, const char *value) entry->key = osi_strdup(key); entry->value = osi_strdup(value); + if (entry->key == NULL || entry->value == NULL) { + entry_free(entry); + return NULL; + } return entry; } diff --git a/components/bt/common/osi/fixed_queue.c b/components/bt/common/osi/fixed_queue.c index 0f1ba9b7c22..ae7ca135047 100644 --- a/components/bt/common/osi/fixed_queue.c +++ b/components/bt/common/osi/fixed_queue.c @@ -22,6 +22,7 @@ #include "osi/osi.h" #include "osi/mutex.h" #include "osi/semaphore.h" +#include "esp_assume.h" typedef struct fixed_queue_t { @@ -43,6 +44,12 @@ fixed_queue_t *fixed_queue_new(size_t capacity) } osi_mutex_new(&ret->lock); + /* CONTRACT / PRECONDITION: + * - ret->lock is guaranteed non-NULL after successful osi_mutex_new(). + * - Rationale: treat mutex allocation failure as fatal in debug builds; + * release builds do not add extra code size. + */ + ESP_ASSUME_NONNULL(ret->lock); ret->capacity = capacity; ret->list = list_new(NULL); diff --git a/components/bt/common/osi/hash_map.c b/components/bt/common/osi/hash_map.c index 59942bfd41b..df73eff7eb3 100644 --- a/components/bt/common/osi/hash_map.c +++ b/components/bt/common/osi/hash_map.c @@ -138,26 +138,37 @@ bool hash_map_set(hash_map_t *hash_map, const void *key, void *data) } list_t *hash_bucket_list = hash_map->bucket[hash_key].list; - hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key); + /* Allocate the new entry FIRST, before touching any existing entry. + * This preserves the atomicity of hash_map_set: on failure the map + * stays exactly as it was, and the caller's previously-stored + * key/data remain valid. */ + hash_map_entry_t *new_entry = osi_calloc(sizeof(hash_map_entry_t)); + if (new_entry == NULL) { + return false; + } + new_entry->key = key; + new_entry->data = data; + new_entry->hash_map = hash_map; - if (hash_map_entry) { - // Calls hash_map callback to delete the hash_map_entry. - bool rc = list_remove(hash_bucket_list, hash_map_entry); + hash_map_entry_t *old_entry = find_bucket_entry_(hash_bucket_list, key); + + if (!list_append(hash_bucket_list, new_entry)) { + osi_free(new_entry); + return false; + } + + /* Only after the new entry has been successfully inserted do we + * destroy the old one (which frees the previous key/data via + * bucket_free_). For a brand-new key, bump hash_size instead. */ + if (old_entry) { + bool rc = list_remove(hash_bucket_list, old_entry); assert(rc == true); (void)rc; } else { hash_map->hash_size++; } - hash_map_entry = osi_calloc(sizeof(hash_map_entry_t)); - if (hash_map_entry == NULL) { - return false; - } - hash_map_entry->key = key; - hash_map_entry->data = data; - hash_map_entry->hash_map = hash_map; - - return list_append(hash_bucket_list, hash_map_entry); + return true; } bool hash_map_erase(hash_map_t *hash_map, const void *key) diff --git a/components/bt/common/osi/list.c b/components/bt/common/osi/list.c index c951d2126fc..f7ffc32d898 100644 --- a/components/bt/common/osi/list.c +++ b/components/bt/common/osi/list.c @@ -181,8 +181,9 @@ bool list_remove(list_t *list, void *data) } if (list->head->data == data) { + const bool was_tail = (list->tail == list->head); list_node_t *next = list_free_node(list, list->head); - if (list->tail == list->head) { + if (was_tail) { list->tail = next; } list->head = next; @@ -191,8 +192,9 @@ bool list_remove(list_t *list, void *data) for (list_node_t *prev = list->head, *node = list->head->next; node; prev = node, node = node->next) if (node->data == data) { + const bool was_tail = (list->tail == node); prev->next = list_free_node(list, node); - if (list->tail == node) { + if (was_tail) { list->tail = prev; } return true; @@ -211,8 +213,9 @@ bool list_delete(list_t *list, void *data) } if (list->head->data == data) { + const bool was_tail = (list->tail == list->head); list_node_t *next = list_delete_node(list, list->head); - if (list->tail == list->head) { + if (was_tail) { list->tail = next; } list->head = next; @@ -221,8 +224,9 @@ bool list_delete(list_t *list, void *data) for (list_node_t *prev = list->head, *node = list->head->next; node; prev = node, node = node->next) if (node->data == data) { + const bool was_tail = (list->tail == node); prev->next = list_delete_node(list, node); - if (list->tail == node) { + if (was_tail) { list->tail = prev; } return true; diff --git a/components/bt/common/osi/mutex.c b/components/bt/common/osi/mutex.c index 5a4de81ee5b..eaa2b8a6dca 100644 --- a/components/bt/common/osi/mutex.c +++ b/components/bt/common/osi/mutex.c @@ -16,6 +16,8 @@ * ******************************************************************************/ +#include + #include "osi/mutex.h" @@ -90,15 +92,21 @@ int osi_mutex_global_init(void) void osi_mutex_global_deinit(void) { + if (gl_mutex == NULL) { + return; + } vSemaphoreDelete(gl_mutex); + gl_mutex = NULL; } void osi_mutex_global_lock(void) { + assert(gl_mutex != NULL); xSemaphoreTakeRecursive(gl_mutex, portMAX_DELAY); } void osi_mutex_global_unlock(void) { + assert(gl_mutex != NULL); xSemaphoreGiveRecursive(gl_mutex); } diff --git a/components/bt/host/bluedroid/api/esp_bluedroid_hci.c b/components/bt/host/bluedroid/api/esp_bluedroid_hci.c index 4ddc169a97c..5476f43409d 100644 --- a/components/bt/host/bluedroid/api/esp_bluedroid_hci.c +++ b/components/bt/host/bluedroid/api/esp_bluedroid_hci.c @@ -68,7 +68,9 @@ bool hci_host_check_send_available(void) void hci_host_send_packet(uint8_t *data, uint16_t len) { #if (BT_HCI_LOG_INCLUDED == TRUE) - bt_hci_log_record_hci_data(data[0], &data[1], len - 1); + if (data != NULL && len > 1) { + bt_hci_log_record_hci_data(data[0], &data[1], (uint16_t)(len - 1)); + } #endif #if CONFIG_BT_BLE_LOG_SPI_OUT_HCI_ENABLED ble_log_spi_out_hci_write(BLE_LOG_SPI_OUT_SOURCE_HCI_DOWNSTREAM, data, len); diff --git a/components/bt/host/bluedroid/api/esp_bt_main.c b/components/bt/host/bluedroid/api/esp_bt_main.c index 55cd9e3caee..10057d9b223 100644 --- a/components/bt/host/bluedroid/api/esp_bt_main.c +++ b/components/bt/host/bluedroid/api/esp_bt_main.c @@ -224,7 +224,9 @@ esp_err_t esp_bluedroid_init_with_cfg(esp_bluedroid_config_t *cfg) } #if (BT_HCI_LOG_INCLUDED == TRUE) - bt_hci_log_init(); + if (bt_hci_log_init() != ESP_OK) { + LOG_WARN("HCI log facility unavailable, continuing without HCI log\n"); + } #endif // (BT_HCI_LOG_INCLUDED == TRUE) s_bt_host_state = ESP_BLUEDROID_STATUS_INITIALIZED; diff --git a/components/bt/host/bluedroid/btc/core/btc_config.c b/components/bt/host/bluedroid/btc/core/btc_config.c index c7edd3f6691..c55b80023d5 100644 --- a/components/bt/host/bluedroid/btc/core/btc_config.c +++ b/components/bt/host/bluedroid/btc/core/btc_config.c @@ -92,7 +92,7 @@ bool btc_config_init(void) config = config_new(CONFIG_FILE_PATH); if (!config) { - BTC_TRACE_WARNING("%s unable to load config file; starting unconfigured.\n", __func__); + BTC_TRACE_WARNING("%s unable to load/parse config; starting unconfigured without overwriting NVS.\n", __func__); config = config_new_empty(); if (!config) { BTC_TRACE_ERROR("%s unable to allocate a config object.\n", __func__); @@ -345,14 +345,17 @@ int btc_config_clear(void) { assert(config != NULL); - config_free(config); + btc_config_lock(); + config_free(config); config = config_new_empty(); if (config == NULL) { - return false; + btc_config_unlock(); + return -1; } - int ret = config_save(config, CONFIG_FILE_PATH); - return ret; + bool ret = config_save(config, CONFIG_FILE_PATH); + btc_config_unlock(); + return ret ? 0 : -1; } void btc_config_lock(void) diff --git a/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c b/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c index 76ce330e529..6a9ff0c0cef 100644 --- a/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c +++ b/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c @@ -593,7 +593,7 @@ static void btc_gap_ble_adv_pkt_handler(void *arg) } } - if (pkt_queue_length(p_env->adv_rpt_queue) != 0) { + if (p_env->adv_rpt_ready && pkt_queue_length(p_env->adv_rpt_queue) != 0) { osi_thread_post_event(p_env->adv_rpt_ready, OSI_THREAD_MAX_TIMEOUT); } } @@ -628,7 +628,9 @@ static void btc_process_adv_rpt_pkt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_ } while (0); pkt_queue_enqueue(p_env->adv_rpt_queue, linked_pkt); - osi_thread_post_event(p_env->adv_rpt_ready, OSI_THREAD_MAX_TIMEOUT); + if (p_env->adv_rpt_ready) { + osi_thread_post_event(p_env->adv_rpt_ready, OSI_THREAD_MAX_TIMEOUT); + } } static void btc_search_callback(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data) @@ -3412,15 +3414,34 @@ void btc_gap_ble_deinit(void) #if (BLE_42_SCAN_EN == TRUE) btc_gap_ble_env_t *p_env = &btc_gap_ble_env; - osi_event_delete(p_env->adv_rpt_ready); + struct osi_event *adv_evt = p_env->adv_rpt_ready; p_env->adv_rpt_ready = NULL; + if (adv_evt) { + osi_event_delete(adv_evt); + } - pkt_queue_destroy(p_env->adv_rpt_queue, NULL); - p_env->adv_rpt_queue = NULL; + if (p_env->adv_rpt_queue) { + pkt_queue_destroy(p_env->adv_rpt_queue, NULL); + p_env->adv_rpt_queue = NULL; + } #endif // #if (BLE_42_SCAN_EN == TRUE) #if (BLE_42_ADV_EN == TRUE) + /* Under BTC_DYNAMIC_MEMORY, gl_bta_adv_data is a macro that dereferences + * gl_bta_adv_data_ptr. Guard against the case where the pointer is NULL + * (e.g. btc_init_mem() failed midway, or btc_deinit() is invoked twice) + * to avoid &(*NULL) UB before reaching btc_cleanup_adv_data()'s NULL + * check. */ +#if (BTC_DYNAMIC_MEMORY == TRUE) + if (gl_bta_adv_data_ptr) { + btc_cleanup_adv_data(&gl_bta_adv_data); + } + if (gl_bta_scan_rsp_data_ptr) { + btc_cleanup_adv_data(&gl_bta_scan_rsp_data); + } +#else btc_cleanup_adv_data(&gl_bta_adv_data); btc_cleanup_adv_data(&gl_bta_scan_rsp_data); +#endif // #if (BTC_DYNAMIC_MEMORY == TRUE) #endif // #if (BLE_42_ADV_EN == TRUE) #endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) } diff --git a/components/bt/host/bluedroid/stack/btu/btu_task.c b/components/bt/host/bluedroid/stack/btu/btu_task.c index 2fe59a92601..27d3ca79fa5 100644 --- a/components/bt/host/bluedroid/stack/btu/btu_task.c +++ b/components/bt/host/bluedroid/stack/btu/btu_task.c @@ -461,11 +461,10 @@ void btu_start_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_sec) return; } } - osi_mutex_unlock(&btu_general_alarm_lock); - alarm = hash_map_get(btu_general_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_ERROR("%s Unable to create alarm", __func__); + osi_mutex_unlock(&btu_general_alarm_lock); return; } osi_alarm_cancel(alarm); @@ -475,6 +474,7 @@ void btu_start_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_sec) p_tle->ticks = timeout_sec; p_tle->in_use = TRUE; osi_alarm_set(alarm, (period_ms_t)((period_ms_t)timeout_sec * 1000)); + osi_mutex_unlock(&btu_general_alarm_lock); } @@ -491,18 +491,21 @@ void btu_stop_timer(TIMER_LIST_ENT *p_tle) { assert(p_tle != NULL); + osi_mutex_lock(&btu_general_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); if (p_tle->in_use == FALSE) { + osi_mutex_unlock(&btu_general_alarm_lock); return; } - p_tle->in_use = FALSE; - - // Get the alarm for the timer list entry. osi_alarm_t *alarm = hash_map_get(btu_general_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_WARNING("%s Unable to find expected alarm in hashmap", __func__); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_general_alarm_lock); return; } osi_alarm_cancel(alarm); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_general_alarm_lock); } /******************************************************************************* @@ -518,16 +521,18 @@ void btu_free_timer(TIMER_LIST_ENT *p_tle) { assert(p_tle != NULL); - p_tle->in_use = FALSE; - - // Get the alarm for the timer list entry. + osi_mutex_lock(&btu_general_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); osi_alarm_t *alarm = hash_map_get(btu_general_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_DEBUG("%s Unable to find expected alarm in hashmap", __func__); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_general_alarm_lock); return; } osi_alarm_cancel(alarm); hash_map_erase(btu_general_alarm_hash_map, p_tle); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_general_alarm_lock); } #if defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) @@ -586,11 +591,10 @@ void btu_start_quick_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_ti return; } } - osi_mutex_unlock(&btu_l2cap_alarm_lock); - alarm = hash_map_get(btu_l2cap_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_ERROR("%s Unable to create alarm", __func__); + osi_mutex_unlock(&btu_l2cap_alarm_lock); return; } osi_alarm_cancel(alarm); @@ -600,6 +604,7 @@ void btu_start_quick_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_ti p_tle->in_use = TRUE; // The quick timer ticks are 100ms long. osi_alarm_set(alarm, (period_ms_t)(timeout_ticks * 100)); + osi_mutex_unlock(&btu_l2cap_alarm_lock); } /******************************************************************************* @@ -615,34 +620,39 @@ void btu_stop_quick_timer(TIMER_LIST_ENT *p_tle) { assert(p_tle != NULL); + osi_mutex_lock(&btu_l2cap_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); if (p_tle->in_use == FALSE) { + osi_mutex_unlock(&btu_l2cap_alarm_lock); return; } - p_tle->in_use = FALSE; - - // Get the alarm for the timer list entry. osi_alarm_t *alarm = hash_map_get(btu_l2cap_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_WARNING("%s Unable to find expected alarm in hashmap", __func__); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_l2cap_alarm_lock); return; } osi_alarm_cancel(alarm); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_l2cap_alarm_lock); } void btu_free_quick_timer(TIMER_LIST_ENT *p_tle) { assert(p_tle != NULL); - p_tle->in_use = FALSE; - - // Get the alarm for the timer list entry. + osi_mutex_lock(&btu_l2cap_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); osi_alarm_t *alarm = hash_map_get(btu_l2cap_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_DEBUG("%s Unable to find expected alarm in hashmap", __func__); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_l2cap_alarm_lock); return; } osi_alarm_cancel(alarm); hash_map_erase(btu_l2cap_alarm_hash_map, p_tle); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_l2cap_alarm_lock); } #endif /* defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) */ @@ -682,11 +692,10 @@ void btu_start_timer_oneshot(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_ return; } } - osi_mutex_unlock(&btu_oneshot_alarm_lock); - alarm = hash_map_get(btu_oneshot_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_ERROR("%s Unable to create alarm", __func__); + osi_mutex_unlock(&btu_oneshot_alarm_lock); return; } osi_alarm_cancel(alarm); @@ -696,24 +705,28 @@ void btu_start_timer_oneshot(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_ // NOTE: This value is in seconds but stored in a ticks field. p_tle->ticks = timeout_sec; osi_alarm_set(alarm, (period_ms_t)(timeout_sec * 1000)); + osi_mutex_unlock(&btu_oneshot_alarm_lock); } void btu_stop_timer_oneshot(TIMER_LIST_ENT *p_tle) { assert(p_tle != NULL); + osi_mutex_lock(&btu_oneshot_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); if (p_tle->in_use == FALSE) { + osi_mutex_unlock(&btu_oneshot_alarm_lock); return; } - p_tle->in_use = FALSE; - - // Get the alarm for the timer list entry. osi_alarm_t *alarm = hash_map_get(btu_oneshot_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_WARNING("%s Unable to find expected alarm in hashmap", __func__); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_oneshot_alarm_lock); return; } osi_alarm_cancel(alarm); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_oneshot_alarm_lock); } #if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE)