qcacmn: Fix possible overread in wifi_pos_parse_req

In wifi_pos_parse_req(), payload sections of nl attributes ATTR_DATA and
ATTR_META_DATA are type casted to driver internal structures tAniMsgHdr
and wifi_pos_field_info respectively without validating payload lengths
which can lead to buffer overread if the payload lengths are less than
size of internal structures.

To fix this, avoid type-cast and return error if payload lengths of nl
attributes ATTR_DATA and ATTR_META_DATA are less than size of tAniMsgHdr
and wifi_pos_field_info respectively.

Change-Id: Ie9e3197f2cd3852b394e834991aa8d3a5b530d85
CRs-Fixed: 2471275
This commit is contained in:
Rajeev Kumar Sirasanagandla
2019-06-26 16:50:00 +05:30
committed by nshrivas
parent a61bf79b15
commit d36ba2d9cf
3 changed files with 70 additions and 20 deletions

View File

@@ -85,6 +85,8 @@ static int wifi_pos_parse_req(const void *data, int len, int pid,
{
tAniMsgHdr *msg_hdr;
struct nlattr *tb[CLD80211_ATTR_MAX + 1];
uint32_t msg_len, id, nl_field_info_size, expected_field_info_size;
struct wifi_pos_field_info *field_info;
if (wlan_cfg80211_nla_parse(tb, CLD80211_ATTR_MAX, data, len, NULL)) {
osif_err("invalid data in request");
@@ -96,23 +98,54 @@ static int wifi_pos_parse_req(const void *data, int len, int pid,
return OEM_ERR_INVALID_MESSAGE_TYPE;
}
msg_hdr = (tAniMsgHdr *)nla_data(tb[CLD80211_ATTR_DATA]);
if (!msg_hdr) {
osif_err("msg_hdr null");
return OEM_ERR_NULL_MESSAGE_HEADER;
msg_len = nla_len(tb[CLD80211_ATTR_DATA]);
if (msg_len < sizeof(*msg_hdr)) {
osif_err("Insufficient length for msg_hdr: %u", msg_len);
return OEM_ERR_INVALID_MESSAGE_LENGTH;
}
msg_hdr = nla_data(tb[CLD80211_ATTR_DATA]);
req->msg_type = msg_hdr->type;
if (msg_len < sizeof(*msg_hdr) + msg_hdr->length) {
osif_err("Insufficient length for msg_hdr buffer: %u",
msg_len);
return OEM_ERR_INVALID_MESSAGE_LENGTH;
}
req->buf_len = msg_hdr->length;
req->buf = (uint8_t *)&msg_hdr[1];
req->pid = pid;
if (tb[CLD80211_ATTR_META_DATA]) {
req->field_info_buf = (struct wifi_pos_field_info *)
nla_data(tb[CLD80211_ATTR_META_DATA]);
req->field_info_buf_len = nla_len(tb[CLD80211_ATTR_META_DATA]);
id = CLD80211_ATTR_META_DATA;
if (!tb[id])
return 0;
nl_field_info_size = nla_len(tb[id]);
if (nl_field_info_size < sizeof(*field_info)) {
osif_err("Insufficient length for field_info_buf: %u",
nl_field_info_size);
return OEM_ERR_INVALID_MESSAGE_LENGTH;
}
field_info = nla_data(tb[id]);
if (!field_info->count) {
osif_debug("field_info->count is zero, ignoring META_DATA");
return 0;
}
expected_field_info_size = sizeof(*field_info) +
(field_info->count - 1) * sizeof(struct wifi_pos_field);
if (nl_field_info_size < expected_field_info_size) {
osif_err("Insufficient len for total no.of %u fields",
field_info->count);
return OEM_ERR_INVALID_MESSAGE_LENGTH;
}
req->field_info_buf = field_info;
req->field_info_buf_len = nl_field_info_size;
return 0;
}
#else
@@ -129,6 +162,12 @@ static int wifi_pos_parse_req(struct sk_buff *skb, struct wifi_pos_req_msg *req)
return OEM_ERR_NULL_MESSAGE_HEADER;
}
if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*msg_hdr))) {
osif_err("nlmsg_len(%d) and msg_hdr_size(%zu) mis-match",
nlh->nlmsg_len, sizeof(*msg_hdr));
return OEM_ERR_INVALID_MESSAGE_LENGTH;
}
msg_hdr = NLMSG_DATA(nlh);
if (!msg_hdr) {
osif_err("Message header null");

View File

@@ -32,6 +32,28 @@
struct wlan_objmgr_psoc;
struct wifi_pos_driver_caps;
/**
* struct wifi_pos_field - wifi positioning field element
* @id: RTT field id
* @offset: data offset in field info buffer
* @length: length of related data in field info buffer
*/
struct wifi_pos_field {
uint32_t id;
uint32_t offset;
uint32_t length;
};
/**
* struct wifi_pos_field_info - wifi positioning field info buffer
* @count: number of @wifi_pos_field elements
* @fields: buffer to hold @wifi_pos_field elements
*/
struct wifi_pos_field_info {
uint32_t count;
struct wifi_pos_field fields[1];
};
#ifdef WIFI_POS_CONVERGED
/**
* enum oem_err_msg - err msg returned to user space

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017 The Linux Foundation. All rights reserved.
* Copyright (c) 2017, 2019 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
@@ -43,17 +43,6 @@
#define WIFI_POS_FLAG_DFS 10
#define WIFI_POS_SET_DFS(info) (info |= (1 << WIFI_POS_FLAG_DFS))
struct wifi_pos_field {
uint32_t id;
uint32_t offset;
uint32_t length;
};
struct wifi_pos_field_info {
uint32_t count;
struct wifi_pos_field fields[1];
};
/**
* enum WMIRTT_FIELD_ID - identifies which field is being specified
* @WMIRTT_FIELD_ID_oem_data_sub_type: oem data req sub type