瀏覽代碼

rndis_wlan: increase assocbuf size and validate association info offsets from driver

Buffer size for get_association_info was limited to WEXT event size. Since association
info no longer is sent through WEXT, this limit is not needed. Code also did not
check if data get truncated, memory outside buffer might be addressed. Fix all these.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Jussi Kivilinna 15 年之前
父節點
當前提交
3d1ca47eba
共有 1 個文件被更改,包括 29 次插入7 次删除
  1. 29 7
      drivers/net/wireless/rndis_wlan.c

+ 29 - 7
drivers/net/wireless/rndis_wlan.c

@@ -2495,8 +2495,7 @@ static int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
 static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
 static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
 {
 {
 	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
 	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
-	struct ndis_80211_assoc_info *info;
-	u8 assoc_buf[sizeof(*info) + IW_CUSTOM_MAX + 32];
+	struct ndis_80211_assoc_info *info = NULL;
 	u8 bssid[ETH_ALEN];
 	u8 bssid[ETH_ALEN];
 	int resp_ie_len, req_ie_len;
 	int resp_ie_len, req_ie_len;
 	u8 *req_ie, *resp_ie;
 	u8 *req_ie, *resp_ie;
@@ -2515,23 +2514,43 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
 	resp_ie = NULL;
 	resp_ie = NULL;
 
 
 	if (priv->infra_mode == NDIS_80211_INFRA_INFRA) {
 	if (priv->infra_mode == NDIS_80211_INFRA_INFRA) {
-		memset(assoc_buf, 0, sizeof(assoc_buf));
-		info = (void *)assoc_buf;
+		info = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL);
+		if (!info) {
+			/* No memory? Try resume work later */
+			set_bit(WORK_LINK_UP, &priv->work_pending);
+			queue_work(priv->workqueue, &priv->work);
+			return;
+		}
 
 
-		/* Get association info IEs from device and send them back to
-		 * userspace. */
-		ret = get_association_info(usbdev, info, sizeof(assoc_buf));
+		/* Get association info IEs from device. */
+		ret = get_association_info(usbdev, info, CONTROL_BUFFER_SIZE);
 		if (!ret) {
 		if (!ret) {
 			req_ie_len = le32_to_cpu(info->req_ie_length);
 			req_ie_len = le32_to_cpu(info->req_ie_length);
 			if (req_ie_len > 0) {
 			if (req_ie_len > 0) {
 				offset = le32_to_cpu(info->offset_req_ies);
 				offset = le32_to_cpu(info->offset_req_ies);
+
+				if (offset > CONTROL_BUFFER_SIZE)
+					offset = CONTROL_BUFFER_SIZE;
+
 				req_ie = (u8 *)info + offset;
 				req_ie = (u8 *)info + offset;
+
+				if (offset + req_ie_len > CONTROL_BUFFER_SIZE)
+					req_ie_len =
+						CONTROL_BUFFER_SIZE - offset;
 			}
 			}
 
 
 			resp_ie_len = le32_to_cpu(info->resp_ie_length);
 			resp_ie_len = le32_to_cpu(info->resp_ie_length);
 			if (resp_ie_len > 0) {
 			if (resp_ie_len > 0) {
 				offset = le32_to_cpu(info->offset_resp_ies);
 				offset = le32_to_cpu(info->offset_resp_ies);
+
+				if (offset > CONTROL_BUFFER_SIZE)
+					offset = CONTROL_BUFFER_SIZE;
+
 				resp_ie = (u8 *)info + offset;
 				resp_ie = (u8 *)info + offset;
+
+				if (offset + resp_ie_len > CONTROL_BUFFER_SIZE)
+					resp_ie_len =
+						CONTROL_BUFFER_SIZE - offset;
 			}
 			}
 		}
 		}
 	} else if (WARN_ON(priv->infra_mode != NDIS_80211_INFRA_ADHOC))
 	} else if (WARN_ON(priv->infra_mode != NDIS_80211_INFRA_ADHOC))
@@ -2563,6 +2582,9 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
 	} else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC)
 	} else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC)
 		cfg80211_ibss_joined(usbdev->net, bssid, GFP_KERNEL);
 		cfg80211_ibss_joined(usbdev->net, bssid, GFP_KERNEL);
 
 
+	if (info != NULL)
+		kfree(info);
+
 	priv->connected = true;
 	priv->connected = true;
 	memcpy(priv->bssid, bssid, ETH_ALEN);
 	memcpy(priv->bssid, bssid, ETH_ALEN);