Merge branch 'bugfix/source_acp_miss_audio_cfg_evt_v5.2' into 'release/v5.2'

fix: enhanced packet length check in Bluedroid v5.2

See merge request espressif/esp-idf!45261
This commit is contained in:
Wang Meng Yang
2026-02-13 14:02:34 +08:00
9 changed files with 104 additions and 42 deletions
@@ -283,6 +283,11 @@ tBTA_JV_STATUS BTA_JvStartDiscovery(BD_ADDR bd_addr, UINT16 num_uuid,
tBTA_JV_API_START_DISCOVERY *p_msg;
APPL_TRACE_API( "BTA_JvStartDiscovery");
if ((num_uuid > BTA_JV_MAX_UUIDS) || ((num_uuid > 0) && (p_uuid_list == NULL))) {
APPL_TRACE_ERROR("invalid uuid list: num_uuid=%u", num_uuid);
return BTA_JV_FAILURE;
}
if ((p_msg = (tBTA_JV_API_START_DISCOVERY *)osi_malloc(sizeof(tBTA_JV_API_START_DISCOVERY))) != NULL) {
p_msg->hdr.event = BTA_JV_API_START_DISCOVERY_EVT;
bdcpy(p_msg->bd_addr, bd_addr);
@@ -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
*/
@@ -652,7 +652,11 @@ static void btc_spp_start_discovery(btc_spp_args_t *arg)
ret = ESP_SPP_NEED_INIT;
break;
}
BTA_JvStartDiscovery(arg->start_discovery.bd_addr, arg->start_discovery.num_uuid, arg->start_discovery.p_uuid_list, NULL);
tBTA_JV_STATUS status = BTA_JvStartDiscovery(arg->start_discovery.bd_addr, arg->start_discovery.num_uuid, arg->start_discovery.p_uuid_list, NULL);
if (status != BTA_JV_SUCCESS) {
BTC_TRACE_ERROR("%s SPP failed to start discovery\n", __func__);
ret = ESP_SPP_NO_RESOURCE;
}
} while (0);
if (ret != ESP_SPP_SUCCESS) {
@@ -481,6 +481,10 @@ static void hci_hal_h4_hdl_rx_packet(BT_HDR *packet)
packet->len--;
if (type == HCI_BLE_EVENT) {
#if (!CONFIG_BT_STACK_NO_LOG)
if (packet->len < 1) {
osi_free(packet);
return;
}
uint8_t len = 0;
STREAM_TO_UINT8(len, stream);
#endif
@@ -447,12 +447,26 @@ static bool filter_incoming_event(BT_HDR *packet)
uint8_t event_code;
command_opcode_t opcode;
if (packet == NULL) {
return true;
}
if (packet->len < HCI_EVENT_PREAMBLE_SIZE) {
HCI_TRACE_WARNING("dropping too short HCI event (len=%u)", packet->len);
osi_free(packet);
return true;
}
STREAM_TO_UINT8(event_code, stream);
STREAM_SKIP_UINT8(stream); // Skip the parameter total length field
HCI_TRACE_DEBUG("Receive packet event_code=0x%x\n", event_code);
if (event_code == HCI_COMMAND_COMPLETE_EVT) {
if (packet->len < HCI_EVENT_PREAMBLE_SIZE + HCI_CC_EVENT_MIN_PARAM_LEN) {
HCI_TRACE_WARNING("dropping too short Command Complete (len=%u)", packet->len);
osi_free(packet);
return true;
}
STREAM_TO_UINT8(hci_host_env.command_credits, stream);
STREAM_TO_UINT16(opcode, stream);
wait_entry = get_waiting_command(opcode);
@@ -481,6 +495,11 @@ static bool filter_incoming_event(BT_HDR *packet)
goto intercepted;
} else if (event_code == HCI_COMMAND_STATUS_EVT) {
uint8_t status;
if (packet->len < HCI_EVENT_PREAMBLE_SIZE + HCI_CS_EVENT_MIN_PARAM_LEN) {
HCI_TRACE_WARNING("dropping too short Command Status (len=%u)", packet->len);
osi_free(packet);
return true;
}
STREAM_TO_UINT8(status, stream);
STREAM_TO_UINT8(hci_host_env.command_credits, stream);
STREAM_TO_UINT16(opcode, stream);
@@ -27,5 +27,9 @@
#define HCI_SCO_PREAMBLE_SIZE 3
// 1 byte for event code, 1 byte for parameter length (Volume 2, Part E, 5.4.4)
#define HCI_EVENT_PREAMBLE_SIZE 2
// 1 byte for Num_HCI_Command_Packets, 2 bytes for Commnad_Opcode (Volume 4, Part E, 7.7.14)
#define HCI_CC_EVENT_MIN_PARAM_LEN 3
// 1 byte for status, 1 byte for Num_HCI_Command_Packets, 2 bytes for Commnad_Opcode (Volume 4, Part E, 7.7.15)
#define HCI_CS_EVENT_MIN_PARAM_LEN 4
#endif /* _HCI_INTERNALS_H_ */
@@ -37,6 +37,7 @@
#define START_PACKET_BOUNDARY 2
#define CONTINUATION_PACKET_BOUNDARY 1
#define L2CAP_HEADER_SIZE 4
#define L2CAP_LENGTH_SIZE 2
// TODO(zachoverflow): find good value for this
#define NUMBER_OF_BUCKETS 42
@@ -81,6 +82,11 @@ static void fragment_and_dispatch(BT_HDR *packet)
callbacks->fragmented(packet, true);
return;
}
if (packet->len < HCI_ACL_PREAMBLE_SIZE) {
HCI_TRACE_ERROR("ACL packet too short for preamble (len=%u)", packet->len);
callbacks->fragmented(packet, true);
return;
}
max_data_size =
SUB_EVENT(packet->event) == LOCAL_BR_EDR_CONTROLLER_ID ?
@@ -143,6 +149,11 @@ static void reassemble_and_dispatch(BT_HDR *packet)
uint16_t l2cap_length;
uint16_t acl_length __attribute__((unused));
if (packet->len < HCI_ACL_PREAMBLE_SIZE + L2CAP_LENGTH_SIZE) {
HCI_TRACE_ERROR("ACL packet too short (len=%u)\n", packet->len);
osi_free(packet);
return;
}
STREAM_TO_UINT16(handle, stream);
STREAM_TO_UINT16(acl_length, stream);
STREAM_TO_UINT16(l2cap_length, stream);
@@ -106,46 +106,54 @@ static tAVRC_STS avrc_pars_vendor_cmd(tAVRC_MSG_VENDOR *p_msg, tAVRC_COMMAND *p_
case AVRC_PDU_GET_CUR_PLAYER_APP_VALUE: /* 0x13 */
case AVRC_PDU_GET_PLAYER_APP_ATTR_TEXT: /* 0x15 */
BE_STREAM_TO_UINT8 (p_result->get_cur_app_val.num_attr, p);
if (len != (p_result->get_cur_app_val.num_attr + 1)) {
if (len < 1) {
status = AVRC_STS_INTERNAL_ERR;
break;
}
p_u8 = p_result->get_cur_app_val.attrs;
for (xx = 0, yy = 0; xx < p_result->get_cur_app_val.num_attr; xx++) {
/* only report the valid player app attributes */
if (AVRC_IsValidPlayerAttr(*p)) {
p_u8[yy++] = *p;
} else {
BE_STREAM_TO_UINT8 (p_result->get_cur_app_val.num_attr, p);
if (len != (p_result->get_cur_app_val.num_attr + 1)) {
status = AVRC_STS_INTERNAL_ERR;
break;
}
p_u8 = p_result->get_cur_app_val.attrs;
for (xx = 0, yy = 0; xx < p_result->get_cur_app_val.num_attr; xx++) {
/* only report the valid player app attributes */
if (AVRC_IsValidPlayerAttr(*p)) {
p_u8[yy++] = *p;
}
p++;
}
p_result->get_cur_app_val.num_attr = yy;
if (yy == 0) {
status = AVRC_STS_BAD_PARAM;
}
p++;
}
p_result->get_cur_app_val.num_attr = yy;
if (yy == 0) {
status = AVRC_STS_BAD_PARAM;
}
break;
case AVRC_PDU_SET_PLAYER_APP_VALUE: /* 0x14 */
BE_STREAM_TO_UINT8 (p_result->set_app_val.num_val, p);
size_needed = sizeof(tAVRC_APP_SETTING);
if (p_buf && (len == ((p_result->set_app_val.num_val << 1) + 1))) {
p_result->set_app_val.p_vals = (tAVRC_APP_SETTING *)p_buf;
p_app_set = p_result->set_app_val.p_vals;
for (xx = 0; ((xx < p_result->set_app_val.num_val) && (buf_len > size_needed)); xx++) {
p_app_set[xx].attr_id = *p++;
p_app_set[xx].attr_val = *p++;
if (!avrc_is_valid_player_attrib_value(p_app_set[xx].attr_id, p_app_set[xx].attr_val)) {
status = AVRC_STS_BAD_PARAM;
}
}
if (xx != p_result->set_app_val.num_val) {
AVRC_TRACE_ERROR("AVRC_PDU_SET_PLAYER_APP_VALUE not enough room:%d orig num_val:%d",
xx, p_result->set_app_val.num_val);
p_result->set_app_val.num_val = xx;
}
} else {
AVRC_TRACE_ERROR("AVRC_PDU_SET_PLAYER_APP_VALUE NULL decode buffer or bad len");
if (len < 1) {
status = AVRC_STS_INTERNAL_ERR;
} else {
BE_STREAM_TO_UINT8 (p_result->set_app_val.num_val, p);
size_needed = sizeof(tAVRC_APP_SETTING);
if (p_buf && (len == ((p_result->set_app_val.num_val << 1) + 1))) {
p_result->set_app_val.p_vals = (tAVRC_APP_SETTING *)p_buf;
p_app_set = p_result->set_app_val.p_vals;
for (xx = 0; ((xx < p_result->set_app_val.num_val) && (buf_len > size_needed)); xx++) {
p_app_set[xx].attr_id = *p++;
p_app_set[xx].attr_val = *p++;
if (!avrc_is_valid_player_attrib_value(p_app_set[xx].attr_id, p_app_set[xx].attr_val)) {
status = AVRC_STS_BAD_PARAM;
}
}
if (xx != p_result->set_app_val.num_val) {
AVRC_TRACE_ERROR("AVRC_PDU_SET_PLAYER_APP_VALUE not enough room:%d orig num_val:%d",
xx, p_result->set_app_val.num_val);
p_result->set_app_val.num_val = xx;
}
} else {
AVRC_TRACE_ERROR("AVRC_PDU_SET_PLAYER_APP_VALUE NULL decode buffer or bad len");
status = AVRC_STS_INTERNAL_ERR;
}
}
break;
@@ -118,6 +118,10 @@ void sdp_server_handle_client_req (tCONN_CB *p_ccb, BT_HDR *p_msg)
UINT8 pdu_id;
UINT16 trans_num, param_len;
if (p_msg->len < 5) {
SDP_TRACE_WARNING("SDP - short request received: len=%u\n", p_msg->len);
return;
}
/* Start inactivity timer */
btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT);
@@ -187,6 +191,11 @@ static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num,
return;
}
if (p_req + 2 > p_req_end) {
sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_MAX_RECORDS_LIST);
return;
}
/* Get the max replies we can send. Cap it at our max anyways. */
BE_STREAM_TO_UINT16 (max_replies, p_req);
@@ -194,7 +203,6 @@ static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num,
max_replies = SDP_MAX_RECORDS;
}
if ((!p_req) || (p_req > p_req_end)) {
sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_MAX_RECORDS_LIST);
return;
@@ -322,14 +330,13 @@ static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
BOOLEAN is_cont = FALSE;
UINT16 attr_len;
/* Extract the record handle */
BE_STREAM_TO_UINT32 (rec_handle, p_req);
if (p_req > p_req_end) {
if (p_req + 4 + 2 > p_req_end) {
sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE);
return;
}
/* Extract the record handle */
BE_STREAM_TO_UINT32 (rec_handle, p_req);
/* Get the max list length we can send. Cap it at MTU size minus overhead */
BE_STREAM_TO_UINT16 (max_list_len, p_req);
@@ -371,7 +378,7 @@ static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
return;
}
if (*p_req++ != SDP_CONTINUATION_LEN) {
if ((*p_req++ != SDP_CONTINUATION_LEN) || (p_req + 2 > p_req_end)) {
sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN);
return;
}
@@ -590,7 +590,7 @@ hci_driver_uart_dma_deinit(void)
ESP_ERROR_CHECK(uart_driver_delete(s_hci_driver_uart_dma_env.hci_uart_params->hci_uart_port));
hci_driver_uart_dma_memory_deinit();
if (!s_hci_driver_uart_dma_env.process_sem) {
if (s_hci_driver_uart_dma_env.process_sem) {
vSemaphoreDelete(s_hci_driver_uart_dma_env.process_sem);
}