qcacld-3.0: Fix mem leak in wma_roam_synch_frame_event_handler

The function wma_roam_synch_frame_event_handler, memory is
allocated for iface->roam_synch_frame_ind.bcn_probe_rsp,
iface->roam_synch_frame_ind.bcn_reassoc_req,
iface->roam_synch_frame_ind.bcn_reassoc_rsp when the wmi event
WMI_ROAM_SYNCH_FRAME_EVENT is received. This event is followed
by a WMI_ROAM_SYNCH_EVENT from the firmware where the host
copies the bcn_probe_rsp, bcn_reassoc_req, bcn_reassoc_rsp to
the structure roam_synch_ind_ptr and frees the allocated memory.
In this flow memory leak can happen in following cases:
 1. Firmware sends multiple cascade of WMI_ROAM_SYNCH_FRAME_EVENT
the host allocates bcn_reassoc_req, bcn_reassoc_rsp and
bcn_probe_rsp with out freeing the previous instance.
 2. Firmware sends WMI_ROAM_SYNCH_FRAME_EVENT with either
bcn_reassoc_req or bcn_reassoc_req or bcn_probe_rsp NULL or all
the three are NULL.
 3. Firmware sends WMI_ROAM_SYNCH_FRAME_EVENT having
bcn_reassoc_req bcn_reassoc_req and bcn_probe_rsp. Then it sends
the WMI_ROAM_SYNCH_EVENT with non zero bcn_reassoc_req_len or
bcn_reassoc_rsp_len or bcn_probe_rsp length.
 4. Host doesn't free the allocated memory in
wma_roam_synch_frame_event_handler during failure cases.

Check if received iface->roam_synch_frame_ind has non NULL
bcn_probe_rsp, bcn_reassoc_req, bcn_reassoc_rsp and free the
same before allocating new memory. Also free the allocated
bcn_probe_rsp, bcn_reassoc_req, bcn_reassoc_rsp in failure
return cases.

