diff options
-rw-r--r-- | include/net/mac80211.h | 5 | ||||
-rw-r--r-- | net/mac80211/rx.c | 36 | ||||
-rw-r--r-- | net/wireless/util.c | 24 |
3 files changed, 54 insertions, 11 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 1a8f50af49a0..ecaae10426f9 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h | |||
@@ -1822,7 +1822,10 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw); | |||
1822 | * ieee80211_rx - receive frame | 1822 | * ieee80211_rx - receive frame |
1823 | * | 1823 | * |
1824 | * Use this function to hand received frames to mac80211. The receive | 1824 | * Use this function to hand received frames to mac80211. The receive |
1825 | * buffer in @skb must start with an IEEE 802.11 header. | 1825 | * buffer in @skb must start with an IEEE 802.11 header. In case of a |
1826 | * paged @skb is used, the driver is recommended to put the ieee80211 | ||
1827 | * header of the frame on the linear part of the @skb to avoid memory | ||
1828 | * allocation and/or memcpy by the stack. | ||
1826 | * | 1829 | * |
1827 | * This function may not be called in IRQ context. Calls to this function | 1830 | * This function may not be called in IRQ context. Calls to this function |
1828 | * for a single hardware must be synchronized against each other. Calls to | 1831 | * for a single hardware must be synchronized against each other. Calls to |
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 1da57c8e849a..11ed5aa90f83 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -38,7 +38,7 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, | |||
38 | { | 38 | { |
39 | if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) { | 39 | if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) { |
40 | if (likely(skb->len > FCS_LEN)) | 40 | if (likely(skb->len > FCS_LEN)) |
41 | skb_trim(skb, skb->len - FCS_LEN); | 41 | __pskb_trim(skb, skb->len - FCS_LEN); |
42 | else { | 42 | else { |
43 | /* driver bug */ | 43 | /* driver bug */ |
44 | WARN_ON(1); | 44 | WARN_ON(1); |
@@ -227,6 +227,12 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, | |||
227 | if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) | 227 | if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) |
228 | present_fcs_len = FCS_LEN; | 228 | present_fcs_len = FCS_LEN; |
229 | 229 | ||
230 | /* make sure hdr->frame_control is on the linear part */ | ||
231 | if (!pskb_may_pull(origskb, 2)) { | ||
232 | dev_kfree_skb(origskb); | ||
233 | return NULL; | ||
234 | } | ||
235 | |||
230 | if (!local->monitors) { | 236 | if (!local->monitors) { |
231 | if (should_drop_frame(origskb, present_fcs_len)) { | 237 | if (should_drop_frame(origskb, present_fcs_len)) { |
232 | dev_kfree_skb(origskb); | 238 | dev_kfree_skb(origskb); |
@@ -931,6 +937,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) | |||
931 | return RX_DROP_MONITOR; | 937 | return RX_DROP_MONITOR; |
932 | } | 938 | } |
933 | 939 | ||
940 | if (skb_linearize(rx->skb)) | ||
941 | return RX_DROP_UNUSABLE; | ||
942 | |||
934 | /* Check for weak IVs if possible */ | 943 | /* Check for weak IVs if possible */ |
935 | if (rx->sta && rx->key->conf.alg == ALG_WEP && | 944 | if (rx->sta && rx->key->conf.alg == ALG_WEP && |
936 | ieee80211_is_data(hdr->frame_control) && | 945 | ieee80211_is_data(hdr->frame_control) && |
@@ -1231,6 +1240,9 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) | |||
1231 | } | 1240 | } |
1232 | I802_DEBUG_INC(rx->local->rx_handlers_fragments); | 1241 | I802_DEBUG_INC(rx->local->rx_handlers_fragments); |
1233 | 1242 | ||
1243 | if (skb_linearize(rx->skb)) | ||
1244 | return RX_DROP_UNUSABLE; | ||
1245 | |||
1234 | seq = (sc & IEEE80211_SCTL_SEQ) >> 4; | 1246 | seq = (sc & IEEE80211_SCTL_SEQ) >> 4; |
1235 | 1247 | ||
1236 | if (frag == 0) { | 1248 | if (frag == 0) { |
@@ -1588,6 +1600,9 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) | |||
1588 | skb->dev = dev; | 1600 | skb->dev = dev; |
1589 | __skb_queue_head_init(&frame_list); | 1601 | __skb_queue_head_init(&frame_list); |
1590 | 1602 | ||
1603 | if (skb_linearize(skb)) | ||
1604 | return RX_DROP_UNUSABLE; | ||
1605 | |||
1591 | ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr, | 1606 | ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr, |
1592 | rx->sdata->vif.type, | 1607 | rx->sdata->vif.type, |
1593 | rx->local->hw.extra_tx_headroom); | 1608 | rx->local->hw.extra_tx_headroom); |
@@ -2357,29 +2372,42 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, | |||
2357 | struct ieee80211_local *local = hw_to_local(hw); | 2372 | struct ieee80211_local *local = hw_to_local(hw); |
2358 | struct ieee80211_sub_if_data *sdata; | 2373 | struct ieee80211_sub_if_data *sdata; |
2359 | struct ieee80211_hdr *hdr; | 2374 | struct ieee80211_hdr *hdr; |
2375 | __le16 fc; | ||
2360 | struct ieee80211_rx_data rx; | 2376 | struct ieee80211_rx_data rx; |
2361 | int prepares; | 2377 | int prepares; |
2362 | struct ieee80211_sub_if_data *prev = NULL; | 2378 | struct ieee80211_sub_if_data *prev = NULL; |
2363 | struct sk_buff *skb_new; | 2379 | struct sk_buff *skb_new; |
2364 | struct sta_info *sta, *tmp; | 2380 | struct sta_info *sta, *tmp; |
2365 | bool found_sta = false; | 2381 | bool found_sta = false; |
2382 | int err = 0; | ||
2366 | 2383 | ||
2367 | hdr = (struct ieee80211_hdr *)skb->data; | 2384 | fc = ((struct ieee80211_hdr *)skb->data)->frame_control; |
2368 | memset(&rx, 0, sizeof(rx)); | 2385 | memset(&rx, 0, sizeof(rx)); |
2369 | rx.skb = skb; | 2386 | rx.skb = skb; |
2370 | rx.local = local; | 2387 | rx.local = local; |
2371 | 2388 | ||
2372 | if (ieee80211_is_data(hdr->frame_control) || ieee80211_is_mgmt(hdr->frame_control)) | 2389 | if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc)) |
2373 | local->dot11ReceivedFragmentCount++; | 2390 | local->dot11ReceivedFragmentCount++; |
2374 | 2391 | ||
2375 | if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) || | 2392 | if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) || |
2376 | test_bit(SCAN_OFF_CHANNEL, &local->scanning))) | 2393 | test_bit(SCAN_OFF_CHANNEL, &local->scanning))) |
2377 | rx.flags |= IEEE80211_RX_IN_SCAN; | 2394 | rx.flags |= IEEE80211_RX_IN_SCAN; |
2378 | 2395 | ||
2396 | if (ieee80211_is_mgmt(fc)) | ||
2397 | err = skb_linearize(skb); | ||
2398 | else | ||
2399 | err = !pskb_may_pull(skb, ieee80211_hdrlen(fc)); | ||
2400 | |||
2401 | if (err) { | ||
2402 | dev_kfree_skb(skb); | ||
2403 | return; | ||
2404 | } | ||
2405 | |||
2406 | hdr = (struct ieee80211_hdr *)skb->data; | ||
2379 | ieee80211_parse_qos(&rx); | 2407 | ieee80211_parse_qos(&rx); |
2380 | ieee80211_verify_alignment(&rx); | 2408 | ieee80211_verify_alignment(&rx); |
2381 | 2409 | ||
2382 | if (ieee80211_is_data(hdr->frame_control)) { | 2410 | if (ieee80211_is_data(fc)) { |
2383 | for_each_sta_info(local, hdr->addr2, sta, tmp) { | 2411 | for_each_sta_info(local, hdr->addr2, sta, tmp) { |
2384 | rx.sta = sta; | 2412 | rx.sta = sta; |
2385 | found_sta = true; | 2413 | found_sta = true; |
diff --git a/net/wireless/util.c b/net/wireless/util.c index be2ab8c59e3a..7acb81b9675d 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -330,11 +330,18 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, | |||
330 | if (iftype == NL80211_IFTYPE_MESH_POINT) { | 330 | if (iftype == NL80211_IFTYPE_MESH_POINT) { |
331 | struct ieee80211s_hdr *meshdr = | 331 | struct ieee80211s_hdr *meshdr = |
332 | (struct ieee80211s_hdr *) (skb->data + hdrlen); | 332 | (struct ieee80211s_hdr *) (skb->data + hdrlen); |
333 | hdrlen += ieee80211_get_mesh_hdrlen(meshdr); | 333 | /* make sure meshdr->flags is on the linear part */ |
334 | if (!pskb_may_pull(skb, hdrlen + 1)) | ||
335 | return -1; | ||
334 | if (meshdr->flags & MESH_FLAGS_AE_A5_A6) { | 336 | if (meshdr->flags & MESH_FLAGS_AE_A5_A6) { |
335 | memcpy(dst, meshdr->eaddr1, ETH_ALEN); | 337 | skb_copy_bits(skb, hdrlen + |
336 | memcpy(src, meshdr->eaddr2, ETH_ALEN); | 338 | offsetof(struct ieee80211s_hdr, eaddr1), |
339 | dst, ETH_ALEN); | ||
340 | skb_copy_bits(skb, hdrlen + | ||
341 | offsetof(struct ieee80211s_hdr, eaddr2), | ||
342 | src, ETH_ALEN); | ||
337 | } | 343 | } |
344 | hdrlen += ieee80211_get_mesh_hdrlen(meshdr); | ||
338 | } | 345 | } |
339 | break; | 346 | break; |
340 | case cpu_to_le16(IEEE80211_FCTL_FROMDS): | 347 | case cpu_to_le16(IEEE80211_FCTL_FROMDS): |
@@ -346,9 +353,14 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, | |||
346 | if (iftype == NL80211_IFTYPE_MESH_POINT) { | 353 | if (iftype == NL80211_IFTYPE_MESH_POINT) { |
347 | struct ieee80211s_hdr *meshdr = | 354 | struct ieee80211s_hdr *meshdr = |
348 | (struct ieee80211s_hdr *) (skb->data + hdrlen); | 355 | (struct ieee80211s_hdr *) (skb->data + hdrlen); |
349 | hdrlen += ieee80211_get_mesh_hdrlen(meshdr); | 356 | /* make sure meshdr->flags is on the linear part */ |
357 | if (!pskb_may_pull(skb, hdrlen + 1)) | ||
358 | return -1; | ||
350 | if (meshdr->flags & MESH_FLAGS_AE_A4) | 359 | if (meshdr->flags & MESH_FLAGS_AE_A4) |
351 | memcpy(src, meshdr->eaddr1, ETH_ALEN); | 360 | skb_copy_bits(skb, hdrlen + |
361 | offsetof(struct ieee80211s_hdr, eaddr1), | ||
362 | src, ETH_ALEN); | ||
363 | hdrlen += ieee80211_get_mesh_hdrlen(meshdr); | ||
352 | } | 364 | } |
353 | break; | 365 | break; |
354 | case cpu_to_le16(0): | 366 | case cpu_to_le16(0): |
@@ -357,7 +369,7 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, | |||
357 | break; | 369 | break; |
358 | } | 370 | } |
359 | 371 | ||
360 | if (unlikely(skb->len - hdrlen < 8)) | 372 | if (!pskb_may_pull(skb, hdrlen + 8)) |
361 | return -1; | 373 | return -1; |
362 | 374 | ||
363 | payload = skb->data + hdrlen; | 375 | payload = skb->data + hdrlen; |