Merge branch 'bugfix/fix_ble_security_issue_2025_v5.5' into 'release/v5.5'

Fix potential CVE-2024-0039 out-of-bounds write in attp_build_value_cmd (v5.5)

See merge request espressif/esp-idf!43807
This commit is contained in:
Island
2025-12-18 14:25:15 +08:00
30 changed files with 482 additions and 129 deletions
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -126,14 +126,24 @@ void esp_receive_apple_data_source(uint8_t *message, uint16_t message_len)
switch (Command_id)
{
case CommandIDGetNotificationAttributes: {
// Security fix: Check minimum message length before accessing message[1..4]
if (message_len < 5) {
ESP_LOGE(BLE_ANCS_TAG, "Message too short for NotificationAttributes");
break;
}
uint32_t NotificationUID = (message[1]) | (message[2]<< 8) | (message[3]<< 16) | (message[4] << 24);
uint32_t remian_attr_len = message_len - 5;
uint8_t *attrs = &message[5];
ESP_LOGI(BLE_ANCS_TAG, "recevice Notification Attributes response Command_id %d NotificationUID %" PRIu32, Command_id, NotificationUID);
while(remian_attr_len > 0) {
// Security fix: Need at least 3 bytes for AttributeID(1) + len(2)
if (remian_attr_len < 3) {
ESP_LOGE(BLE_ANCS_TAG, "Incomplete attribute header");
break;
}
uint8_t AttributeID = attrs[0];
uint16_t len = attrs[1] | (attrs[2] << 8);
if(len > (remian_attr_len -3)) {
if(len > (remian_attr_len - 3)) {
ESP_LOGE(BLE_ANCS_TAG, "data error");
break;
}
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -31,6 +31,8 @@
#define ADV_CONFIG_FLAG (1 << 0)
#define SCAN_RSP_CONFIG_FLAG (1 << 1)
#define INVALID_HANDLE 0
#define ANCS_CMD_BUFFER_MAX_SIZE 600
static uint8_t adv_config_done = 0;
static bool get_service = false;
static esp_gattc_char_elem_t *char_elem_result = NULL;
@@ -168,15 +170,26 @@ esp_noti_attr_list_t p_attr[8] = {
void esp_get_notification_attributes(uint8_t *notificationUID, uint8_t num_attr, esp_noti_attr_list_t *p_attr)
{
uint8_t cmd[600] = {0};
uint8_t cmd[ANCS_CMD_BUFFER_MAX_SIZE] = {0};
uint32_t index = 0;
cmd[0] = CommandIDGetNotificationAttributes;
index ++;
memcpy(&cmd[index], notificationUID, ESP_NOTIFICATIONUID_LEN);
index += ESP_NOTIFICATIONUID_LEN;
while(num_attr > 0) {
// Security fix: Check buffer boundary before writing
if (index >= ANCS_CMD_BUFFER_MAX_SIZE) {
ESP_LOGE(BLE_ANCS_TAG, "Command buffer overflow in get_notification_attributes");
return;
}
cmd[index ++] = p_attr->noti_attribute_id;
if (p_attr->attribute_len > 0) {
// Need 2 more bytes for attribute_len
if ((index + 2) > ANCS_CMD_BUFFER_MAX_SIZE) {
ESP_LOGE(BLE_ANCS_TAG, "Command buffer overflow in get_notification_attributes");
return;
}
cmd[index ++] = p_attr->attribute_len;
cmd[index ++] = (p_attr->attribute_len << 8);
}
@@ -195,8 +208,15 @@ void esp_get_notification_attributes(uint8_t *notificationUID, uint8_t num_attr,
void esp_get_app_attributes(uint8_t *appidentifier, uint16_t appidentifier_len, uint8_t num_attr, uint8_t *p_app_attrs)
{
uint8_t buffer[600] = {0};
uint8_t buffer[ANCS_CMD_BUFFER_MAX_SIZE] = {0};
uint32_t index = 0;
// Security fix: Check buffer boundary before memcpy
if ((1 + appidentifier_len + num_attr) > ANCS_CMD_BUFFER_MAX_SIZE) {
ESP_LOGE(BLE_ANCS_TAG, "Buffer overflow in get_app_attributes");
return;
}
buffer[0] = CommandIDGetAppAttributes;
index ++;
memcpy(&buffer[index], appidentifier, appidentifier_len);
@@ -215,7 +235,7 @@ void esp_get_app_attributes(uint8_t *appidentifier, uint16_t appidentifier_len,
void esp_perform_notification_action(uint8_t *notificationUID, uint8_t ActionID)
{
uint8_t buffer[600] = {0};
uint8_t buffer[ANCS_CMD_BUFFER_MAX_SIZE] = {0};
uint32_t index = 0;
buffer[0] = CommandIDPerformNotificationAction;
index ++;
@@ -517,6 +537,12 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
esp_get_notification_attributes(notificationUID, sizeof(p_attr)/sizeof(esp_noti_attr_list_t), p_attr);
}
} else if (param->notify.handle == gl_profile_tab[PROFILE_A_APP_ID].data_source_handle) {
if ((data_buffer.len + param->notify.value_len) > sizeof(data_buffer.buffer)) {
ESP_LOGE(BLE_ANCS_TAG, "Data source buffer overflow detected, discarding data");
memset(data_buffer.buffer, 0, sizeof(data_buffer.buffer));
data_buffer.len = 0;
break;
}
memcpy(&data_buffer.buffer[data_buffer.len], param->notify.value, param->notify.value_len);
data_buffer.len += param->notify.value_len;
if (param->notify.value_len == (gl_profile_tab[PROFILE_A_APP_ID].MTU_size - 3)) {
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -21,6 +21,7 @@
#include "esp_eddystone_protocol.h"
#include "esp_eddystone_api.h"
#define EDDYSTONE_URL_BUF_SIZE 100
/* Declare static functions */
static esp_err_t esp_eddystone_uid_received(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res);
@@ -101,18 +102,33 @@ static esp_err_t esp_eddystone_uid_received(const uint8_t* buf, uint8_t len, esp
static char* esp_eddystone_resolve_url_scheme(const uint8_t *url_start, const uint8_t *url_end)
{
int pos = 0;
static char url_buf[100] = {0};
static char url_buf[EDDYSTONE_URL_BUF_SIZE] = {0};
const uint8_t *p = url_start;
int written;
pos += sprintf(&url_buf[pos], "%s", eddystone_url_prefix[*p++]);
// Security fix: Use snprintf instead of sprintf to prevent buffer overflow
written = snprintf(&url_buf[pos], EDDYSTONE_URL_BUF_SIZE - pos, "%s", eddystone_url_prefix[*p++]);
if (written < 0 || written >= (EDDYSTONE_URL_BUF_SIZE - pos)) {
url_buf[EDDYSTONE_URL_BUF_SIZE - 1] = '\0';
return url_buf;
}
pos += written;
for (; p <= url_end; p++) {
if (esp_eddystone_is_char_invalid((*p))) {
pos += sprintf(&url_buf[pos], "%s", eddystone_url_encoding[*p]);
} else {
pos += sprintf(&url_buf[pos], "%c", *p);
if (pos >= EDDYSTONE_URL_BUF_SIZE - 1) {
break;
}
if (esp_eddystone_is_char_invalid((*p))) {
written = snprintf(&url_buf[pos], EDDYSTONE_URL_BUF_SIZE - pos, "%s", eddystone_url_encoding[*p]);
} else {
written = snprintf(&url_buf[pos], EDDYSTONE_URL_BUF_SIZE - pos, "%c", *p);
}
if (written < 0 || written >= (EDDYSTONE_URL_BUF_SIZE - pos)) {
break;
}
pos += written;
}
url_buf[EDDYSTONE_URL_BUF_SIZE - 1] = '\0';
return url_buf;
}
@@ -277,20 +277,25 @@ static bool store_wr_buffer(esp_ble_gatts_cb_param_t *p_data)
ESP_LOGI(GATTS_TABLE_TAG, "malloc error %s %d", __func__, __LINE__);
return false;
}
temp_spp_recv_data_node_p1->len = p_data->write.len;
temp_spp_recv_data_node_p1->next_node = NULL;
temp_spp_recv_data_node_p1->node_buff = (uint8_t *)malloc(p_data->write.len);
if (temp_spp_recv_data_node_p1->node_buff == NULL) {
ESP_LOGI(GATTS_TABLE_TAG, "malloc error %s %d\n", __func__, __LINE__);
// Security fix: Free the node and return false to prevent memory leak
free(temp_spp_recv_data_node_p1);
temp_spp_recv_data_node_p1 = NULL;
return false;
}
memcpy(temp_spp_recv_data_node_p1->node_buff, p_data->write.value, p_data->write.len);
// Security fix: Link to list only after successful allocation
if(temp_spp_recv_data_node_p2 != NULL){
temp_spp_recv_data_node_p2->next_node = temp_spp_recv_data_node_p1;
}
temp_spp_recv_data_node_p1->len = p_data->write.len;
SppRecvDataBuff.buff_size += p_data->write.len;
temp_spp_recv_data_node_p1->next_node = NULL;
temp_spp_recv_data_node_p1->node_buff = (uint8_t *)malloc(p_data->write.len);
temp_spp_recv_data_node_p2 = temp_spp_recv_data_node_p1;
if (temp_spp_recv_data_node_p1->node_buff == NULL) {
ESP_LOGI(GATTS_TABLE_TAG, "malloc error %s %d\n", __func__, __LINE__);
temp_spp_recv_data_node_p1->len = 0;
} else {
memcpy(temp_spp_recv_data_node_p1->node_buff,p_data->write.value,p_data->write.len);
}
SppRecvDataBuff.buff_size += p_data->write.len;
if(SppRecvDataBuff.node_num == 0){
SppRecvDataBuff.first_node = temp_spp_recv_data_node_p1;
@@ -16,7 +16,6 @@
//#define SUPPORT_HEARTBEAT
//#define SPP_DEBUG_MODE
#define spp_sprintf(s,...) sprintf((char*)(s), ##__VA_ARGS__)
#define SPP_DATA_MAX_LEN (512)
#define SPP_CMD_MAX_LEN (20)
#define SPP_STATUS_MAX_LEN (20)
@@ -282,7 +282,8 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare
}
}
esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)malloc(sizeof(esp_gatt_rsp_t));
// Security fix: Use calloc to ensure memory is zero-initialized
esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)calloc(1, sizeof(esp_gatt_rsp_t));
if (gatt_rsp) {
gatt_rsp->attr_value.len = param->write.len;
gatt_rsp->attr_value.handle = param->write.handle;
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -131,8 +131,24 @@ static struct gatts_profile_inst gl_profile_tab[PROFILE_NUM] = {
};
static void ble_init_adv_data(const char *name)
{
int len = strlen(name);
uint8_t raw_adv_data[len+5];
if (name == NULL) {
ESP_LOGE(BT_BLE_COEX_TAG, "ble_init_adv_data: name is NULL");
return;
}
size_t len = strlen(name);
// ADV data max is 31 bytes; overhead is 5 bytes (Flags: 3, Name header: 2)
#define ADV_DATA_MAX_LEN 31
#define ADV_DATA_OVERHEAD 5
if (len > (ADV_DATA_MAX_LEN - ADV_DATA_OVERHEAD)) {
ESP_LOGW(BT_BLE_COEX_TAG, "ADV name too long (%d), truncating to %d", (int)len, ADV_DATA_MAX_LEN - ADV_DATA_OVERHEAD);
len = ADV_DATA_MAX_LEN - ADV_DATA_OVERHEAD;
}
uint8_t raw_adv_data[ADV_DATA_MAX_LEN];
size_t adv_data_len = len + ADV_DATA_OVERHEAD;
//flag
raw_adv_data[0] = 2;
raw_adv_data[1] = ESP_BT_EIR_TYPE_FLAGS;
@@ -140,16 +156,14 @@ static void ble_init_adv_data(const char *name)
//adv name
raw_adv_data[3] = len + 1;
raw_adv_data[4] = ESP_BLE_AD_TYPE_NAME_CMPL;
for (int i = 0;i < len;i++)
{
raw_adv_data[i+5] = *(name++);
}
memcpy(&raw_adv_data[5], name, len);
//The length of adv data must be less than 31 bytes
esp_err_t raw_adv_ret = esp_ble_gap_config_adv_data_raw(raw_adv_data, sizeof(raw_adv_data));
esp_err_t raw_adv_ret = esp_ble_gap_config_adv_data_raw(raw_adv_data, adv_data_len);
if (raw_adv_ret){
ESP_LOGE(BT_BLE_COEX_TAG, "config raw adv data failed, error code = 0x%x ", raw_adv_ret);
}
esp_err_t raw_scan_ret = esp_ble_gap_config_scan_rsp_data_raw(raw_adv_data, sizeof(raw_adv_data));
esp_err_t raw_scan_ret = esp_ble_gap_config_scan_rsp_data_raw(raw_adv_data, adv_data_len);
if (raw_scan_ret){
ESP_LOGE(BT_BLE_COEX_TAG, "config raw scan rsp data failed, error code = 0x%x", raw_scan_ret);
}
@@ -41,6 +41,7 @@
struct blufi_security {
#define DH_SELF_PUB_KEY_LEN 128
#define DH_PARAM_LEN_MAX 1024
uint8_t self_public_key[DH_SELF_PUB_KEY_LEN];
#define SHARE_KEY_LEN 128
uint8_t share_key[SHARE_KEY_LEN];
@@ -83,6 +84,13 @@ void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_da
switch (type) {
case SEC_TYPE_DH_PARAM_LEN:
blufi_sec->dh_param_len = ((data[1]<<8)|data[2]);
// Security fix: Limit DH param length to prevent DoS via large memory allocation
if (blufi_sec->dh_param_len == 0 || blufi_sec->dh_param_len > DH_PARAM_LEN_MAX) {
BLUFI_ERROR("%s, invalid dh param len %d\n", __func__, blufi_sec->dh_param_len);
blufi_sec->dh_param_len = 0;
btc_blufi_report_error(ESP_BLUFI_DH_PARAM_ERROR);
return;
}
if (blufi_sec->dh_param) {
free(blufi_sec->dh_param);
blufi_sec->dh_param = NULL;
@@ -278,6 +278,9 @@ static void handle_bt_device_result(struct disc_res_param *disc_res)
GAP_DBG_PRINTF(", %s: ", gap_bt_prop_type_names[prop->type]);
}
if (prop->type == ESP_BT_GAP_DEV_PROP_BDNAME) {
if (prop->val == NULL) {
continue;
}
name = (uint8_t *)prop->val;
name_len = strlen((const char *)name);
GAP_DBG_PRINTF("%s", (const char *)name);
@@ -164,7 +164,7 @@ void hid_demo_task(void *pvParameters)
printf("] srv 0x%03x, ", r->bt.cod.service);
print_uuid(&r->bt.uuid);
printf(", ");
if (strncmp(r->name, remote_device_name, strlen(remote_device_name)) == 0) {
if (r->name && strncmp(r->name, remote_device_name, strlen(remote_device_name)) == 0) {
break;
}
}
@@ -175,7 +175,7 @@ void hid_demo_task(void *pvParameters)
}
#if CONFIG_BT_HID_HOST_ENABLED
if (cr && strncmp(cr->name, remote_device_name, strlen(remote_device_name)) == 0) {
if (cr && cr->name && strncmp(cr->name, remote_device_name, strlen(remote_device_name)) == 0) {
esp_hidh_dev_open(cr->bda, cr->transport, cr->ble.addr_type);
}
#else