Change-Id: I2b76769d09fd61929f7837cb8661d778cd2f881a
CRs-Fixed: 2282413
diff --git a/core/wma/src/wma_scan_roam.c b/core/wma/src/wma_scan_roam.c
index 5827614..99dab76 100644
--- a/core/wma/src/wma_scan_roam.c
+++ b/core/wma/src/wma_scan_roam.c
@@ -1895,6 +1895,35 @@
 }
 
 /**
+ * wma_free_roam_synch_frame_ind() - Free the bcn_probe_rsp, reassoc_req,
+ * reassoc_rsp received as part of the ROAM_SYNC_FRAME event
+ *
+ * @iface - interaface corresponding to a vdev
+ *
+ * This API is used to free the buffer allocated during the ROAM_SYNC_FRAME
+ * event
+ *
+ */
+static void wma_free_roam_synch_frame_ind(struct wma_txrx_node *iface)
+{
+	if (iface->roam_synch_frame_ind.bcn_probe_rsp) {
+		qdf_mem_free(iface->roam_synch_frame_ind.bcn_probe_rsp);
+		iface->roam_synch_frame_ind.bcn_probe_rsp_len = 0;
+		iface->roam_synch_frame_ind.bcn_probe_rsp = NULL;
+	}
+	if (iface->roam_synch_frame_ind.reassoc_req) {
+		qdf_mem_free(iface->roam_synch_frame_ind.reassoc_req);
+		iface->roam_synch_frame_ind.reassoc_req_len = 0;
+		iface->roam_synch_frame_ind.reassoc_req = NULL;
+	}
+	if (iface->roam_synch_frame_ind.reassoc_rsp) {
+		qdf_mem_free(iface->roam_synch_frame_ind.reassoc_rsp);
+		iface->roam_synch_frame_ind.reassoc_rsp_len = 0;
+		iface->roam_synch_frame_ind.reassoc_rsp = NULL;
+	}
+}
+
+/**
  * wma_fill_data_synch_frame_event() - Fill the the roam sync data buffer using
  * synch frame event data
  * @wma: Global WMA Handle
@@ -2038,12 +2067,14 @@
 	wmi_key_material *key;
 	struct wma_txrx_node *iface = NULL;
 	wmi_roam_fils_synch_tlv_param *fils_info;
+	int status = -EINVAL;
 
 	synch_event = param_buf->fixed_param;
 	roam_synch_ind_ptr->roamedVdevId = synch_event->vdev_id;
 	roam_synch_ind_ptr->authStatus = synch_event->auth_status;
 	roam_synch_ind_ptr->roamReason = synch_event->roam_reason;
 	roam_synch_ind_ptr->rssi = synch_event->rssi;
+	iface = &wma->interfaces[synch_event->vdev_id];
 	WMI_MAC_ADDR_TO_CHAR_ARRAY(&synch_event->bssid,
 				   roam_synch_ind_ptr->bssid.bytes);
 	WMA_LOGD("%s: roamedVdevId %d authStatus %d roamReason %d rssi %d isBeacon %d",
@@ -2055,11 +2086,10 @@
 		wma->csr_roam_synch_cb((tpAniSirGlobal)wma->mac_context,
 		roam_synch_ind_ptr, NULL, SIR_ROAMING_DEREGISTER_STA))) {
 		WMA_LOGE("LFR3: CSR Roam synch cb failed");
-		return -EINVAL;
+		wma_free_roam_synch_frame_ind(iface);
+		return status;
 	}
 
-	iface = &wma->interfaces[synch_event->vdev_id];
-
 	/*
 	 * If lengths of bcn_probe_rsp, reassoc_req and reassoc_rsp are zero in
 	 * synch_event driver would have received bcn_probe_rsp, reassoc_req
@@ -2072,19 +2102,22 @@
 			WMA_LOGE("LFR3: bcn_probe_rsp is NULL");
 			QDF_ASSERT(iface->roam_synch_frame_ind.
 				   bcn_probe_rsp != NULL);
-			return -EINVAL;
+			wma_free_roam_synch_frame_ind(iface);
+			return status;
 		}
 		if (!iface->roam_synch_frame_ind.reassoc_rsp) {
 			WMA_LOGE("LFR3: reassoc_rsp is NULL");
 			QDF_ASSERT(iface->roam_synch_frame_ind.
 				   reassoc_rsp != NULL);
-			return -EINVAL;
+			wma_free_roam_synch_frame_ind(iface);
+			return status;
 		}
 		if (!iface->roam_synch_frame_ind.reassoc_req) {
 			WMA_LOGE("LFR3: reassoc_req is NULL");
 			QDF_ASSERT(iface->roam_synch_frame_ind.
 				   reassoc_req != NULL);
-			return -EINVAL;
+			wma_free_roam_synch_frame_ind(iface);
+			return status;
 		}
 		wma_fill_data_synch_frame_event(wma, roam_synch_ind_ptr, iface);
 	} else {
@@ -2120,7 +2153,8 @@
 				 __func__,
 				 fils_info->kek_len,
 				 fils_info->pmk_len);
-			return -EINVAL;
+			wma_free_roam_synch_frame_ind(iface);
+			return status;
 		}
 
 		roam_synch_ind_ptr->kek_len = fils_info->kek_len;
@@ -2143,6 +2177,7 @@
 			 roam_synch_ind_ptr->update_erp_next_seq_num,
 			 roam_synch_ind_ptr->next_erp_seq_num);
 	}
+	wma_free_roam_synch_frame_ind(iface);
 	return 0;
 }
 
@@ -2477,25 +2512,25 @@
 	WMA_LOGD("LFR3:Synch Frame event");
 	if (!event) {
 		WMA_LOGE("event param null");
-		goto cleanup_label;
+		return status;
 	}
 
 	param_buf = (WMI_ROAM_SYNCH_FRAME_EVENTID_param_tlvs *) event;
 	if (!param_buf) {
 		WMA_LOGE("received null buf from target");
-		goto cleanup_label;
+		return status;
 	}
 
 	synch_frame_event = param_buf->fixed_param;
 	if (!synch_frame_event) {
 		WMA_LOGE("received null event data from target");
-		goto cleanup_label;
+		return status;
 	}
 
 	if (synch_frame_event->vdev_id >= wma->max_bssid) {
 		WMA_LOGE("received invalid vdev_id %d",
 				 synch_frame_event->vdev_id);
-		goto cleanup_label;
+		return status;
 	}
 
 	vdev_id = synch_frame_event->vdev_id;
@@ -2503,7 +2538,8 @@
 
 	if (wma_is_roam_synch_in_progress(wma, vdev_id)) {
 		WMA_LOGE("Ignoring this event as it is unexpected");
-		goto cleanup_label;
+		wma_free_roam_synch_frame_ind(iface);
+		return status;
 	}
 	WMA_LOGD("LFR3: Received ROAM_SYNCH_FRAME_EVENT");
 
@@ -2519,6 +2555,9 @@
 		iface->roam_synch_frame_ind.is_beacon =
 				synch_frame_event->is_beacon;
 
+		if (iface->roam_synch_frame_ind.bcn_probe_rsp)
+			qdf_mem_free(iface->roam_synch_frame_ind.
+				     bcn_probe_rsp);
 		iface->roam_synch_frame_ind.bcn_probe_rsp =
 			qdf_mem_malloc(iface->roam_synch_frame_ind.
 				       bcn_probe_rsp_len);
@@ -2527,7 +2566,8 @@
 			QDF_ASSERT(iface->roam_synch_frame_ind.
 				   bcn_probe_rsp != NULL);
 			status = -ENOMEM;
-			goto cleanup_label;
+			wma_free_roam_synch_frame_ind(iface);
+			return status;
 		}
 		qdf_mem_copy(iface->roam_synch_frame_ind.
 			bcn_probe_rsp,
@@ -2539,6 +2579,8 @@
 		iface->roam_synch_frame_ind.reassoc_req_len =
 				synch_frame_event->reassoc_req_len;
 
+		if (iface->roam_synch_frame_ind.reassoc_req)
+			qdf_mem_free(iface->roam_synch_frame_ind.reassoc_req);
 		iface->roam_synch_frame_ind.reassoc_req =
 			qdf_mem_malloc(iface->roam_synch_frame_ind.
 				       reassoc_req_len);
@@ -2547,7 +2589,8 @@
 			QDF_ASSERT(iface->roam_synch_frame_ind.
 				   reassoc_req != NULL);
 			status = -ENOMEM;
-			goto cleanup_label;
+			wma_free_roam_synch_frame_ind(iface);
+			return status;
 		}
 		qdf_mem_copy(iface->roam_synch_frame_ind.reassoc_req,
 			     param_buf->reassoc_req_frame,
@@ -2555,9 +2598,18 @@
 	}
 
 	if (synch_frame_event->reassoc_rsp_len) {
+		if (!iface->roam_synch_frame_ind.bcn_probe_rsp ||
+		    !iface->roam_synch_frame_ind.reassoc_req) {
+			WMA_LOGE("failed: No probe or reassoc rsp");
+			wma_free_roam_synch_frame_ind(iface);
+			return status;
+		}
 		iface->roam_synch_frame_ind.reassoc_rsp_len =
 				synch_frame_event->reassoc_rsp_len;
 
+		if (iface->roam_synch_frame_ind.reassoc_rsp)
+			qdf_mem_free(iface->roam_synch_frame_ind.reassoc_rsp);
+
 		iface->roam_synch_frame_ind.reassoc_rsp =
 			qdf_mem_malloc(iface->roam_synch_frame_ind.
 				       reassoc_rsp_len);
@@ -2566,29 +2618,14 @@
 			QDF_ASSERT(iface->roam_synch_frame_ind.
 				   reassoc_rsp != NULL);
 			status = -ENOMEM;
-			goto cleanup_label;
+			wma_free_roam_synch_frame_ind(iface);
+			return status;
 		}
 		qdf_mem_copy(iface->roam_synch_frame_ind.reassoc_rsp,
 			     param_buf->reassoc_rsp_frame,
 			     iface->roam_synch_frame_ind.reassoc_rsp_len);
 	}
 	return 0;
-
-cleanup_label:
-	if (iface && (iface->roam_synch_frame_ind.bcn_probe_rsp)) {
-		qdf_mem_free(iface->roam_synch_frame_ind.
-			     bcn_probe_rsp);
-		iface->roam_synch_frame_ind.bcn_probe_rsp = NULL;
-	}
-	if (iface && (iface->roam_synch_frame_ind.reassoc_req)) {
-		qdf_mem_free(iface->roam_synch_frame_ind.reassoc_req);
-		iface->roam_synch_frame_ind.reassoc_req = NULL;
-	}
-	if (iface && (iface->roam_synch_frame_ind.reassoc_rsp)) {
-		qdf_mem_free(iface->roam_synch_frame_ind.reassoc_rsp);
-		iface->roam_synch_frame_ind.reassoc_rsp = NULL;
-	}
-	return status;
 }
 
 #define RSN_CAPS_SHIFT               16