Ver Fonte

qcacld-3.0: Check buff len alloc in __iw_set_packet_filter_params

In __iw_set_packet_filter_params(), a user controlled length value,
priv_data.length, is used to allocated a buffer. This buffer is then
cast to a struct pointer of struct pkt_filter_cfg type without ensuring
the buffer is of proper length.

Add a sanity check on priv_data.length to ensure that the command being
issued has proper parameters.

Change-Id: Ia871e35ef938ca889fb6b1609a0c881d76f29e4b
CRs-Fixed: 2250775
Sourav Mohapatra há 6 anos atrás
pai
commit
69d0780f6e
1 ficheiros alterados com 40 adições e 2 exclusões
  1. 40 2
      core/hdd/src/wlan_hdd_wext.c

+ 40 - 2
core/hdd/src/wlan_hdd_wext.c

@@ -8061,6 +8061,38 @@ static int iw_set_keepalive_params(struct net_device *dev,
 }
 
 #ifdef WLAN_FEATURE_PACKET_FILTERING
+/**
+ * validate_packet_filter_params_size() - Validate the size of the params rcvd
+ * @priv_data: Pointer to the priv data from user space
+ * @request: Pointer to the struct containing the copied data from user space
+ *
+ * Return: False on invalid length, true otherwise
+ */
+static bool validate_packet_filter_params_size(struct pkt_filter_cfg *request,
+					       uint16_t length)
+{
+	int max_params_size, rcvd_params_size;
+
+	max_params_size = HDD_MAX_CMP_PER_PACKET_FILTER *
+					sizeof(struct pkt_filter_param_cfg);
+
+	if (length < sizeof(struct pkt_filter_cfg) - max_params_size) {
+		hdd_err("Less than minimum number of arguments needed");
+		return false;
+	}
+
+	rcvd_params_size = request->num_params *
+					sizeof(struct pkt_filter_param_cfg);
+
+	if (length != sizeof(struct pkt_filter_cfg) -
+					max_params_size + rcvd_params_size) {
+		hdd_err("Arguments do not match the number of params provided");
+		return false;
+	}
+
+	return true;
+}
+
 /**
  * __iw_set_packet_filter_params() - set packet filter parameters in target
  * @dev: Pointer to netdev
@@ -8101,8 +8133,7 @@ static int __iw_set_packet_filter_params(struct net_device *dev,
 		return -EINVAL;
 	}
 
-	if ((NULL == priv_data.pointer) || (0 == priv_data.length) ||
-	   priv_data.length < sizeof(struct pkt_filter_cfg)) {
+	if ((NULL == priv_data.pointer) || (0 == priv_data.length)) {
 		hdd_err("invalid priv data %pK or invalid priv data length %d",
 			priv_data.pointer, priv_data.length);
 		return -EINVAL;
@@ -8122,11 +8153,18 @@ static int __iw_set_packet_filter_params(struct net_device *dev,
 	/* copy data using copy_from_user */
 	request = mem_alloc_copy_from_user_helper(priv_data.pointer,
 						   priv_data.length);
+
 	if (NULL == request) {
 		hdd_err("mem_alloc_copy_from_user_helper fail");
 		return -ENOMEM;
 	}
 
+	if (!validate_packet_filter_params_size(request, priv_data.length)) {
+		hdd_err("Invalid priv data length %d", priv_data.length);
+		qdf_mem_free(request);
+		return -EINVAL;
+	}
+
 	if (request->filter_action == HDD_RCV_FILTER_SET)
 		hdd_ctx->user_configured_pkt_filter_rules |=
 					1 << request->filter_id;