avrcp: avoid out of bound reads in target and controller

This commit is contained in:
Matthias Ringwald 2021-02-26 00:12:25 +01:00
parent 39ac97114d
commit 99702381fb
2 changed files with 55 additions and 31 deletions

View File

@ -87,9 +87,12 @@ static int avrcp_controller_supports_browsing(uint16_t controller_supported_feat
return controller_supported_features & AVRCP_FEATURE_MASK_BROWSING;
}
static void avrcp_controller_emit_notification_for_event_id(uint16_t avrcp_cid, avrcp_notification_event_id_t event_id, avrcp_command_type_t ctype, const uint8_t * payload){
static void avrcp_controller_emit_notification_for_event_id(uint16_t avrcp_cid, avrcp_notification_event_id_t event_id,
avrcp_command_type_t ctype, const uint8_t *payload,
uint16_t size) {
switch (event_id){
case AVRCP_NOTIFICATION_EVENT_PLAYBACK_POS_CHANGED:{
if (size < 4) break;
uint32_t song_position = big_endian_read_32(payload, 0);
uint16_t offset = 0;
uint8_t event[10];
@ -105,6 +108,7 @@ static void avrcp_controller_emit_notification_for_event_id(uint16_t avrcp_cid,
break;
}
case AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED:{
if (size < 1) break;
uint16_t offset = 0;
uint8_t event[7];
event[offset++] = HCI_EVENT_AVRCP_META;
@ -154,6 +158,7 @@ static void avrcp_controller_emit_notification_for_event_id(uint16_t avrcp_cid,
break;
}
case AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED:{
if (size < 1) break;
uint16_t offset = 0;
uint8_t event[7];
event[offset++] = HCI_EVENT_AVRCP_META;
@ -167,6 +172,7 @@ static void avrcp_controller_emit_notification_for_event_id(uint16_t avrcp_cid,
break;
}
case AVRCP_NOTIFICATION_EVENT_UIDS_CHANGED:{
if (size < 2) break;
uint8_t event[8];
uint16_t offset = 0;
uint16_t uuid = big_endian_read_16(payload, 0);
@ -207,6 +213,7 @@ static void avrcp_controller_emit_notification_for_event_id(uint16_t avrcp_cid,
break;
}
case AVRCP_NOTIFICATION_EVENT_BATT_STATUS_CHANGED:{
if (size < 1) break;
uint16_t offset = 0;
uint8_t event[7];
event[offset++] = HCI_EVENT_AVRCP_META;
@ -221,6 +228,7 @@ static void avrcp_controller_emit_notification_for_event_id(uint16_t avrcp_cid,
}
case AVRCP_NOTIFICATION_EVENT_SYSTEM_STATUS_CHANGED:{
if (size < 1) break;
uint16_t offset = 0;
uint8_t event[7];
event[offset++] = HCI_EVENT_AVRCP_META;
@ -644,7 +652,9 @@ static uint8_t avrcp_controller_request_continue_response(avrcp_connection_t * c
return ERROR_CODE_SUCCESS;
}
static void avrcp_controller_handle_notification(avrcp_connection_t * connection, avrcp_command_type_t ctype, uint8_t * payload){
static void
avrcp_controller_handle_notification(avrcp_connection_t *connection, avrcp_command_type_t ctype, uint8_t *payload, uint16_t size) {
if (size < 1) return;
uint16_t pos = 0;
avrcp_notification_event_id_t event_id = (avrcp_notification_event_id_t) payload[pos++];
uint16_t event_mask = (1 << event_id);
@ -671,14 +681,13 @@ static void avrcp_controller_handle_notification(avrcp_connection_t * connection
connection->notifications_to_deregister &= reset_event_mask;
break;
}
avrcp_controller_emit_notification_for_event_id(connection->avrcp_cid, event_id, ctype, payload+pos);
avrcp_controller_emit_notification_for_event_id(connection->avrcp_cid, event_id, ctype, payload + pos, size - pos);
}
static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connection_t * connection, uint8_t *packet, uint16_t size){
if (size < 6u) return;
uint16_t pos = 3;
uint8_t pdu_id;
uint8_t vendor_dependent_packet_type;
@ -695,7 +704,8 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
log_info("Fragmentation is not supported");
return;
}
uint16_t pos = 3;
avrcp_command_type_t ctype = (avrcp_command_type_t) packet[pos++];
#ifdef ENABLE_LOG_INFO
@ -739,6 +749,9 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
break;
}
case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT:
if ((size - pos) < 7) return;
// Company ID (3)
pos += 3;
pdu_id = packet[pos++];
@ -747,14 +760,11 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
pos += 2;
log_info("operands length %d, remaining size %d", param_length, size - pos);
if ((size - pos) < param_length) {
log_error("Wrong packet size %d < %d", size - pos, param_length);
return;
};
if ((size - pos) < param_length) return;
// handle asynchronous notifications, without changing state
if (pdu_id == AVRCP_PDU_ID_REGISTER_NOTIFICATION){
avrcp_controller_handle_notification(connection, ctype, packet+pos);
avrcp_controller_handle_notification(connection, ctype, packet + pos, size - pos);
break;
}
@ -955,6 +965,7 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
}
break;
case AVRCP_CMD_OPCODE_PASS_THROUGH:{
if ((size - pos) < 1) return;
uint8_t operation_id = packet[pos++];
switch (connection->state){
case AVCTP_W2_RECEIVE_PRESS_RESPONSE:

View File

@ -789,9 +789,7 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
avrcp_subunit_id_t subunit_id = (avrcp_subunit_id_t) (packet[4] & 0x07);
avrcp_command_opcode_t opcode = (avrcp_command_opcode_t) avrcp_cmd_opcode(packet,size);
uint8_t * company_id = avrcp_get_company_id(packet, size);
uint8_t * pdu = avrcp_get_pdu(packet, size);
int pos = 4;
uint16_t length;
avrcp_pdu_id_t pdu_id;
@ -802,11 +800,13 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
avrcp_target_unit_info(connection);
break;
case AVRCP_CMD_OPCODE_SUBUNIT_INFO:{
if ((size - pos) < 3) return;
uint8_t offset = 4 * (packet[pos+2]>>4);
avrcp_target_subunit_info(connection, offset);
break;
}
case AVRCP_CMD_OPCODE_PASS_THROUGH:{
if (size < 9) return;
log_info("AVRCP_OPERATION_ID 0x%02x, operands length %d, operand %d", packet[6], packet[7], packet[8]);
avrcp_operation_id_t operation_id = (avrcp_operation_id_t) packet[6];
@ -850,17 +850,27 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
}
case AVRCP_CMD_OPCODE_VENDOR_DEPENDENT:
pdu_id = (avrcp_pdu_id_t) pdu[0];
// 1 - reserved
// 2-3 param length,
length = big_endian_read_16(pdu, 2);
(void)memcpy(connection->cmd_operands, company_id, 3);
if (size < 13) return;
// pos = 6 - company id
(void)memcpy(connection->cmd_operands, &packet[pos], 3);
connection->cmd_operands_length = 3;
pos += 3;
// pos = 9
pdu_id = (avrcp_pdu_id_t) packet[pos++];
// 1 - reserved
pos++;
// 2-3 param length,
length = big_endian_read_16(packet, pos);
pos += 2;
// pos = 13
switch (pdu_id){
case AVRCP_PDU_ID_SET_ADDRESSED_PLAYER:{
if ((pos + 2) > size) return;
bool ok = length == 4;
if (avrcp_target_context.set_addressed_player_callback != NULL){
uint16_t player_id = big_endian_read_16(pdu, 4);
uint16_t player_id = big_endian_read_16(packet, pos);
ok = avrcp_target_context.set_addressed_player_callback(player_id);
}
if (ok){
@ -871,7 +881,7 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
break;
}
case AVRCP_PDU_ID_GET_CAPABILITIES:{
avrcp_capability_id_t capability_id = (avrcp_capability_id_t) pdu[pos];
avrcp_capability_id_t capability_id = (avrcp_capability_id_t) packet[pos];
switch (capability_id){
case AVRCP_CAPABILITY_ID_EVENT:
avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_EVENT_IDS_QUERY);
@ -889,12 +899,14 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
avrcp_target_emit_respond_vendor_dependent_query(avrcp_target_context.avrcp_callback, connection->avrcp_cid, AVRCP_SUBEVENT_PLAY_STATUS_QUERY);
break;
case AVRCP_PDU_ID_REQUEST_ABORT_CONTINUING_RESPONSE:
connection->cmd_operands[0] = pdu[4];
if ((pos + 1) > size) return;
connection->cmd_operands[0] = packet[pos];
connection->abort_continue_response = 1;
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
break;
case AVRCP_PDU_ID_REQUEST_CONTINUING_RESPONSE:
if (pdu[4] != AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES){
if ((pos + 1) > size) return;
if (packet[pos] != AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES){
avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_COMMAND);
return;
}
@ -902,22 +914,24 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
avrcp_request_can_send_now(connection, connection->l2cap_signaling_cid);
break;
case AVRCP_PDU_ID_GET_ELEMENT_ATTRIBUTES:{
if ((pos + 9) > size) return;
uint8_t play_identifier[8];
memset(play_identifier, 0, 8);
if (memcmp(pdu+pos, play_identifier, 8) != 0) {
if (memcmp(&packet[pos], play_identifier, 8) != 0) {
avrcp_target_response_reject(connection, subunit_type, subunit_id, opcode, pdu_id, AVRCP_STATUS_INVALID_PARAMETER);
return;
}
pos += 8;
uint8_t attribute_count = pdu[pos++];
uint8_t attribute_count = packet[pos++];
connection->next_attr_id = AVRCP_MEDIA_ATTR_NONE;
if (!attribute_count){
connection->now_playing_info_attr_bitmap = 0xFE;
} else {
int i;
connection->now_playing_info_attr_bitmap = 0;
if ((pos + attribute_count * 2) > size) return;
for (i=0; i < attribute_count; i++){
uint16_t attr_id = big_endian_read_16(pdu, pos);
uint16_t attr_id = big_endian_read_16(packet, pos);
pos += 2;
connection->now_playing_info_attr_bitmap |= (1 << attr_id);
}
@ -927,10 +941,8 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
break;
}
case AVRCP_PDU_ID_REGISTER_NOTIFICATION:{
// 0 - pdu id
// 1 - reserved
// 2-3 param length
avrcp_notification_event_id_t event_id = (avrcp_notification_event_id_t) pdu[4];
if ((pos + 1) > size) return;
avrcp_notification_event_id_t event_id = (avrcp_notification_event_id_t) packet[pos];
uint16_t event_mask = (1 << event_id);
avrcp_target_set_transaction_label_for_notification(connection, event_id, connection->transaction_id);
@ -986,7 +998,8 @@ static void avrcp_handle_l2cap_data_packet_for_signaling_connection(avrcp_connec
break;
}
uint8_t absolute_volume = pdu[4];
if ((pos + 1) > size) return;
uint8_t absolute_volume = packet[pos];
if (absolute_volume < 0x80){
connection->volume_percentage = absolute_volume;
}