lib: qmi: Add check to avoid out of bound memory access

If QMI clients pass an invalid maximum message length parameter, the
QMI message encode operation may perform an out of bound encode buffer
access.

Add a check in the QMI message encode operation to avoid out of bound
buffer access.

CRs-Fixed: 437329
Change-Id: I9d16078fade8160f327a8a77aea6c2a8e66b3127
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
diff --git a/lib/qmi_encdec.c b/lib/qmi_encdec.c
index 40273d0..5c489cf 100644
--- a/lib/qmi_encdec.c
+++ b/lib/qmi_encdec.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -82,7 +82,7 @@
 
 static int _qmi_kernel_encode(struct elem_info *ei_array,
 			      void *out_buf, void *in_c_struct,
-			      int enc_level);
+			      uint32_t out_buf_len, int enc_level);
 
 static int _qmi_kernel_decode(struct elem_info *ei_array,
 			      void *out_c_struct,
@@ -114,7 +114,7 @@
 		return -ETOOSMALL;
 
 	return _qmi_kernel_encode(desc->ei_array, out_buf,
-				  in_c_struct, enc_level);
+				  in_c_struct, out_buf_len, enc_level);
 }
 EXPORT_SYMBOL(qmi_kernel_encode);
 
@@ -152,6 +152,7 @@
  * @buf_dst: Buffer to store the encoded information.
  * @buf_src: Buffer containing the elements to be encoded.
  * @elem_len: Number of elements, in the buf_src, to be encoded.
+ * @out_buf_len: Available space in the encode buffer.
  * @enc_level: Depth of the nested structure from the main structure.
  *
  * @return: Mumber of bytes of encoded information, on success.
@@ -165,14 +166,16 @@
  */
 static int qmi_encode_struct_elem(struct elem_info *ei_array,
 				  void *buf_dst, void *buf_src,
-				  uint32_t elem_len, int enc_level)
+				  uint32_t elem_len, uint32_t out_buf_len,
+				  int enc_level)
 {
 	int i, rc, encoded_bytes = 0;
 	struct elem_info *temp_ei = ei_array;
 
 	for (i = 0; i < elem_len; i++) {
-		rc = _qmi_kernel_encode(temp_ei->ei_array,
-					buf_dst, buf_src, enc_level);
+		rc = _qmi_kernel_encode(temp_ei->ei_array, buf_dst, buf_src,
+					(out_buf_len - encoded_bytes),
+					enc_level);
 		if (rc < 0) {
 			pr_err("%s: STRUCT Encode failure\n", __func__);
 			return rc;
@@ -214,6 +217,7 @@
  * @ei_array: Struct info array describing the structure to be encoded.
  * @out_buf: Buffer to hold the encoded QMI message.
  * @in_c_struct: Pointer to the C structure to be encoded.
+ * @out_buf_len: Available space in the encode buffer.
  * @enc_level: Encode level to indicate the depth of the nested structure,
  *             within the main structure, being encoded.
  *
@@ -222,7 +226,7 @@
  */
 static int _qmi_kernel_encode(struct elem_info *ei_array,
 			      void *out_buf, void *in_c_struct,
-			      int enc_level)
+			      uint32_t out_buf_len, int enc_level)
 {
 	struct elem_info *temp_ei = ei_array;
 	uint8_t opt_flag_value = 0;
@@ -268,6 +272,13 @@
 			memcpy(&data_len_value, buf_src, temp_ei->elem_size);
 			data_len_sz = temp_ei->elem_size == sizeof(uint8_t) ?
 					sizeof(uint8_t) : sizeof(uint16_t);
+			/* Check to avoid out of range buffer access */
+			if ((data_len_sz + encoded_bytes + TLV_LEN_SIZE +
+			    TLV_TYPE_SIZE) > out_buf_len) {
+				pr_err("%s: Too Small Buffer @DATA_LEN\n",
+					__func__);
+				return -ETOOSMALL;
+			}
 			rc = qmi_encode_basic_elem(buf_dst, &data_len_value,
 						   1, data_len_sz);
 			if (data_len_value) {
@@ -285,6 +296,14 @@
 		case QMI_UNSIGNED_8_BYTE:
 		case QMI_SIGNED_2_BYTE_ENUM:
 		case QMI_SIGNED_4_BYTE_ENUM:
+			/* Check to avoid out of range buffer access */
+			if (((data_len_value * temp_ei->elem_size) +
+			    encoded_bytes + TLV_LEN_SIZE + TLV_TYPE_SIZE) >
+			    out_buf_len) {
+				pr_err("%s: Too Small Buffer @data_type:%d\n",
+					__func__, temp_ei->data_type);
+				return -ETOOSMALL;
+			}
 			rc = qmi_encode_basic_elem(buf_dst, buf_src,
 				data_len_value, temp_ei->elem_size);
 			QMI_ENCODE_LOG_ELEM(enc_level, data_len_value,
@@ -295,7 +314,8 @@
 
 		case QMI_STRUCT:
 			rc = qmi_encode_struct_elem(temp_ei, buf_dst, buf_src,
-				data_len_value, (enc_level + 1));
+				data_len_value, (out_buf_len - encoded_bytes),
+				(enc_level + 1));
 			if (rc < 0)
 				return rc;
 			UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,