From fec800ab539956671af604bdd6e1ee3b84eef491 Mon Sep 17 00:00:00 2001 From: Pooventhiran G Date: Sun, 11 Aug 2024 23:54:12 +0530 Subject: [PATCH] qcacmn: Fix out-of-bounds in wlan_mlo_parse_t2lm_info wlan_mlo_parse_t2lm_info() does not check ie_len boundary before accessing optional elements. This could lead to OOB access if presence bit is set but its corresponding sub-field is not present in the frame. Add the necessary boundary checks before accessing optional sub-fields when its presence bit is set. Change-Id: Icfc079c460e5ad3382507c11b60ef6541e9baf5e CRs-Fixed: 3895196 --- umac/mlo_mgr/src/wlan_mlo_t2lm.c | 74 +++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/umac/mlo_mgr/src/wlan_mlo_t2lm.c b/umac/mlo_mgr/src/wlan_mlo_t2lm.c index 2770dc7e79..4b7a865490 100644 --- a/umac/mlo_mgr/src/wlan_mlo_t2lm.c +++ b/umac/mlo_mgr/src/wlan_mlo_t2lm.c @@ -42,8 +42,26 @@ QDF_STATUS wlan_mlo_parse_t2lm_info(uint8_t *ie, uint8_t *link_mapping_of_tids; uint8_t tid_num; uint8_t *ie_ptr = NULL; + uint32_t ie_len_parsed = 0; + uint32_t ie_len = 0; + + if (!ie || !t2lm) { + t2lm_err("IE buffer is null"); + return QDF_STATUS_E_NULL_VALUE; + } t2lm_ie = (struct wlan_ie_tid_to_link_mapping *)ie; + ie_len = t2lm_ie->elem_len; + + /* Minimum IE length is 2 bytes: + * elem_id_extn is 1 byte + * t2lm_control_field can be of minimum 1 byte + */ + if (ie_len < WLAN_T2LM_CTRL_SIZE) { + t2lm_debug("T2LM IE min length (%u) is invalid", ie_len); + return QDF_STATUS_E_PROTO; + } + ie_len_parsed++; t2lm_control_field = t2lm_ie->data; if (!t2lm_control_field) { @@ -51,7 +69,9 @@ QDF_STATUS wlan_mlo_parse_t2lm_info(uint8_t *ie, return QDF_STATUS_E_NULL_VALUE; } - t2lm_control = qdf_le16_to_cpu(*(uint16_t *)t2lm_control_field); + t2lm_control = *t2lm_control_field; + if (ie_len > WLAN_T2LM_CTRL_SIZE) + t2lm_control = qdf_le16_to_cpu(*(uint16_t *)t2lm_control_field); dir = QDF_GET_BITS(t2lm_control, WLAN_T2LM_CONTROL_DIRECTION_IDX, WLAN_T2LM_CONTROL_DIRECTION_BITS); @@ -88,8 +108,20 @@ QDF_STATUS wlan_mlo_parse_t2lm_info(uint8_t *ie, t2lm->link_mapping_size); if (t2lm->default_link_mapping) { + if (ie_len < (ie_len_parsed + sizeof(uint8_t))) { + t2lm_rl_debug("Failed to parse Default link mapping=1"); + return QDF_STATUS_E_PROTO; + } + ie_len_parsed += sizeof(uint8_t); + ie_ptr = t2lm_control_field + sizeof(uint8_t); } else { + if (ie_len < (ie_len_parsed + sizeof(t2lm_control))) { + t2lm_rl_debug("Failed to parse Default link mapping=0"); + return QDF_STATUS_E_PROTO; + } + ie_len_parsed += sizeof(t2lm_control); + link_mapping_presence_ind = QDF_GET_BITS(t2lm_control, WLAN_T2LM_CONTROL_LINK_MAPPING_PRESENCE_INDICATOR_IDX, @@ -98,12 +130,25 @@ QDF_STATUS wlan_mlo_parse_t2lm_info(uint8_t *ie, } if (t2lm->mapping_switch_time_present) { + if (ie_len < (ie_len_parsed + sizeof(uint16_t))) { + t2lm_rl_debug("Failed to parse Mapping switch time"); + return QDF_STATUS_E_PROTO; + } + ie_len_parsed += sizeof(uint16_t); + t2lm->mapping_switch_time = qdf_le16_to_cpu(*(uint16_t *)ie_ptr); ie_ptr += sizeof(uint16_t); } if (t2lm->expected_duration_present) { + if (ie_len < + (ie_len_parsed + WLAN_T2LM_EXPECTED_DURATION_SIZE)) { + t2lm_rl_debug("Failed to parse Expected duration"); + return QDF_STATUS_E_PROTO; + } + ie_len_parsed += WLAN_T2LM_EXPECTED_DURATION_SIZE; + qdf_mem_copy(&t2lm->expected_duration, ie_ptr, WLAN_T2LM_EXPECTED_DURATION_SIZE * (sizeof(uint8_t))); @@ -113,8 +158,16 @@ QDF_STATUS wlan_mlo_parse_t2lm_info(uint8_t *ie, t2lm_debug("mapping_switch_time:%d expected_duration:%d", t2lm->mapping_switch_time, t2lm->expected_duration); - if (t2lm->default_link_mapping) + if (t2lm->default_link_mapping) { + /* With default link mapping set to 1, there is no + * `Link Mapping of Tid n` field present. + */ + if (ie_len > ie_len_parsed) { + t2lm_rl_debug("Link mapping of TID present when default link mapping is set"); + return QDF_STATUS_E_PROTO; + } return QDF_STATUS_SUCCESS; + } link_mapping_of_tids = ie_ptr; @@ -123,10 +176,22 @@ QDF_STATUS wlan_mlo_parse_t2lm_info(uint8_t *ie, continue; if (!t2lm->link_mapping_size) { + if (ie_len < (ie_len_parsed + sizeof(uint16_t))) { + t2lm_rl_debug("Failed to parse Link mapping for tid=%u", tid_num); + return QDF_STATUS_E_PROTO; + } + ie_len_parsed += sizeof(uint16_t); + t2lm->ieee_link_map_tid[tid_num] = qdf_le16_to_cpu(*(uint16_t *)link_mapping_of_tids); link_mapping_of_tids += sizeof(uint16_t); } else { + if (ie_len < (ie_len_parsed + sizeof(uint8_t))) { + t2lm_rl_debug("Failed to parse Link mapping for tid=%u", tid_num); + return QDF_STATUS_E_PROTO; + } + ie_len_parsed += sizeof(uint8_t); + t2lm->ieee_link_map_tid[tid_num] = *(uint8_t *)link_mapping_of_tids; link_mapping_of_tids += sizeof(uint8_t); @@ -136,6 +201,11 @@ QDF_STATUS wlan_mlo_parse_t2lm_info(uint8_t *ie, t2lm->ieee_link_map_tid[tid_num]); } + if (ie_len > ie_len_parsed) { + t2lm_rl_debug("More data present at the end of T2LM element"); + return QDF_STATUS_E_PROTO; + } + return QDF_STATUS_SUCCESS; }