soc: qcom: qmi_encdec: out of bound check for input buffer

Data shared by remote processors can not be trusted.
QMI message could be malformed which can result in decoded
bytes greater than length of input buffer supplied causing
buffer overflow.

Check decoded bytes against buffer length to avoid buffer
overflow.

Change-Id: I1d2d3aadd297718b8ecc023a20475b60f4bce022
Signed-off-by: Deepak Kumar Singh <quic_deesin@quicinc.com>
This commit is contained in:
Deepak Kumar Singh 2023-08-15 22:06:23 +05:30 committed by Sarannya S
parent 253ff03050
commit ff6fb61c78

View File

@ -427,6 +427,7 @@ static int qmi_encode(struct qmi_elem_info *ei_array, void *out_buf,
* @buf_src: Buffer containing the elements in QMI wire format.
* @elem_len: Number of elements to be decoded.
* @elem_size: Size of a single instance of the element to be decoded.
* @src_len: Source buffer length.
*
* This function decodes the "elem_len" number of elements in QMI wire format,
* each of size "elem_size" bytes from the source buffer "buf_src" and stores
@ -437,10 +438,13 @@ static int qmi_encode(struct qmi_elem_info *ei_array, void *out_buf,
* Return: The total size of the decoded data elements, in bytes.
*/
static int qmi_decode_basic_elem(void *buf_dst, const void *buf_src,
u32 elem_len, u32 elem_size)
u32 elem_len, u32 elem_size, u32 src_len)
{
u32 i, rc = 0;
if (elem_len * elem_size > src_len)
return -EINVAL;
for (i = 0; i < elem_len; i++) {
QMI_ENCDEC_DECODE_N_BYTES(buf_dst, buf_src, elem_size);
rc += elem_size;
@ -458,6 +462,7 @@ static int qmi_decode_basic_elem(void *buf_dst, const void *buf_src,
* @tlv_len: Total size of the encoded inforation corresponding to
* this struct element.
* @dec_level: Depth of the nested structure from the main structure.
* @src_len: Source buffer length.
*
* This function decodes the "elem_len" number of elements in QMI wire format,
* each of size "(tlv_len/elem_len)" bytes from the source buffer "buf_src"
@ -471,16 +476,20 @@ static int qmi_decode_basic_elem(void *buf_dst, const void *buf_src,
static int qmi_decode_struct_elem(struct qmi_elem_info *ei_array,
void *buf_dst, const void *buf_src,
u32 elem_len, u32 tlv_len,
int dec_level)
int dec_level, u32 src_len)
{
int i, rc, decoded_bytes = 0;
struct qmi_elem_info *temp_ei = ei_array;
if (tlv_len > src_len)
return -EINVAL;
for (i = 0; i < elem_len && decoded_bytes < tlv_len; i++) {
rc = qmi_decode(temp_ei->ei_array, buf_dst, buf_src,
tlv_len - decoded_bytes, dec_level);
if (rc < 0)
return rc;
buf_src = buf_src + rc;
buf_dst = buf_dst + temp_ei->elem_size;
decoded_bytes += rc;
@ -505,6 +514,7 @@ static int qmi_decode_struct_elem(struct qmi_elem_info *ei_array,
* @tlv_len: Total size of the encoded inforation corresponding to
* this string element.
* @dec_level: Depth of the string element from the main structure.
* @src_len: Source buffer length.
*
* This function decodes the string element of maximum length
* "ei_array->elem_len" from the source buffer "buf_src" and puts it into
@ -516,7 +526,7 @@ static int qmi_decode_struct_elem(struct qmi_elem_info *ei_array,
*/
static int qmi_decode_string_elem(struct qmi_elem_info *ei_array,
void *buf_dst, const void *buf_src,
u32 tlv_len, int dec_level)
u32 tlv_len, int dec_level, u32 src_len)
{
int rc;
int decoded_bytes = 0;
@ -530,7 +540,10 @@ static int qmi_decode_string_elem(struct qmi_elem_info *ei_array,
string_len_sz = temp_ei->elem_len <= U8_MAX ?
sizeof(u8) : sizeof(u16);
rc = qmi_decode_basic_elem(&string_len, buf_src,
1, string_len_sz);
1, string_len_sz, src_len);
if (rc < 0)
return rc;
decoded_bytes += rc;
}
@ -545,7 +558,11 @@ static int qmi_decode_string_elem(struct qmi_elem_info *ei_array,
}
rc = qmi_decode_basic_elem(buf_dst, buf_src + decoded_bytes,
string_len, temp_ei->elem_size);
string_len, temp_ei->elem_size,
src_len - decoded_bytes);
if (rc < 0)
return rc;
*((char *)buf_dst + string_len) = '\0';
decoded_bytes += rc;
@ -611,6 +628,9 @@ static int qmi_decode(struct qmi_elem_info *ei_array, void *out_c_struct,
if (dec_level == 1) {
tlv_pointer = buf_src;
if (decoded_bytes + TLV_TYPE_SIZE + TLV_LEN_SIZE > in_buf_len)
return -EINVAL;
QMI_ENCDEC_DECODE_TLV(&tlv_type,
&tlv_len, tlv_pointer);
buf_src += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
@ -643,7 +663,11 @@ static int qmi_decode(struct qmi_elem_info *ei_array, void *out_c_struct,
data_len_sz = temp_ei->elem_size == sizeof(u8) ?
sizeof(u8) : sizeof(u16);
rc = qmi_decode_basic_elem(&data_len_value, buf_src,
1, data_len_sz);
1, data_len_sz,
in_buf_len - decoded_bytes);
if (rc < 0)
return rc;
memcpy(buf_dst, &data_len_value, sizeof(u32));
temp_ei = temp_ei + 1;
buf_dst = out_c_struct + temp_ei->offset;
@ -670,24 +694,32 @@ static int qmi_decode(struct qmi_elem_info *ei_array, void *out_c_struct,
case QMI_SIGNED_4_BYTE_ENUM:
rc = qmi_decode_basic_elem(buf_dst, buf_src,
data_len_value,
temp_ei->elem_size);
temp_ei->elem_size,
in_buf_len - decoded_bytes);
if (rc < 0)
return rc;
UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
break;
case QMI_STRUCT:
rc = qmi_decode_struct_elem(temp_ei, buf_dst, buf_src,
data_len_value, tlv_len,
dec_level + 1);
dec_level + 1,
in_buf_len - decoded_bytes);
if (rc < 0)
return rc;
UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
break;
case QMI_STRING:
rc = qmi_decode_string_elem(temp_ei, buf_dst, buf_src,
tlv_len, dec_level);
tlv_len, dec_level,
in_buf_len - decoded_bytes);
if (rc < 0)
return rc;
UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
break;