fix(bt_stack): Fix some critical bugs in classic_bt stack

related: obex, smp, pbap, sdp, rfcomm, stack_dm

- Deinit function doesn't delete connection when OBEX_DYNAMIC_MEMORY is on
- Union tGOEPC_DATA sometimes is free by osi_free in some cases when it contains mtu_id
- Add correct free and return solution after fail
- Fix symbol mistake in mod calculation
- Fix pointer-related UAF problems and memory free problems
- Fix buffer overflows and out-of-bounds access
- Fix infinite loops triggered by integer overflow wraparound
- Fix double free
- Change layer_specific usage to avoid heap overflow
- Add some NULL check for pointers
- Fix sdp_db free function
- Fix state table mismatch
This commit is contained in:
hejiaxin
2026-07-23 15:46:38 +08:00
parent 4097b5e3ca
commit 54cd579d3b
27 changed files with 177 additions and 73 deletions
@@ -354,10 +354,6 @@ static void goepc_sm_state_opened_idle(tGOEPC_CCB *p_ccb, UINT8 event, tGOEPC_DA
break;
default:
GOEPC_TRACE_ERROR("goepc_sm_state_opened_idle unexpected event: 0x%x\n", event);
if (p_data->pkt != NULL) {
osi_free(p_data->pkt);
p_data->pkt = NULL;
}
break;
}
}
@@ -85,7 +85,7 @@ typedef union
typedef void (tOBEX_TL_CBACK)(tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg);
typedef struct {
void (*init)(tOBEX_TL_CBACK *callback);
UINT16 (*init)(tOBEX_TL_CBACK *callback);
void (*deinit)(void);
UINT16 (*connect)(tOBEX_TL_SVR_INFO *server);
void (*disconnect)(UINT16 tl_hdl);
@@ -67,12 +67,16 @@ UINT16 OBEX_Init(void)
memset(&obex_cb, 0, sizeof(tOBEX_CB));
obex_cb.tl_ops[OBEX_OVER_L2CAP] = obex_tl_l2cap_ops_get();
if (obex_cb.tl_ops[OBEX_OVER_L2CAP] && obex_cb.tl_ops[OBEX_OVER_L2CAP]->init) {
obex_cb.tl_ops[OBEX_OVER_L2CAP]->init(obex_tl_l2cap_callback);
if (obex_cb.tl_ops[OBEX_OVER_L2CAP]->init(obex_tl_l2cap_callback) == 0) {
return OBEX_NO_RESOURCES;
}
}
#if (RFCOMM_INCLUDED == TRUE)
obex_cb.tl_ops[OBEX_OVER_RFCOMM] = obex_tl_rfcomm_ops_get();
if (obex_cb.tl_ops[OBEX_OVER_RFCOMM] && obex_cb.tl_ops[OBEX_OVER_RFCOMM]->init) {
obex_cb.tl_ops[OBEX_OVER_RFCOMM]->init(obex_tl_rfcomm_callback);
if(obex_cb.tl_ops[OBEX_OVER_RFCOMM]->init(obex_tl_rfcomm_callback) == 0) {
return OBEX_NO_RESOURCES;
}
}
#endif
obex_cb.trace_level = BT_TRACE_LEVEL_ERROR;
@@ -89,16 +93,18 @@ UINT16 OBEX_Init(void)
*******************************************************************************/
void OBEX_Deinit(void)
{
if (obex_cb.tl_ops[OBEX_OVER_L2CAP] && obex_cb.tl_ops[OBEX_OVER_L2CAP]->deinit) {
obex_cb.tl_ops[OBEX_OVER_L2CAP]->deinit();
}
#if (RFCOMM_INCLUDED == TRUE)
if (obex_cb.tl_ops[OBEX_OVER_RFCOMM] && obex_cb.tl_ops[OBEX_OVER_RFCOMM]->deinit) {
obex_cb.tl_ops[OBEX_OVER_RFCOMM]->deinit();
}
#endif
#if (OBEX_DYNAMIC_MEMORY)
if (obex_cb_ptr) {
#endif
if (obex_cb.tl_ops[OBEX_OVER_L2CAP] && obex_cb.tl_ops[OBEX_OVER_L2CAP]->deinit) {
obex_cb.tl_ops[OBEX_OVER_L2CAP]->deinit();
}
#if (RFCOMM_INCLUDED == TRUE)
if (obex_cb.tl_ops[OBEX_OVER_RFCOMM] && obex_cb.tl_ops[OBEX_OVER_RFCOMM]->deinit) {
obex_cb.tl_ops[OBEX_OVER_RFCOMM]->deinit();
}
#endif
#if (OBEX_DYNAMIC_MEMORY)
osi_free(obex_cb_ptr);
obex_cb_ptr = NULL;
}
@@ -325,6 +331,9 @@ UINT16 OBEX_BuildRequest(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_
if (buff_size < OBEX_MIN_PACKET_SIZE || info == NULL || out_pkt == NULL) {
return OBEX_INVALID_PARAM;
}
if (UINT16_MAX - buff_size < sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET + OBEX_BT_HDR_RESERVE_LEN) {
return OBEX_NO_RESOURCES;
}
buff_size += sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET + OBEX_BT_HDR_RESERVE_LEN;
BT_HDR *p_buf = (BT_HDR *)osi_malloc(buff_size);
@@ -386,6 +395,9 @@ UINT16 OBEX_BuildResponse(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out
if (buff_size < OBEX_MIN_PACKET_SIZE || info == NULL || out_pkt == NULL) {
return OBEX_INVALID_PARAM;
}
if (0xFFFF - buff_size < sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET + OBEX_BT_HDR_RESERVE_LEN) {
return OBEX_NO_RESOURCES;
}
buff_size += sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET + OBEX_BT_HDR_RESERVE_LEN;
BT_HDR *p_buf= (BT_HDR *)osi_malloc(buff_size);
@@ -554,7 +554,7 @@ void obex_tl_l2cap_congestion_status_ind(UINT16 lcid, BOOLEAN is_congested)
** other APIs
**
*******************************************************************************/
void obex_tl_l2cap_init(tOBEX_TL_CBACK *callback)
UINT16 obex_tl_l2cap_init(tOBEX_TL_CBACK *callback)
{
assert(callback != NULL);
#if (OBEX_DYNAMIC_MEMORY)
@@ -562,7 +562,7 @@ void obex_tl_l2cap_init(tOBEX_TL_CBACK *callback)
obex_tl_l2cap_cb_ptr = (tOBEX_TL_L2CAP_CB *)osi_malloc(sizeof(tOBEX_TL_L2CAP_CB));
if (!obex_tl_l2cap_cb_ptr) {
OBEX_TL_L2CAP_TRACE_ERROR("OBEX over L2CAP transport layer initialize failed, no memory\n");
assert(0);
return OBEX_TL_FAILED;
}
}
#endif /* #if (OBEX_DYNAMIC_MEMORY) */
@@ -583,6 +583,7 @@ void obex_tl_l2cap_init(tOBEX_TL_CBACK *callback)
p_reg_info->pL2CA_DataInd_Cb = obex_tl_l2cap_buf_data_ind;
p_reg_info->pL2CA_CongestionStatus_Cb = obex_tl_l2cap_congestion_status_ind;
p_reg_info->pL2CA_TxComplete_Cb = NULL;
return OBEX_TL_SUCCESS;
}
/*******************************************************************************
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -276,7 +276,7 @@ static void rfcomm_event_callback(UINT32 code, UINT16 rfc_handle)
}
}
void obex_tl_rfcomm_init(tOBEX_TL_CBACK *callback)
UINT16 obex_tl_rfcomm_init(tOBEX_TL_CBACK *callback)
{
assert(callback != NULL);
#if (OBEX_DYNAMIC_MEMORY)
@@ -284,19 +284,32 @@ void obex_tl_rfcomm_init(tOBEX_TL_CBACK *callback)
obex_tl_rfcomm_cb_ptr = (tOBEX_TL_RFCOMM_CB *)osi_malloc(sizeof(tOBEX_TL_RFCOMM_CB));
if (!obex_tl_rfcomm_cb_ptr) {
OBEX_TL_RFCOMM_TRACE_ERROR("OBEX over RFCOMM transport layer initialize failed, no memory\n");
assert(0);
return OBEX_TL_FAILED;
}
}
#endif /* #if (OBEX_DYNAMIC_MEMORY) */
memset(&obex_tl_rfcomm_cb, 0, sizeof(tOBEX_TL_RFCOMM_CB));
obex_tl_rfcomm_cb.callback = callback;
obex_tl_rfcomm_cb.trace_level = BT_TRACE_LEVEL_ERROR;
return OBEX_TL_SUCCESS;
}
void obex_tl_rfcomm_deinit(void)
{
#if (OBEX_DYNAMIC_MEMORY)
if (obex_tl_rfcomm_cb_ptr) {
#endif
for (size_t i = 0; i < OBEX_TL_RFCOMM_NUM_CONN; i++) {
if (obex_tl_rfcomm_cb.ccb[i].rfc_handle != 0) {
RFCOMM_RemoveConnection(obex_tl_rfcomm_cb.ccb[i].rfc_handle);
}
}
for (size_t i = 0; i < OBEX_TL_RFCOMM_NUM_SERVER; i++) {
if (obex_tl_rfcomm_cb.scb[i].rfc_handle != 0) {
RFCOMM_RemoveServer(obex_tl_rfcomm_cb.scb[i].rfc_handle);
}
}
#if (OBEX_DYNAMIC_MEMORY)
osi_free(obex_tl_rfcomm_cb_ptr);
obex_tl_rfcomm_cb_ptr = NULL;
}
@@ -357,8 +370,6 @@ UINT16 obex_tl_rfcomm_send(UINT16 handle, BT_HDR *p_buf)
if (PORT_Write(p_ccb->rfc_handle, p_buf) == PORT_SUCCESS) {
ret = OBEX_TL_SUCCESS;
} else {
osi_free(p_buf);
}
} while (0);
return ret;
@@ -1564,7 +1564,7 @@ int PORT_WriteDataCO (UINT16 handle, int *p_len, int len, UINT8 *p_data)
}
p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET;
p_buf->layer_specific = handle;
p_buf->layer_specific = length;
p_buf->len = length;
p_buf->event = BT_EVT_TO_BTU_SP_DATA;
@@ -1648,17 +1648,14 @@ int PORT_WriteData (UINT16 handle, char *p_data, UINT16 max_len, UINT16 *p_len)
return (PORT_UNKNOWN_ERROR);
}
/* Length for each buffer is the smaller of GKI buffer, peer MTU, or max_len */
length = RFCOMM_DATA_BUF_SIZE -
(UINT16)(sizeof(BT_HDR) + L2CAP_MIN_OFFSET + RFCOMM_DATA_OVERHEAD);
/* If there are buffers scheduled for transmission check if requested */
/* data fits into the end of the queue */
osi_mutex_global_lock();
if (((p_buf = (BT_HDR *)fixed_queue_try_peek_last(p_port->tx.queue)) != NULL)
p_buf = (BT_HDR *)fixed_queue_try_peek_last(p_port->tx.queue);
if ((p_buf != NULL)
&& ((p_buf->len + max_len) <= p_port->peer_mtu)
&& ((p_buf->len + max_len) <= length)) {
&& ((p_buf->len + max_len) <= p_buf->layer_specific)) {
memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset + p_buf->len, p_data, max_len);
p_port->tx.queue_size += max_len;
@@ -1686,8 +1683,9 @@ int PORT_WriteData (UINT16 handle, char *p_data, UINT16 max_len, UINT16 *p_len)
}
p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET;
p_buf->layer_specific = handle;
p_buf->layer_specific = RFCOMM_DATA_BUF_SIZE - (UINT16)(sizeof(BT_HDR) + L2CAP_MIN_OFFSET + RFCOMM_DATA_OVERHEAD + L2CAP_FCS_LEN);
/* Length for each buffer is the smaller of GKI buffer, peer MTU, or max_len */
length = p_buf->layer_specific;
if (p_port->peer_mtu < length) {
length = p_port->peer_mtu;
}
@@ -1762,7 +1760,8 @@ int PORT_Test (UINT16 handle, UINT8 *p_data, UINT16 len)
return (PORT_NOT_OPENED);
}
if (len > ((p_port->mtu == 0) ? RFCOMM_DEFAULT_MTU : p_port->mtu)) {
if ((len > ((p_port->mtu == 0) ? RFCOMM_DEFAULT_MTU : p_port->mtu))
|| (len > RFCOMM_CMD_BUF_SIZE - sizeof(BT_HDR) - (L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2))) {
return (PORT_UNKNOWN_ERROR);
}
@@ -873,6 +873,9 @@ BOOLEAN SDP_FindProfileVersionInRec (tSDP_DISC_REC *p_rec, UINT16 profile_uuid,
/* Now fill in the major and minor numbers */
/* if the attribute matches the description for version (type UINT, size 2 bytes) */
p_sattr = p_sattr->p_next_attr;
if (p_sattr == NULL) {
return (FALSE);
}
if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UINT_DESC_TYPE) &&
(SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2)) {
@@ -956,9 +956,12 @@ INT32 SDP_ReadRecord(UINT32 handle, UINT8 *p_data, INT32 *p_data_len)
UINT16 start = 0;
UINT16 end = 0xffff;
tSDP_ATTRIBUTE *p_attr;
UINT16 rem_len;
INT32 rem_len;
UINT8 *p_rsp;
if (p_data_len && *p_data_len <= 3) {
return offset;
}
/* Find the record in the database */
p_rec = sdp_db_find_record(handle);
if (p_rec && p_data && p_data_len) {
@@ -967,12 +970,17 @@ INT32 SDP_ReadRecord(UINT32 handle, UINT8 *p_data, INT32 *p_data_len)
/* Check if attribute fits. Assume 3-byte value type/length */
rem_len = *p_data_len - (UINT16) (p_rsp - p_data);
if (p_attr->len > (UINT32)(rem_len - 6)) {
UINT16 required_len = sdpu_get_attrib_entry_len(p_attr);
if (rem_len < (INT32)required_len) {
break;
}
p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr);
// Check overflow
if (p_attr->id == UINT16_MAX) {
break;
}
/* next attr id */
start = p_attr->id + 1;
}
@@ -209,9 +209,11 @@ void sdp_init (void)
void sdp_deinit (void)
{
#if SDP_DYNAMIC_MEMORY == FALSE
list_free(sdp_cb.server_db.p_record_list);
#if SDP_DYNAMIC_MEMORY
#else
if (sdp_cb_ptr) {
list_free(sdp_cb_ptr->server_db.p_record_list);
osi_free(sdp_cb_ptr);
sdp_cb_ptr = NULL;
}
@@ -485,6 +485,10 @@ static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
/* If doing a range, stick with this one till no more attributes found */
if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) {
// Check overflow
if (p_attr->id == UINT16_MAX) {
continue;
}
/* Update for next time through */
attr_seq.attr_entry[xx].start = p_attr->id + 1;
@@ -774,6 +778,10 @@ static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
/* If doing a range, stick with this one till no more attributes found */
if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) {
// Check overflow
if (p_attr->id == UINT16_MAX) {
continue;
}
/* Update for next time through */
attr_seq.attr_entry[xx].start = p_attr->id + 1;
@@ -956,6 +956,11 @@ UINT16 sdpu_get_attrib_seq_len(tSDP_RECORD *p_rec, tSDP_ATTR_SEQ *attr_seq)
/* If doing a range, stick with this one till no more attributes found */
if (start_id != end_id) {
// Check overflow
if (p_attr->id == UINT16_MAX) {
is_range = FALSE;
continue;
}
/* Update for next time through */
start_id = p_attr->id + 1;
xx--;
@@ -459,7 +459,7 @@ extern void smp_process_secure_connection_long_term_key(void);
extern void smp_set_local_oob_keys(tSMP_CB *p_cb, tSMP_INT_DATA *p_data);
extern void smp_set_local_oob_random_commitment(tSMP_CB *p_cb, tSMP_INT_DATA *p_data);
extern void smp_set_derive_link_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data);
extern void smp_derive_link_key_from_long_term_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data);
extern BOOLEAN smp_derive_link_key_from_long_term_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data);
extern void smp_br_process_pairing_command(tSMP_CB *p_cb, tSMP_INT_DATA *p_data);
extern void smp_br_process_security_grant(tSMP_CB *p_cb, tSMP_INT_DATA *p_data);
extern void smp_br_process_slave_keys_response(tSMP_CB *p_cb, tSMP_INT_DATA *p_data);
@@ -429,7 +429,7 @@ void multiprecision_fast_mod_P256(DWORD *c, DWORD *a)
if (U & 0x80000000) {
DWORD UU;
UU = 0 - U;
U = (a[1] < UU);
U = 0 - (a[1] < UU);
c[1] = a[1] - UU;
} else {
c[1] = a[1] + U;
@@ -446,7 +446,7 @@ void multiprecision_fast_mod_P256(DWORD *c, DWORD *a)
if (U & 0x80000000) {
DWORD UU;
UU = 0 - U;
U = (a[2] < UU);
U = 0 - (a[2] < UU);
c[2] = a[2] - UU;
} else {
c[2] = a[2] + U;
@@ -463,7 +463,7 @@ void multiprecision_fast_mod_P256(DWORD *c, DWORD *a)
if (U & 0x80000000) {
DWORD UU;
UU = 0 - U;
U = (a[3] < UU);
U = 0 - (a[3] < UU);
c[3] = a[3] - UU;
} else {
c[3] = a[3] + U;
@@ -488,7 +488,7 @@ void multiprecision_fast_mod_P256(DWORD *c, DWORD *a)
if (U & 0x80000000) {
DWORD UU;
UU = 0 - U;
U = (a[4] < UU);
U = 0 - (a[4] < UU);
c[4] = a[4] - UU;
} else {
c[4] = a[4] + U;
@@ -511,7 +511,7 @@ void multiprecision_fast_mod_P256(DWORD *c, DWORD *a)
if (U & 0x80000000) {
DWORD UU;
UU = 0 - U;
U = (a[5] < UU);
U = 0 - (a[5] < UU);
c[5] = a[5] - UU;
} else {
c[5] = a[5] + U;
@@ -532,7 +532,7 @@ void multiprecision_fast_mod_P256(DWORD *c, DWORD *a)
if (U & 0x80000000) {
DWORD UU;
UU = 0 - U;
U = (a[6] < UU);
U = 0 - (a[6] < UU);
c[6] = a[6] - UU;
} else {
c[6] = a[6] + U;
@@ -555,7 +555,7 @@ void multiprecision_fast_mod_P256(DWORD *c, DWORD *a)
if (U & 0x80000000) {
DWORD UU;
UU = 0 - U;
U = (a[7] < UU);
U = 0 - (a[7] < UU);
c[7] = a[7] - UU;
} else {
c[7] = a[7] + U;
@@ -1461,7 +1461,9 @@ void smp_key_distribution(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
SMP_TRACE_DEBUG("%s BREDR key is higher security than existing LE keys, "
"don't derive LK from LTK", __func__);
} else {
smp_derive_link_key_from_long_term_key(p_cb, NULL);
if (!smp_derive_link_key_from_long_term_key(p_cb, NULL)){
return;
}
}
p_cb->derive_lk = FALSE;
}
@@ -2261,10 +2263,10 @@ void smp_set_derive_link_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
**
** Description This function is called to derive BR/EDR LK from LTK.
**
** Returns void
** Returns BOOLEAN
**
*******************************************************************************/
void smp_derive_link_key_from_long_term_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
BOOLEAN smp_derive_link_key_from_long_term_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data)
{
tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN;
@@ -2272,8 +2274,9 @@ void smp_derive_link_key_from_long_term_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data
if (!smp_calculate_link_key_from_long_term_key(p_cb)) {
SMP_TRACE_ERROR("%s failed\n", __FUNCTION__);
smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status);
return;
return FALSE;
}
return TRUE;
}
#endif ///BLE_INCLUDED == TRUE
@@ -217,7 +217,7 @@ tSMP_STATUS SMP_BR_PairWith (BD_ADDR bd_addr)
if (!L2CA_ConnectFixedChnl (L2CAP_SMP_BR_CID, bd_addr, BLE_ADDR_UNKNOWN_TYPE, FALSE, FALSE, 0xFF, 0xFF)) {
SMP_TRACE_ERROR("%s: L2C connect fixed channel failed.", __FUNCTION__);
smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &status);
smp_reset_control_value(p_cb);
return status;
}
@@ -484,8 +484,8 @@ BOOLEAN aes_cipher_msg_auth_code(BT_OCTET16 key, UINT8 *input, UINT16 length,
}
#else
{
UINT16 len, diff;
UINT16 n = (length + BT_OCTET16_LEN - 1) / BT_OCTET16_LEN;
UINT32 len, diff;
UINT32 n = (length + BT_OCTET16_LEN - 1) / BT_OCTET16_LEN;
if (n == 0) {
n = 1;
@@ -730,6 +730,11 @@ void smp_sm_event(tSMP_CB *p_cb, tSMP_EVENT event, void *p_data)
UINT8 curr_state = p_cb->state;
tSMP_SM_TBL state_table;
UINT8 action, entry, i;
if (p_cb->role > 1) {
SMP_TRACE_ERROR( "Invalid role\n") ;
return;
}
tSMP_ENTRY_TBL entry_table = smp_entry_table[p_cb->role];
SMP_TRACE_EVENT("main smp_sm_event\n");