qcacld-3.0: Parse OCI IE properly to avoid OOB

There is no valid check for oci ie_len buffer while
typecasting, which may lead to OOB access.

Use dot11f_unpack_ie_oci to parse OCI IE to avoid
OOB read.

Change-Id: Iad8cc82072e8d729a4b95bc04c2e8df31e2582f4
CRs-Fixed: 3581129
This commit is contained in:
Aravind Kishore Sukla
2023-08-16 15:39:09 +05:30
committato da Rahul Choudhary
parent 88fe66052d
commit e0181c4873

Vedi File

@@ -64,6 +64,11 @@
(DOT11F_FF_CATEGORY_LEN + DOT11F_FF_ACTION_LEN + DOT11F_FF_TRANSACTIONID_LEN)
#define SA_QUERY_IE_OFFSET (4)
#define MIN_OCI_IE_LEN 6
#define OCI_IE_OUI_SIZE 1
#define OCI_IE_OP_CLS_OFFSET 3
#define ELE_ID_EXT_LEN 1
static last_processed_msg rrm_link_action_frm;
/**-----------------------------------------------------------------
@@ -1222,16 +1227,22 @@ static bool
lim_check_oci_match(struct mac_context *mac, struct pe_session *pe_session,
uint8_t *ie, uint8_t *peer, uint32_t ie_len)
{
const uint8_t *oci_ie;
tDot11fIEoci self_oci, *peer_oci;
const uint8_t *oci_ie, ext_id_param = WLAN_EXTN_ELEMID_OCI;
tDot11fIEoci self_oci, peer_oci = {0};
uint16_t peer_chan_width;
uint16_t local_peer_chan_width = 0;
uint8_t country_code[CDS_COUNTRY_CODE_LEN + 1];
uint32_t status = DOT11F_PARSE_SUCCESS;
if (!lim_is_self_and_peer_ocv_capable(mac, peer, pe_session))
return true;
oci_ie = wlan_get_ie_ptr_from_eid(DOT11F_EID_OCI, ie, ie_len);
if (ie_len < MIN_OCI_IE_LEN)
return false;
oci_ie = wlan_get_ext_ie_ptr_from_ext_id(&ext_id_param,
OCI_IE_OUI_SIZE,
ie, ie_len);
if (!oci_ie) {
pe_err("OCV not found OCI in SA Query frame!");
return false;
@@ -1245,29 +1256,35 @@ lim_check_oci_match(struct mac_context *mac, struct pe_session *pe_session,
* Primary channel : 1 byte
* Freq_seg_1_ch_num : 1 byte
*/
peer_oci = (tDot11fIEoci *)&oci_ie[2];
status = dot11f_unpack_ie_oci(mac,
(uint8_t *)&oci_ie[OCI_IE_OP_CLS_OFFSET],
oci_ie[SIR_MAC_IE_LEN_OFFSET] -
ELE_ID_EXT_LEN,
&peer_oci, false);
if (!DOT11F_SUCCEEDED(status) || !peer_oci.present)
return false;
wlan_reg_read_current_country(mac->psoc, country_code);
peer_chan_width =
wlan_reg_dmn_get_chanwidth_from_opclass_auto(
country_code,
peer_oci->prim_ch_num,
peer_oci->op_class);
peer_oci.prim_ch_num,
peer_oci.op_class);
lim_fill_oci_params(mac, pe_session, &self_oci, peer,
&local_peer_chan_width);
if (((self_oci.op_class != peer_oci->op_class) &&
(local_peer_chan_width > peer_chan_width)) ||
(self_oci.prim_ch_num != peer_oci->prim_ch_num) ||
(self_oci.freq_seg_1_ch_num != peer_oci->freq_seg_1_ch_num)) {
if ((self_oci.op_class != peer_oci.op_class &&
local_peer_chan_width > peer_chan_width) ||
self_oci.prim_ch_num != peer_oci.prim_ch_num ||
self_oci.freq_seg_1_ch_num != peer_oci.freq_seg_1_ch_num) {
pe_err("OCI mismatch,self %d %d %d %d, peer %d %d %d %d",
self_oci.op_class,
self_oci.prim_ch_num,
self_oci.freq_seg_1_ch_num,
local_peer_chan_width,
peer_oci->op_class,
peer_oci->prim_ch_num,
peer_oci->freq_seg_1_ch_num,
peer_oci.op_class,
peer_oci.prim_ch_num,
peer_oci.freq_seg_1_ch_num,
peer_chan_width);
return false;
}