Browse Source

qcacld-3.0: Process pno ioctl appropriately

propagation from qcacld-2.0 to qcacld-3.0.

While processing setpno ioctl, input arguments are not validated
and also while parsing arguments, there is a possibility of Host
accessing memory beyond memory allocated as there is no check
whether is Host is accessing valid memory or not.
Validate input arguments and make sure Host won't access invalid
memory, while processing setpno ioctl.

Change-Id: Ica9ea56283d55282cff3ccd349e4bc1c08b80e70
CRs-Fixed: 1097868
Hanumanth Reddy Pothula 8 years ago
parent
commit
6310c77b23
3 changed files with 42 additions and 13 deletions
  1. 0 2
      core/hdd/src/wlan_hdd_cfg80211.c
  2. 3 0
      core/hdd/src/wlan_hdd_cfg80211.h
  3. 39 11
      core/hdd/src/wlan_hdd_wext.c

+ 0 - 2
core/hdd/src/wlan_hdd_cfg80211.c

@@ -199,8 +199,6 @@
 
 #define IS_DFS_MODE_VALID(mode) ((mode >= DFS_MODE_NONE && \
 			mode <= DFS_MODE_DEPRIORITIZE))
-#define IS_CHANNEL_VALID(channel) ((channel >= 0 && channel < 15) \
-		|| (channel >= 36 && channel <= 184))
 
 #define MAX_TXPOWER_SCALE 4
 #define CDS_MAX_FEATURE_SET   8

+ 3 - 0
core/hdd/src/wlan_hdd_cfg80211.h

@@ -108,6 +108,9 @@ static inline void wlan_hdd_clear_link_layer_stats(hdd_adapter_t *adapter) {}
 #define MAX_CHANNEL (NUM_24GHZ_CHANNELS + NUM_5GHZ_CHANNELS)
 #define MAX_SCAN_SSID 10
 
+#define IS_CHANNEL_VALID(channel) ((channel >= 0 && channel < 15) \
+			|| (channel >= 36 && channel <= 184))
+
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) \
 	|| defined(BACKPORTED_CHANNEL_SWITCH_PRESENT)
 #define CHANNEL_SWITCH_SUPPORTED

+ 39 - 11
core/hdd/src/wlan_hdd_wext.c

