diff --git a/components/bt/host/bluedroid/stack/gap/gap_ble.c b/components/bt/host/bluedroid/stack/gap/gap_ble.c index 823fadc983..97e7d9da82 100644 --- a/components/bt/host/bluedroid/stack/gap/gap_ble.c +++ b/components/bt/host/bluedroid/stack/gap/gap_ble.c @@ -315,6 +315,9 @@ UINT8 gap_proc_write_req( tGATTS_REQ_TYPE type, tGATT_WRITE_REQ *p_data) switch (p_db_attr->uuid) { #if (GATTS_DEVICE_NAME_WRITABLE == TRUE) case GATT_UUID_GAP_DEVICE_NAME: { + if (p_data->len > BD_NAME_LEN) { + return GATT_INVALID_ATTR_LEN; + } UINT8 *p_val = p_data->value; p_val[p_data->len] = '\0'; BTM_SetLocalDeviceName((char *)p_val, BT_DEVICE_TYPE_BLE); diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_cl.c b/components/bt/host/bluedroid/stack/gatt/gatt_cl.c index 98214f8797..5d7c7e2bb3 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_cl.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_cl.c @@ -1146,9 +1146,11 @@ BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb) } } else { GATT_TRACE_ERROR("gatt_cl_send_next_cmd_inq: L2CAP sent error"); - + /* attp_send_msg_to_l2cap() already freed p_cmd->p_cmd on failure */ + p_cmd->p_cmd = NULL; memset(p_cmd, 0, sizeof(tGATT_CMD_Q)); p_tcb->pending_cl_req ++; + p_tcb->pending_cl_req %= GATT_CL_MAX_LCB; p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; } diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_sr.c b/components/bt/host/bluedroid/stack/gatt/gatt_sr.c index 25bc51a02c..62b170b1a2 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_sr.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_sr.c @@ -486,9 +486,11 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U tGATT_IF gatt_if; UINT16 conn_id; UINT16 queue_num = 0; - BOOLEAN is_first = TRUE; + UINT16 zeroed_cap = 0, zeroed_count = 0; + void **zeroed_attrs = NULL; BOOLEAN is_prepare_write_valid = FALSE; BOOLEAN is_need_dequeue_sr_cmd = FALSE; + BOOLEAN sr_cmd_already_dequeued = FALSE; tGATT_PREPARE_WRITE_RECORD *prepare_record = NULL; tGATT_PREPARE_WRITE_QUEUE_DATA * queue_data = NULL; @@ -529,6 +531,7 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U gatt_exec_write_rsp.op_code = GATT_RSP_EXEC_WRITE; gatt_send_packet(p_tcb, (UINT8 *)(&gatt_exec_write_rsp), sizeof(gatt_exec_write_rsp)); gatt_dequeue_sr_cmd(p_tcb); + sr_cmd_already_dequeued = TRUE; if (flag != GATT_PREP_WRITE_CANCEL){ is_prepare_write_valid = TRUE; } @@ -549,23 +552,99 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U gatt_send_error_rsp(p_tcb, prepare_record->error_code_app, GATT_REQ_EXEC_WRITE, 0, is_need_dequeue_sr_cmd); } - //dequeue prepare write data + /* Track which attributes we've zeroed so we only zero on first write per attribute. + * Zeroing all up-front would leave attr_len=0 for attributes whose write is later + * skipped (e.g. overflow), causing data loss. */ + if (is_prepare_write_valid && queue_num > 0) { + zeroed_cap = queue_num; /* max unique attributes in this exec is at most queue_num */ + zeroed_attrs = (void **)osi_calloc(zeroed_cap * sizeof(void *)); + if (zeroed_attrs == NULL) { + GATT_TRACE_ERROR("%s: no memory for zeroed_attrs, abort exec write", __func__); + while (fixed_queue_try_peek_first(prepare_record->queue)) { + queue_data = fixed_queue_dequeue(prepare_record->queue, FIXED_QUEUE_MAX_TIMEOUT); + osi_free(queue_data); + } + fixed_queue_free(prepare_record->queue, NULL); + prepare_record->queue = NULL; + { + UINT16 total_num_saved = prepare_record->total_num; + prepare_record->total_num = 0; + prepare_record->error_code_app = GATT_SUCCESS; + /* Only send error (and dequeue via gatt_send_error_rsp) if we did not already send success and dequeue (first branch) */ + if (!sr_cmd_already_dequeued) { + gatt_send_error_rsp(p_tcb, GATT_NO_RESOURCES, GATT_REQ_EXEC_WRITE, 0, TRUE); + } else { + /* Success already sent to peer; notify apps and clear prep_cnt so state stays consistent */ + if (!gatt_sr_is_prep_cnt_zero(p_tcb)) { + if (total_num_saved > queue_num) { + trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, 0); + gatt_sr_copy_prep_cnt_to_cback_cnt(p_tcb); + } + for (i = 0; i < GATT_MAX_APPS; i++) { + if (p_tcb->prep_cnt[i]) { + gatt_if = (tGATT_IF) (i + 1); + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_if); + gatt_sr_send_req_callback(conn_id, + trans_id, + GATTS_REQ_TYPE_WRITE_EXEC, + (tGATTS_DATA *)&flag); + p_tcb->prep_cnt[i] = 0; + } + } + /* OOM path: we enqueued sr_cmd but will not get app response flow; clear sr_cmd so later requests are not dropped */ + if (total_num_saved > queue_num) { + gatt_dequeue_sr_cmd(p_tcb); + } + } + } + } + return; + } + } + while(fixed_queue_try_peek_first(prepare_record->queue)) { queue_data = fixed_queue_dequeue(prepare_record->queue, FIXED_QUEUE_MAX_TIMEOUT); if (is_prepare_write_valid){ if((queue_data->p_attr->p_value != NULL) && (queue_data->p_attr->p_value->attr_val.attr_val != NULL)){ - if(is_first) { - //clear attr_val.attr_len before handle prepare write data - queue_data->p_attr->p_value->attr_val.attr_len = 0; - is_first = FALSE; + UINT16 attr_max_len = queue_data->p_attr->p_value->attr_val.attr_max_len; + if (queue_data->offset > attr_max_len || + queue_data->len > attr_max_len - queue_data->offset) { + GATT_TRACE_ERROR("%s: exec write overflow prevented, offset=%u len=%u max=%u", + __func__, queue_data->offset, queue_data->len, attr_max_len); + } else { + UINT16 k; + BOOLEAN need_zero = TRUE; + /* Zero attr_len only on first successful write to this attribute in this exec */ + if (zeroed_attrs != NULL) { + for (k = 0; k < zeroed_count; k++) { + if (zeroed_attrs[k] == (void *)queue_data->p_attr) { + need_zero = FALSE; + break; + } + } + } + if (need_zero) { + queue_data->p_attr->p_value->attr_val.attr_len = 0; + if (zeroed_attrs != NULL && zeroed_count < zeroed_cap) { + zeroed_attrs[zeroed_count++] = (void *)queue_data->p_attr; + } + } + memcpy(queue_data->p_attr->p_value->attr_val.attr_val + queue_data->offset, + queue_data->value, queue_data->len); + { + UINT16 write_end = queue_data->offset + queue_data->len; + if (write_end > queue_data->p_attr->p_value->attr_val.attr_len) { + queue_data->p_attr->p_value->attr_val.attr_len = write_end; + } + } } - memcpy(queue_data->p_attr->p_value->attr_val.attr_val+queue_data->offset, queue_data->value, queue_data->len); - //don't forget to increase the attribute value length in the gatts database. - queue_data->p_attr->p_value->attr_val.attr_len += queue_data->len; } } osi_free(queue_data); } + if (zeroed_attrs != NULL) { + osi_free(zeroed_attrs); + } fixed_queue_free(prepare_record->queue, NULL); prepare_record->queue = NULL; diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_sr_hash.c b/components/bt/host/bluedroid/stack/gatt/gatt_sr_hash.c index 067c25d08e..869610b370 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_sr_hash.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_sr_hash.c @@ -86,10 +86,13 @@ static size_t calculate_database_info_size(void) len += 8 + get_uuid_stream_len(p_attr->p_value->incl_handle.service_type); } else if (p_attr->uuid == GATT_UUID_CHAR_DECLARE) { tBT_UUID char_uuid = {0}; - // Characteristic declaration + if (p_attr->p_next == NULL) { + GATT_TRACE_ERROR("%s: malformed DB, char decl at handle %u has no value attr", + __func__, p_attr->handle); + break; + } p_attr = (tGATT_ATTR16 *)p_attr->p_next; attr_uuid_to_bt_uuid((void *)p_attr, &char_uuid); - // Increment 1 to fetch characteristic uuid from value declaration attribute len += 7 + get_uuid_stream_len(char_uuid); } else if (p_attr->uuid == GATT_UUID_CHAR_DESCRIPTION || p_attr->uuid == GATT_UUID_CHAR_CLIENT_CONFIG || @@ -136,14 +139,17 @@ static void fill_database_info(UINT8 *p_data) gatt_build_uuid_to_stream(&p_data, p_attr->p_value->incl_handle.service_type); } else if (p_attr->uuid == GATT_UUID_CHAR_DECLARE) { tBT_UUID char_uuid = {0}; - // Characteristic declaration + if (p_attr->p_next == NULL) { + GATT_TRACE_ERROR("%s: malformed DB, char decl at handle %u has no value attr", + __func__, p_attr->handle); + break; + } UINT16_TO_STREAM(p_data, p_attr->handle); UINT16_TO_STREAM(p_data, GATT_UUID_CHAR_DECLARE); UINT8_TO_STREAM(p_data, p_attr->p_value->char_decl.property); UINT16_TO_STREAM(p_data, p_attr->p_value->char_decl.char_val_handle); p_attr = (tGATT_ATTR16 *)p_attr->p_next; attr_uuid_to_bt_uuid((void *)p_attr, &char_uuid); - // Increment 1 to fetch characteristic uuid from value declaration attribute gatt_build_uuid_to_stream(&p_data, char_uuid); } else if (p_attr->uuid == GATT_UUID_CHAR_DESCRIPTION || p_attr->uuid == GATT_UUID_CHAR_CLIENT_CONFIG || @@ -160,6 +166,7 @@ static void fill_database_info(UINT8 *p_data) // TODO: process extended properties descriptor if (p_attr->p_value->attr_val.attr_len == 2) { memcpy(p_data, p_attr->p_value->attr_val.attr_val, 2); + p_data += 2; } else { UINT16_TO_STREAM(p_data, 0x0000); } @@ -180,6 +187,11 @@ tGATT_STATUS gatts_calculate_datebase_hash(BT_OCTET16 hash) len = calculate_database_info_size(); + if (len == 0) { + memset(hash, 0, BT_OCTET16_LEN); + return GATT_SUCCESS; + } + data_buf = (UINT8 *)osi_malloc(len); if (data_buf == NULL) { GATT_TRACE_ERROR ("%s failed to allocate buffer (%u)\n", __func__, len); @@ -237,6 +249,10 @@ void gatts_show_local_database(void) tBT_UUID char_uuid = {0}; tGATT_ATTR16 *p_char_val; p_char_val = (tGATT_ATTR16 *)p_attr->p_next; + if (p_char_val == NULL) { + printf("characteristic (malformed - no value attr)\n"); + break; + } attr_uuid_to_bt_uuid((void *)p_char_val, &char_uuid); printf("%s\n", gatt_get_attr_name(p_attr->uuid)); diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_utils.c b/components/bt/host/bluedroid/stack/gatt/gatt_utils.c index 451ce28709..e8babbbad4 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_utils.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_utils.c @@ -797,11 +797,11 @@ tGATTS_SRV_CHG *gatt_is_bda_in_the_srv_chg_clt_list (BD_ADDR bda) p_buf = (tGATTS_SRV_CHG *)list_node(node); if (!memcmp( bda, p_buf->bda, BD_ADDR_LEN)) { GATT_TRACE_DEBUG("bda is in the srv chg clt list"); - break; + return p_buf; } } - return p_buf; + return NULL; } #endif // (GATTS_INCLUDED == TRUE) @@ -1006,11 +1006,11 @@ tGATT_TCB *gatt_allocate_tcb_by_bdaddr(BD_ADDR bda, tBT_TRANSPORT transport) allocated = TRUE; } if (i != GATT_INDEX_INVALID) { - p_tcb = gatt_tcb_alloc(i); - if (!p_tcb) { - return NULL; - } if (allocated) { + p_tcb = gatt_tcb_alloc(i); + if (!p_tcb) { + return NULL; + } memset(p_tcb, 0, sizeof(tGATT_TCB)); #if (SMP_INCLUDED == TRUE) p_tcb->pending_enc_clcb = fixed_queue_new(QUEUE_SIZE_MAX); @@ -1018,6 +1018,11 @@ tGATT_TCB *gatt_allocate_tcb_by_bdaddr(BD_ADDR bda, tBT_TRANSPORT transport) p_tcb->in_use = TRUE; p_tcb->tcb_idx = i; p_tcb->transport = transport; + } else { + p_tcb = gatt_get_tcb_by_idx(i); + if (!p_tcb) { + return NULL; + } } memcpy(p_tcb->peer_bda, bda, BD_ADDR_LEN); #if GATTS_ROBUST_CACHING_ENABLED @@ -1778,10 +1783,10 @@ tGATT_TCB *gatt_find_tcb_by_cid (UINT16 lcid) for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { p_tcb = list_node(p_node); if (p_tcb->in_use && p_tcb->att_lcid == lcid) { - break; + return p_tcb; } } - return p_tcb; + return NULL; } /******************************************************************************* @@ -1969,7 +1974,7 @@ void gatt_sr_update_cback_cnt(tGATT_TCB *p_tcb, tGATT_IF gatt_if, BOOLEAN is_inc #if (GATTS_INCLUDED == TRUE) UINT8 idx = ((UINT8) gatt_if) - 1 ; - if (p_tcb) { + if (p_tcb && idx < GATT_MAX_APPS) { if (is_reset_first) { gatt_sr_reset_cback_cnt(p_tcb); } @@ -1999,9 +2004,9 @@ void gatt_sr_update_prep_cnt(tGATT_TCB *p_tcb, tGATT_IF gatt_if, BOOLEAN is_inc, UINT8 idx = ((UINT8) gatt_if) - 1 ; GATT_TRACE_DEBUG("gatt_sr_update_prep_cnt tcb idx=%d gatt_if=%d is_inc=%d is_reset_first=%d", - p_tcb->tcb_idx, gatt_if, is_inc, is_reset_first); + p_tcb ? p_tcb->tcb_idx : 0, gatt_if, is_inc, is_reset_first); - if (p_tcb) { + if (p_tcb && idx < GATT_MAX_APPS) { if (is_reset_first) { gatt_sr_reset_prep_cnt(p_tcb); } @@ -2164,6 +2169,11 @@ UINT8 gatt_send_write_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, { tGATT_CL_MSG msg; + if (len > GATT_MAX_ATTR_LEN) { + GATT_TRACE_ERROR("%s: len %u exceeds max %u", __func__, len, GATT_MAX_ATTR_LEN); + return GATT_ILLEGAL_PARAMETER; + } + msg.attr_value.handle = handle; msg.attr_value.len = len; msg.attr_value.offset = offset; @@ -2312,9 +2322,9 @@ void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason, tBT_TRANSPORT transport) GATT_TRACE_DEBUG ("found p_clcb conn_id=%d clcb_idx=%d", p_clcb->conn_id, p_clcb->clcb_idx); if (p_clcb->operation != GATTC_OPTYPE_NONE) { gatt_end_operation(p_clcb, GATT_ERROR, NULL); - p_clcb = NULL; + } else { + gatt_clcb_dealloc(p_clcb); } - gatt_clcb_dealloc(p_clcb); } } #if (GATTC_INCLUDED == TRUE) diff --git a/components/bt/host/bluedroid/stack/hcic/hciblecmds.c b/components/bt/host/bluedroid/stack/hcic/hciblecmds.c index a688c12b00..445c61ec06 100644 --- a/components/bt/host/bluedroid/stack/hcic/hciblecmds.c +++ b/components/bt/host/bluedroid/stack/hcic/hciblecmds.c @@ -1329,6 +1329,10 @@ UINT8 btsnd_hcic_ble_set_ext_adv_data(UINT8 adv_handle, HCI_TRACE_EVENT("%s, adv_handle = %d, operation = %d, fragment_prefrence = %d,\ data_len = %d", __func__, adv_handle, operation, fragment_prefrence, data_len); + if (data_len > HCIC_PARAM_SIZE_EXT_ADV_WRITE_DATA) { + data_len = HCIC_PARAM_SIZE_EXT_ADV_WRITE_DATA; + } + HCIC_BLE_CMD_CREATED(p, pp, data_len + 4); UINT16_TO_STREAM(pp, HCI_BLE_SET_EXT_ADV_DATA); UINT8_TO_STREAM(pp, data_len + 4); @@ -1336,10 +1340,6 @@ UINT8 btsnd_hcic_ble_set_ext_adv_data(UINT8 adv_handle, UINT8_TO_STREAM(pp, operation); UINT8_TO_STREAM(pp, fragment_prefrence); - if (data_len > HCIC_PARAM_SIZE_EXT_ADV_WRITE_DATA) { - data_len = HCIC_PARAM_SIZE_EXT_ADV_WRITE_DATA; - } - UINT8_TO_STREAM (pp, data_len); if (p_data != NULL && data_len > 0){ @@ -1361,6 +1361,10 @@ UINT8 btsnd_hcic_ble_set_ext_adv_scan_rsp_data(UINT8 adv_handle, HCI_TRACE_EVENT("%s, adv_handle = %d, operation = %d, fragment_prefrence = %d,\n\ data_len = %d", __func__, adv_handle, operation, fragment_prefrence, data_len); + if (data_len > HCIC_PARAM_SIZE_EXT_ADV_WRITE_DATA) { + data_len = HCIC_PARAM_SIZE_EXT_ADV_WRITE_DATA; + } + HCIC_BLE_CMD_CREATED(p, pp, data_len + 4); UINT16_TO_STREAM(pp, HCI_BLE_SET_EXT_SCAN_RSP_DATA); @@ -1369,12 +1373,6 @@ UINT8 btsnd_hcic_ble_set_ext_adv_scan_rsp_data(UINT8 adv_handle, UINT8_TO_STREAM(pp, operation); UINT8_TO_STREAM(pp, fragment_prefrence); - memset(pp, 0, data_len); - - if (data_len > HCIC_PARAM_SIZE_EXT_ADV_WRITE_DATA) { - data_len = HCIC_PARAM_SIZE_EXT_ADV_WRITE_DATA; - } - UINT8_TO_STREAM (pp, data_len); if (p_data != NULL && data_len > 0) { ARRAY_TO_STREAM (pp, p_data, data_len); @@ -1540,6 +1538,10 @@ UINT8 btsnd_hcic_ble_set_periodic_adv_data(UINT8 adv_handle, HCI_TRACE_EVENT("%s, adv_handle = %d, operation = %d, len = %d", __func__, adv_handle, operation, len); + if (len > HCIC_PARAM_SIZE_WRITE_PERIODIC_ADV_DATA) { + len = HCIC_PARAM_SIZE_WRITE_PERIODIC_ADV_DATA; + } + HCIC_BLE_CMD_CREATED(p, pp, len + 3); UINT16_TO_STREAM(pp, HCI_BLE_SET_PERIOD_ADV_DATA); @@ -1547,12 +1549,6 @@ UINT8 btsnd_hcic_ble_set_periodic_adv_data(UINT8 adv_handle, UINT8_TO_STREAM(pp, adv_handle); UINT8_TO_STREAM(pp, operation); - //memset(pp, 0, len); - - if (len > HCIC_PARAM_SIZE_WRITE_PERIODIC_ADV_DATA) { - len = HCIC_PARAM_SIZE_WRITE_PERIODIC_ADV_DATA; - } - UINT8_TO_STREAM (pp, len); if (p_data != NULL && len > 0) { @@ -2268,17 +2264,19 @@ UINT8 btsnd_hcic_ble_big_sync_create(uint8_t big_handle, uint16_t sync_handle, HCI_TRACE_DEBUG("big sync create: big_handle %d sync_handle %d encryption %d mse %d big_sync_timeout %d", big_handle, sync_handle, encryption, mse, big_sync_timeout); - // for (uint8_t i = 0; i < num_bis; i++) - // { - // HCI_TRACE_ERROR("i %d bis %d", bis[i]); - // } + #define HCIC_PARAM_SIZE_BIG_SYNC_CREATE_FIXED 24 + if (num_bis > (HCIC_PARAM_SIZE_BIG_SYNC_CREATE_PARAMS - HCIC_PARAM_SIZE_BIG_SYNC_CREATE_FIXED)) { + HCI_TRACE_ERROR("%s: num_bis %u exceeds max", __func__, num_bis); + return FALSE; + } - HCIC_BLE_CMD_CREATED(p, pp, HCIC_PARAM_SIZE_BIG_SYNC_CREATE_PARAMS); + UINT8 param_size = HCIC_PARAM_SIZE_BIG_SYNC_CREATE_FIXED + num_bis; + HCIC_BLE_CMD_CREATED(p, pp, param_size); pp = (UINT8 *)(p + 1); UINT16_TO_STREAM(pp, HCI_BLE_BIG_CREATE_SYNC); - UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_BIG_SYNC_CREATE_PARAMS); + UINT8_TO_STREAM(pp, param_size); UINT8_TO_STREAM(pp, big_handle); UINT16_TO_STREAM(pp, sync_handle);