Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b59b74fe authored by Deepak Kumar Singh's avatar Deepak Kumar Singh Committed by Sarannya S
Browse files

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: default avatarDeepak Kumar Singh <quic_deesin@quicinc.com>
parent 30a9d3b2
Loading
Loading
Loading
Loading
+42 −9
Original line number Diff line number Diff line
@@ -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,10 @@ 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 +664,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 +695,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;