qcacmn: Add sanity check for wmi TLV length

Add sanity check for wmi TLV header length before padding/shrinking
elements in a wmi which has a variable length for its TLV structure.
Currently, the TLV length is not checked so its maximum value could
be 65535 which results in a hugh count for elements. Number of elements
is used to terminate the loop for padding/shrinking. If the number
was too large, there would be memory overflow.

Change-Id: Iea0615fc511696c6cc5dcc48a9dfff225256a52b
CRs-Fixed: 2181685
diff --git a/wmi_tlv_helper.c b/wmi_tlv_helper.c
index c282ef3..48031de 100644
--- a/wmi_tlv_helper.c
+++ b/wmi_tlv_helper.c
@@ -507,6 +507,7 @@
 	wmitlv_cmd_param_info *cmd_param_tlvs_ptr = NULL;
 	A_UINT32 remaining_expected_tlvs = 0xFFFFFFFF;
 	A_UINT32 len_wmi_cmd_struct_buf;
+	A_UINT32 free_buf_len;
 	A_INT32 error = -1;
 
 	/* Get the number of TLVs for this command/event */
@@ -569,6 +570,13 @@
 			WMITLV_GET_TLVLEN(WMITLV_GET_HDR(buf_ptr));
 		int num_padding_bytes = 0;
 
+		free_buf_len = param_buf_len - (buf_idx + WMI_TLV_HDR_SIZE);
+		if (curr_tlv_len > free_buf_len) {
+			wmi_tlv_print_error("%s: TLV length overflow",
+					    __func__);
+			goto Error_wmitlv_check_and_pad_tlvs;
+		}
+
 		/* Get the attributes of the TLV with the given order in "tlv_index" */
 		wmi_tlv_OS_MEMZERO(&attr_struct_ptr,
 				   sizeof(wmitlv_attributes_struc));
@@ -632,6 +640,13 @@
 						WMITLV_GET_TLVLEN(WMITLV_GET_HDR
 									  (buf_ptr));
 					in_tlv_len += WMI_TLV_HDR_SIZE;
+					if (in_tlv_len > curr_tlv_len) {
+						wmi_tlv_print_error("%s: Invalid in_tlv_len=%d",
+								    __func__,
+								    in_tlv_len);
+						goto
+						Error_wmitlv_check_and_pad_tlvs;
+					}
 					tlv_size_diff =
 						in_tlv_len -
 						attr_struct_ptr.tag_struct_size;
@@ -751,8 +766,17 @@
 
 					/* Move subsequent TLVs by number of bytes to be padded
 					 * for all elements */
-					if (param_buf_len >
-					    (buf_idx + curr_tlv_len)) {
+					if ((free_buf_len <
+					    attr_struct_ptr.tag_struct_size *
+					    num_of_elems) ||
+					    (param_buf_len <
+					    buf_idx + curr_tlv_len +
+					    num_padding_bytes * num_of_elems)) {
+						wmi_tlv_print_error("%s: Insufficent buffer\n",
+								    __func__);
+						goto
+						Error_wmitlv_check_and_pad_tlvs;
+					} else {
 						src_addr =
 							buf_ptr + curr_tlv_len;
 						dst_addr =
@@ -772,12 +796,10 @@
 					 * bytes to be padded for one element and alse set
 					 * padding bytes to zero */
 					tlv_buf_ptr = buf_ptr;
-					for (i = 0; i < num_of_elems; i++) {
+					for (i = 0; i < num_of_elems - 1; i++) {
 						src_addr =
 							tlv_buf_ptr + in_tlv_len;
 						if (i != (num_of_elems - 1)) {
-							/* Need not move anything for last element
-							 * in the array */
 							dst_addr =
 								tlv_buf_ptr +
 								in_tlv_len +
@@ -800,6 +822,9 @@
 							attr_struct_ptr.
 							tag_struct_size;
 					}
+					src_addr = tlv_buf_ptr + in_tlv_len;
+					wmi_tlv_OS_MEMZERO(src_addr,
+							   num_padding_bytes);
 
 					/* Update the number of padding bytes to total number
 					 * of bytes padded for all elements in the array */