@@ -12152,9 +12152,10 @@ static int __iw_set_pno(struct net_device *dev,
 	struct wlan_objmgr_psoc *psoc;
 	int ret = 0;
 	int offset;
-	char *ptr;
+	char *ptr, *data;
 	uint8_t i, j, params;
 	QDF_STATUS status;
+	size_t len;
 
 	/* request is a large struct, so we make it static to avoid
 	 * stack overflow.  This API is only invoked via ioctl, so it
@@ -12183,9 +12184,18 @@ static int __iw_set_pno(struct net_device *dev,
 
 	hdd_debug("PNO data len %d data %s", wrqu->data.length, extra);
 
-	ptr = extra;
+	/* making sure argument string ends with '\0' */
+	len = (wrqu->data.length + 1);
+	data = qdf_mem_malloc(len);
+	if (!data) {
+		hdd_err("fail to allocate memory %zu", len);
+		return -EINVAL;
+	}
+	qdf_mem_copy(data, extra, (len-1));
+	data[len] = '\0';
+	ptr = data;
 
-	if (1 != sscanf(ptr, "%hhu%n", &value, &offset)) {
+	if (1 != sscanf(ptr, " %hhu %n", &value, &offset)) {
 		hdd_err("PNO enable input is not valid %s", ptr);
 		ret = -EINVAL;
 		goto exit;
@@ -12210,7 +12220,7 @@ static int __iw_set_pno(struct net_device *dev,
 
 	ptr += offset;
 
-	if (1 != sscanf(ptr, "%hhu %n", &value, &offset)) {
+	if (1 != sscanf(ptr, " %hhu %n", &value, &offset)) {
 		hdd_err("PNO count input not valid %s", ptr);
 		ret = -EINVAL;
 		goto exit;
@@ -12234,7 +12244,7 @@ static int __iw_set_pno(struct net_device *dev,
 
 		req.networks_list[i].ssid.length = 0;
 
-		params = sscanf(ptr, "%hhu %n",
+		params = sscanf(ptr, " %hhu %n",
 				  &(req.networks_list[i].ssid.length),
 				  &offset);
 
@@ -12259,7 +12269,7 @@ static int __iw_set_pno(struct net_device *dev,
 		       req.networks_list[i].ssid.length);
 		ptr += req.networks_list[i].ssid.length;
 
-		params = sscanf(ptr, "%u %u %hhu %n",
+		params = sscanf(ptr, " %u %u %hhu %n",
 				  &(req.networks_list[i].authentication),
 				  &(req.networks_list[i].encryption),
 				  &(req.networks_list[i].channel_cnt),
@@ -12292,13 +12302,18 @@ static int __iw_set_pno(struct net_device *dev,
 		if (0 != req.networks_list[i].channel_cnt) {
 			for (j = 0; j < req.networks_list[i].channel_cnt;
 			     j++) {
-				if (1 != sscanf(ptr, "%hhu %n", &value,
+				if (1 != sscanf(ptr, " %hhu %n", &value,
 				   &offset)) {
 					hdd_err("PNO network channel is not valid %s",
 						  ptr);
 					ret = -EINVAL;
 					goto exit;
 				}
+				if (!IS_CHANNEL_VALID(value)) {
+					hdd_err("invalid channel: %hhu", value);
+					ret = -EINVAL;
+					goto exit;
+				}
 				req.networks_list[i].channels[j] =
 					cds_chan_to_freq(value);
 				/* Advance to next channel number */
@@ -12306,7 +12321,7 @@ static int __iw_set_pno(struct net_device *dev,
 			}
 		}
 
-		if (1 != sscanf(ptr, "%u %n",
+		if (1 != sscanf(ptr, " %u %n",
 				&(req.networks_list[i].bc_new_type),
 				&offset)) {
 			hdd_err("PNO broadcast network type is not valid %s",
@@ -12314,13 +12329,19 @@ static int __iw_set_pno(struct net_device *dev,
 			ret = -EINVAL;
 			goto exit;
 		}
+		if (req.networks_list[i].bc_new_type > 2) {
+			hdd_err("invalid bcast nw type: %u",
+				req.networks_list[i].bc_new_type);
+			ret = -EINVAL;
+			goto exit;
+		}
 
 		hdd_debug("PNO bcastNetwType %d offset %d",
 			  req.networks_list[i].bc_new_type, offset);
 
 		/* Advance to rssi Threshold */
 		ptr += offset;
-		if (1 != sscanf(ptr, "%d %n",
+		if (1 != sscanf(ptr, " %d %n",
 				&(req.networks_list[i].rssi_thresh),
 				&offset)) {
 			hdd_err("PNO rssi threshold input is not valid %s",
@@ -12335,13 +12356,19 @@ static int __iw_set_pno(struct net_device *dev,
 	} /* For ucNetworkCount */
 
 	req.fast_scan_period = 0;
-	if (sscanf(ptr, "%u %n", &(req.fast_scan_period), &offset) > 0) {
+	if (sscanf(ptr, " %u %n", &(req.fast_scan_period), &offset) > 0) {
 		req.fast_scan_period *= MSEC_PER_SEC;
 		ptr += offset;
 	}
+	if (req.fast_scan_period == 0) {
+		hdd_err("invalid fast scan period %u",
+			req.fast_scan_period);
+			ret = -EINVAL;
+			goto exit;
+	}
 
 	req.fast_scan_max_cycles = 0;
-	if (sscanf(ptr, "%hhu %n", &value,
+	if (sscanf(ptr, " %hhu %n", &value,
 		   &offset) > 0)
 		ptr += offset;
 	req.fast_scan_max_cycles = value;
@@ -12362,6 +12389,7 @@ static int __iw_set_pno(struct net_device *dev,
 exit:
 	wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID);
 
+	qdf_mem_free(data);
 	return ret;
 }