diff --git a/components/bt/host/bluedroid/stack/l2cap/include/l2c_int.h b/components/bt/host/bluedroid/stack/l2cap/include/l2c_int.h index df956dc442..cfc5163664 100644 --- a/components/bt/host/bluedroid/stack/l2cap/include/l2c_int.h +++ b/components/bt/host/bluedroid/stack/l2cap/include/l2c_int.h @@ -461,7 +461,7 @@ typedef struct t_l2c_linkcb { */ /* create connection retry count*/ UINT8 retry_create_con; - UINT32 start_time_s; + int64_t start_time_s; #endif #if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) @@ -736,7 +736,7 @@ extern void l2c_link_timeout (tL2C_LCB *p_lcb); extern void l2c_info_timeout (tL2C_LCB *p_lcb); extern void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf); extern void l2c_link_adjust_allocation (void); -extern void l2c_link_process_num_completed_pkts (UINT8 *p); +extern void l2c_link_process_num_completed_pkts (UINT8 *p, UINT8 evt_len); extern void l2c_link_process_num_completed_blocks (UINT8 controller_id, UINT8 *p, UINT16 evt_len); extern void l2c_link_processs_num_bufs (UINT16 num_lm_acl_bufs); extern UINT8 l2c_link_pkts_rcvd (UINT16 *num_pkts, UINT16 *handles); diff --git a/components/bt/host/bluedroid/stack/l2cap/l2c_api.c b/components/bt/host/bluedroid/stack/l2cap/l2c_api.c index 07377901a7..c0487f16a5 100644 --- a/components/bt/host/bluedroid/stack/l2cap/l2c_api.c +++ b/components/bt/host/bluedroid/stack/l2cap/l2c_api.c @@ -1863,6 +1863,11 @@ UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf) BOOLEAN L2CA_CheckIsCongest(UINT16 fixed_cid, BD_ADDR addr) { + if ((fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL)) { + L2CAP_TRACE_ERROR ("L2CA_CheckIsCongest() Invalid CID: 0x%04x", fixed_cid); + return TRUE; + } + tL2C_LCB *p_lcb; p_lcb = l2cu_find_lcb_by_bd_addr(addr, BT_TRANSPORT_LE); diff --git a/components/bt/host/bluedroid/stack/l2cap/l2c_ble.c b/components/bt/host/bluedroid/stack/l2cap/l2c_ble.c index 88cc764dcf..c430e55629 100644 --- a/components/bt/host/bluedroid/stack/l2cap/l2c_ble.c +++ b/components/bt/host/bluedroid/stack/l2cap/l2c_ble.c @@ -828,7 +828,6 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len) STREAM_TO_UINT16(mps, p); STREAM_TO_UINT16(credits, p); L2CAP_TRACE_DEBUG("%s spsm %x, scid %x", __func__, spsm, scid); - UNUSED(spsm); p_ccb = l2cu_find_ccb_by_remote_cid(p_lcb, scid); if (p_ccb) { @@ -836,12 +835,11 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len) break; } - #if 0 p_rcb = l2cu_find_ble_rcb_by_psm(spsm); if (p_rcb == NULL) { + l2cu_reject_ble_connection(p_lcb, id, L2CAP_LE_RESULT_NO_PSM); break; } - #endif p_ccb = l2cu_allocate_ccb(p_lcb, 0); if (p_ccb == NULL) { @@ -862,6 +860,34 @@ void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len) l2cu_send_peer_ble_credit_based_conn_res(p_ccb, L2CAP_LE_RESULT_CONN_OK); break; } + case L2CAP_CMD_BLE_FLOW_CTRL_CREDIT: { + if (cmd_len < L2CAP_CMD_BLE_FLOW_CTRL_CREDIT_LEN) { + L2CAP_TRACE_WARNING ("L2CAP - LE - flow ctrl credit too short: %d", cmd_len); + return; + } + tL2C_CCB *p_ccb = NULL; + UINT16 lcid; + UINT16 credit; + STREAM_TO_UINT16(lcid, p); + STREAM_TO_UINT16(credit, p); + + p_ccb = l2cu_find_ccb_by_cid(p_lcb, lcid); + if (p_ccb == NULL) { + L2CAP_TRACE_WARNING ("L2CAP - LE - flow ctrl credit for unknown CID: 0x%04x", lcid); + break; + } + + if ((p_ccb->peer_conn_cfg.credits + credit) > L2CAP_LE_MAX_CREDIT) { + L2CAP_TRACE_WARNING ("L2CAP - LE - credit overflow, disconnecting CID: 0x%04x", lcid); + l2cble_send_peer_disc_req(p_ccb); + } else { + p_ccb->peer_conn_cfg.credits += credit; + L2CAP_TRACE_DEBUG ("L2CAP - LE - credits updated to %d for CID: 0x%04x", + p_ccb->peer_conn_cfg.credits, lcid); + l2c_link_check_send_pkts(p_ccb->p_lcb, NULL, NULL); + } + break; + } case L2CAP_CMD_DISC_REQ: { if (cmd_len < 4) { L2CAP_TRACE_WARNING ("L2CAP - LE - short cmd: %d", cmd_len); @@ -988,13 +1014,20 @@ BOOLEAN l2cble_init_direct_conn (tL2C_LCB *p_lcb) uint32_t link_timeout = L2CAP_BLE_LINK_CONNECT_TOUT; if(GATTC_CONNECT_RETRY_COUNT) { if(!p_lcb->retry_create_con) { - p_lcb->start_time_s = (esp_system_get_time()/1000); + p_lcb->start_time_s = esp_system_get_time() / 1000; } - uint32_t current_time = (esp_system_get_time()/1000); - link_timeout = (L2CAP_BLE_LINK_CONNECT_TOUT*1000 - (current_time - p_lcb->start_time_s))/1000; + int64_t current_time = esp_system_get_time() / 1000; + int64_t elapsed_ms = current_time - p_lcb->start_time_s; + int64_t timeout_ms = (int64_t)L2CAP_BLE_LINK_CONNECT_TOUT * 1000; + int64_t remaining_ms = timeout_ms - elapsed_ms; - if(link_timeout == 0 || link_timeout > L2CAP_BLE_LINK_CONNECT_TOUT) { + if (remaining_ms <= 0 || remaining_ms > timeout_ms) { link_timeout = L2CAP_BLE_LINK_CONNECT_TOUT; + } else { + link_timeout = (uint32_t)(remaining_ms / 1000); + if (link_timeout == 0) { + link_timeout = 1; + } } } diff --git a/components/bt/host/bluedroid/stack/l2cap/l2c_link.c b/components/bt/host/bluedroid/stack/l2cap/l2c_link.c index 9eefc8c075..347e220a41 100644 --- a/components/bt/host/bluedroid/stack/l2cap/l2c_link.c +++ b/components/bt/host/bluedroid/stack/l2cap/l2c_link.c @@ -379,6 +379,9 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason) } else { #if (BLE_INCLUDED == TRUE) tL2C_LINK_STATE link_state_temp = p_lcb->link_state; +#if ((BLE_42_ADV_EN == TRUE) || (GATTC_CONNECT_RETRY_EN == TRUE) || (BLE_50_EXTEND_ADV_EN == TRUE)) + UINT8 link_role_temp = p_lcb->link_role; +#endif #endif // (BLE_INCLUDED == TRUE) /* There can be a case when we rejected PIN code authentication */ /* otherwise save a new reason */ @@ -469,6 +472,7 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason) } #endif } + /* l2cu_create_conn must not release the LCB on failure */ if (l2cu_create_conn(p_lcb, transport)) { lcb_is_free = FALSE; /* still using this lcb */ } @@ -476,13 +480,15 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason) p_lcb->p_pending_ccb = NULL; #if (BLE_INCLUDED == TRUE) - if(p_lcb->transport == BT_TRANSPORT_LE) { + /* Use p_lcb->transport so BLE reconnection (slave adv restart / master retry) runs even when + * there was no pending CCB; 'transport' is only set when (p_first_ccb || p_pending_ccb). */ + if (p_lcb->transport == BT_TRANSPORT_LE) { // for legacy adv, adv restart in gatt_le_connect_cback->gatt_cleanup_upon_disc->BTM_Recovery_Pre_State if (reason == HCI_ERR_CONN_FAILED_ESTABLISHMENT) { #if (BLE_42_FEATURE_SUPPORT == TRUE) #if (BLE_42_ADV_EN == TRUE) - if(!btm_ble_inter_get() && p_lcb->link_role == HCI_ROLE_SLAVE) { + if(!btm_ble_inter_get() && link_role_temp == HCI_ROLE_SLAVE) { L2CAP_TRACE_DEBUG("slave resatrt adv, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason); tBTM_STATUS start_adv_status = btm_ble_start_adv(); if (start_adv_status != BTM_SUCCESS) { @@ -496,7 +502,7 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason) if ((reason == HCI_ERR_CONN_FAILED_ESTABLISHMENT) || (link_state_temp == LST_CONNECTING)) { #if (GATTC_CONNECT_RETRY_EN == TRUE) - if(p_lcb->link_role == HCI_ROLE_MASTER && p_lcb->retry_create_con < GATTC_CONNECT_RETRY_COUNT) { + if(link_role_temp == HCI_ROLE_MASTER && p_lcb->retry_create_con < GATTC_CONNECT_RETRY_COUNT) { L2CAP_TRACE_DEBUG("master retry connect, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason); p_lcb->retry_create_con ++; // create connection retry @@ -513,7 +519,7 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason) if ((reason == HCI_ERR_CONN_FAILED_ESTABLISHMENT) || (link_state_temp == LST_DISCONNECTED)) { #if (BLE_50_FEATURE_SUPPORT == TRUE) #if (BLE_50_EXTEND_ADV_EN == TRUE) - if(btm_ble_inter_get() && p_lcb->link_role == HCI_ROLE_SLAVE) { + if(btm_ble_inter_get() && link_role_temp == HCI_ROLE_SLAVE) { L2CAP_TRACE_DEBUG("slave restart extend adv, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason); tBTM_STATUS start_adv_status = BTM_BleStartExtAdvRestart(handle); if (start_adv_status != BTM_SUCCESS) { @@ -626,6 +632,7 @@ void l2c_link_timeout (tL2C_LCB *p_lcb) #endif /* Release the LCB */ l2cu_release_lcb (p_lcb); + return; } /* If link is connected, check for inactivity timeout */ @@ -1314,6 +1321,13 @@ static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf) acl_data_size = controller->get_acl_data_size_classic(); xmit_window = l2cb.controller_xmit_window; } + + if (p_buf->len <= HCI_DATA_PREAMBLE_SIZE) { + L2CAP_TRACE_ERROR ("l2c_link_send_to_lower - bad buffer len: %u", p_buf->len); + osi_free(p_buf); + return FALSE; + } + num_segs = (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_data_size - 1) / acl_data_size; @@ -1395,15 +1409,26 @@ static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf) ** Returns void ** *******************************************************************************/ -void l2c_link_process_num_completed_pkts (UINT8 *p) +void l2c_link_process_num_completed_pkts (UINT8 *p, UINT8 evt_len) { UINT8 num_handles, xx; UINT16 handle; UINT16 num_sent; tL2C_LCB *p_lcb; + if (evt_len < 1) { + L2CAP_TRACE_ERROR ("l2c_link_process_num_completed_pkts: evt too short (len=%u)", evt_len); + return; + } + STREAM_TO_UINT8 (num_handles, p); + if (num_handles > (evt_len - 1) / 4) { + L2CAP_TRACE_ERROR ("l2c_link_process_num_completed_pkts: num_handles %u exceeds evt_len %u, truncating", + num_handles, evt_len); + num_handles = (evt_len - 1) / 4; + } + for (xx = 0; xx < num_handles; xx++) { STREAM_TO_UINT16 (handle, p); STREAM_TO_UINT16 (num_sent, p); diff --git a/components/bt/host/bluedroid/stack/l2cap/l2c_main.c b/components/bt/host/bluedroid/stack/l2cap/l2c_main.c index c2c3150b2d..1a58b69277 100644 --- a/components/bt/host/bluedroid/stack/l2cap/l2c_main.c +++ b/components/bt/host/bluedroid/stack/l2cap/l2c_main.c @@ -150,7 +150,6 @@ void l2c_rcv_acl_data (BT_HDR *p_msg) #if (!CONFIG_BT_STACK_NO_LOG) UINT16 psm; #endif - UINT16 credit; /* Extract the handle */ STREAM_TO_UINT16 (handle, p); @@ -292,6 +291,9 @@ void l2c_rcv_acl_data (BT_HDR *p_msg) if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) { #if (CLASSIC_BT_INCLUDED == TRUE) l2c_fcr_proc_pdu (p_ccb, p_msg); +#else + /* Classic FCR not compiled (e.g. BLE-only); free p_msg to avoid leak */ + osi_free (p_msg); #endif ///CLASSIC_BT_INCLUDED == TRUE } else { (*l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb) @@ -310,31 +312,24 @@ void l2c_rcv_acl_data (BT_HDR *p_msg) osi_free (p_msg); } else { if (p_lcb->transport == BT_TRANSPORT_LE) { - // Got a pkt, valid send out credits to the peer device - credit = L2CAP_LE_DEFAULT_CREDIT; - L2CAP_TRACE_DEBUG("%s Credits received %d",__func__, credit); - if((p_ccb->peer_conn_cfg.credits + credit) > L2CAP_LE_MAX_CREDIT) { - /* we have received credits more than max coc credits, - * so disconnecting the Le Coc Channel - */ -#if (BLE_INCLUDED == TRUE) - l2cble_send_peer_disc_req (p_ccb); -#endif ///BLE_INCLUDED == TRUE - } else { - p_ccb->peer_conn_cfg.credits += credit; - l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); - } + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); } /* Basic mode packets go straight to the state machine */ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) { -#if (CLASSIC_BT_INCLUDED == TRUE) +#if (L2CAP_COC_INCLUDED == TRUE) l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_msg); -#endif ///CLASSIC_BT_INCLUDED == TRUE +#else + /* COC not included: state machine not compiled; free p_msg to avoid leak */ + osi_free (p_msg); +#endif } else { /* eRTM or streaming mode, so we need to validate states first */ if ((p_ccb->chnl_state == CST_OPEN) || (p_ccb->chnl_state == CST_CONFIG)) { #if (CLASSIC_BT_INCLUDED == TRUE) l2c_fcr_proc_pdu (p_ccb, p_msg); +#else + /* Classic FCR not compiled (e.g. BLE-only); free p_msg to avoid leak */ + osi_free (p_msg); #endif ///CLASSIC_BT_INCLUDED == TRUE } else { osi_free (p_msg); @@ -423,14 +418,26 @@ static void process_l2cap_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len) switch (cmd_code) { case L2CAP_CMD_REJECT: + if (cmd_len < L2CAP_CMD_REJECT_LEN) { + L2CAP_TRACE_WARNING ("L2CAP - cmd reject too short, cmd_len: %d", cmd_len); + break; + } STREAM_TO_UINT16 (rej_reason, p); if (rej_reason == L2CAP_CMD_REJ_MTU_EXCEEDED) { + if (cmd_len < L2CAP_CMD_REJECT_LEN + 2) { + L2CAP_TRACE_WARNING ("L2CAP - MTU rej too short, cmd_len: %d", cmd_len); + break; + } STREAM_TO_UINT16 (rej_mtu, p); /* What to do with the MTU reject ? We have negotiated an MTU. For now */ /* we will ignore it and let a higher protocol timeout take care of it */ L2CAP_TRACE_WARNING ("L2CAP - MTU rej Handle: %d MTU: %d", p_lcb->handle, rej_mtu); } if (rej_reason == L2CAP_CMD_REJ_INVALID_CID) { + if (cmd_len < L2CAP_CMD_REJECT_LEN + 4) { + L2CAP_TRACE_WARNING ("L2CAP - CID rej too short, cmd_len: %d", cmd_len); + break; + } STREAM_TO_UINT16 (rcid, p); STREAM_TO_UINT16 (lcid, p); diff --git a/components/bt/host/bluedroid/stack/l2cap/l2c_utils.c b/components/bt/host/bluedroid/stack/l2cap/l2c_utils.c index 3d3c1cc3ff..ad996c18da 100644 --- a/components/bt/host/bluedroid/stack/l2cap/l2c_utils.c +++ b/components/bt/host/bluedroid/stack/l2cap/l2c_utils.c @@ -1899,15 +1899,31 @@ void l2cu_disconnect_chnl (tL2C_CCB *p_ccb) UINT16 local_cid = p_ccb->local_cid; if (local_cid >= L2CAP_BASE_APPL_CID) { - tL2CA_DISCONNECT_IND_CB *p_disc_cb = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + tL2CA_DISCONNECT_IND_CB *p_disc_cb = NULL; + + if (p_ccb->p_rcb != NULL) { + p_disc_cb = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + } L2CAP_TRACE_WARNING ("L2CAP - disconnect_chnl CID: 0x%04x", local_cid); l2cu_send_peer_disc_req (p_ccb); + /* + * l2cu_send_peer_disc_req drains xmit_hold_q in basic FCR mode. + * Free the queue and NULL it here to prevent l2cu_release_ccb from + * calling fixed_queue_free again on already-dequeued buffers. + */ + if (p_ccb->xmit_hold_q != NULL) { + fixed_queue_free(p_ccb->xmit_hold_q, osi_free_func); + p_ccb->xmit_hold_q = NULL; + } + l2cu_release_ccb (p_ccb); - (*p_disc_cb)(local_cid, FALSE); + if (p_disc_cb) { + (*p_disc_cb)(local_cid, FALSE); + } } else { /* failure on the AMP channel, probably need to disconnect ACL */ L2CAP_TRACE_ERROR ("L2CAP - disconnect_chnl CID: 0x%04x Ignored", local_cid);