aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/scan.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-11-28 19:25:20 -0500
committerJohannes Berg <johannes.berg@intel.com>2012-11-30 07:42:20 -0500
commit9caf03640279e64d0ba36539b42daa1b43a49486 (patch)
treecb094a4a577f61421d1b402e16f0e68f151d5726 /net/wireless/scan.c
parentb9a9ada14aab17f08c1d9735601f1097cdcfc6de (diff)
cfg80211: fix BSS struct IE access races
When a BSS struct is updated, the IEs are currently overwritten or freed. This can lead to races if some other CPU is accessing the BSS struct and using the IEs concurrently. Fix this by always allocating the IEs in a new struct that holds the data and length and protecting access to this new struct with RCU. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless/scan.c')
-rw-r--r--net/wireless/scan.c409
1 files changed, 200 insertions, 209 deletions
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 834e0d153fbe..01592d7d4789 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -23,6 +23,7 @@
23 23
24static void bss_release(struct kref *ref) 24static void bss_release(struct kref *ref)
25{ 25{
26 struct cfg80211_bss_ies *ies;
26 struct cfg80211_internal_bss *bss; 27 struct cfg80211_internal_bss *bss;
27 28
28 bss = container_of(ref, struct cfg80211_internal_bss, ref); 29 bss = container_of(ref, struct cfg80211_internal_bss, ref);
@@ -33,10 +34,12 @@ static void bss_release(struct kref *ref)
33 if (bss->pub.free_priv) 34 if (bss->pub.free_priv)
34 bss->pub.free_priv(&bss->pub); 35 bss->pub.free_priv(&bss->pub);
35 36
36 if (bss->beacon_ies_allocated) 37 ies = (void *)rcu_access_pointer(bss->pub.beacon_ies);
37 kfree(bss->pub.beacon_ies); 38 if (ies)
38 if (bss->proberesp_ies_allocated) 39 kfree_rcu(ies, rcu_head);
39 kfree(bss->pub.proberesp_ies); 40 ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies);
41 if (ies)
42 kfree_rcu(ies, rcu_head);
40 43
41 kfree(bss); 44 kfree(bss);
42} 45}
@@ -288,7 +291,7 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type,
288} 291}
289EXPORT_SYMBOL(cfg80211_find_vendor_ie); 292EXPORT_SYMBOL(cfg80211_find_vendor_ie);
290 293
291static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) 294static int cmp_ies(u8 num, const u8 *ies1, int len1, const u8 *ies2, int len2)
292{ 295{
293 const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); 296 const u8 *ie1 = cfg80211_find_ie(num, ies1, len1);
294 const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); 297 const u8 *ie2 = cfg80211_find_ie(num, ies2, len2);
@@ -311,6 +314,7 @@ static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
311static bool is_bss(struct cfg80211_bss *a, const u8 *bssid, 314static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
312 const u8 *ssid, size_t ssid_len) 315 const u8 *ssid, size_t ssid_len)
313{ 316{
317 const struct cfg80211_bss_ies *ies;
314 const u8 *ssidie; 318 const u8 *ssidie;
315 319
316 if (bssid && !ether_addr_equal(a->bssid, bssid)) 320 if (bssid && !ether_addr_equal(a->bssid, bssid))
@@ -319,9 +323,10 @@ static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
319 if (!ssid) 323 if (!ssid)
320 return true; 324 return true;
321 325
322 ssidie = cfg80211_find_ie(WLAN_EID_SSID, 326 ies = rcu_access_pointer(a->ies);
323 a->information_elements, 327 if (!ies)
324 a->len_information_elements); 328 return false;
329 ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
325 if (!ssidie) 330 if (!ssidie)
326 return false; 331 return false;
327 if (ssidie[1] != ssid_len) 332 if (ssidie[1] != ssid_len)
@@ -331,20 +336,21 @@ static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
331 336
332static bool is_mesh_bss(struct cfg80211_bss *a) 337static bool is_mesh_bss(struct cfg80211_bss *a)
333{ 338{
339 const struct cfg80211_bss_ies *ies;
334 const u8 *ie; 340 const u8 *ie;
335 341
336 if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) 342 if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
337 return false; 343 return false;
338 344
339 ie = cfg80211_find_ie(WLAN_EID_MESH_ID, 345 ies = rcu_access_pointer(a->ies);
340 a->information_elements, 346 if (!ies)
341 a->len_information_elements); 347 return false;
348
349 ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len);
342 if (!ie) 350 if (!ie)
343 return false; 351 return false;
344 352
345 ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, 353 ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len);
346 a->information_elements,
347 a->len_information_elements);
348 if (!ie) 354 if (!ie)
349 return false; 355 return false;
350 356
@@ -355,14 +361,17 @@ static bool is_mesh(struct cfg80211_bss *a,
355 const u8 *meshid, size_t meshidlen, 361 const u8 *meshid, size_t meshidlen,
356 const u8 *meshcfg) 362 const u8 *meshcfg)
357{ 363{
364 const struct cfg80211_bss_ies *ies;
358 const u8 *ie; 365 const u8 *ie;
359 366
360 if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) 367 if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
361 return false; 368 return false;
362 369
363 ie = cfg80211_find_ie(WLAN_EID_MESH_ID, 370 ies = rcu_access_pointer(a->ies);
364 a->information_elements, 371 if (!ies)
365 a->len_information_elements); 372 return false;
373
374 ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len);
366 if (!ie) 375 if (!ie)
367 return false; 376 return false;
368 if (ie[1] != meshidlen) 377 if (ie[1] != meshidlen)
@@ -370,9 +379,7 @@ static bool is_mesh(struct cfg80211_bss *a,
370 if (memcmp(ie + 2, meshid, meshidlen)) 379 if (memcmp(ie + 2, meshid, meshidlen))
371 return false; 380 return false;
372 381
373 ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, 382 ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len);
374 a->information_elements,
375 a->len_information_elements);
376 if (!ie) 383 if (!ie)
377 return false; 384 return false;
378 if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) 385 if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
@@ -389,24 +396,28 @@ static bool is_mesh(struct cfg80211_bss *a,
389 396
390static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b) 397static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b)
391{ 398{
399 const struct cfg80211_bss_ies *a_ies, *b_ies;
392 int r; 400 int r;
393 401
394 if (a->channel != b->channel) 402 if (a->channel != b->channel)
395 return b->channel->center_freq - a->channel->center_freq; 403 return b->channel->center_freq - a->channel->center_freq;
396 404
397 if (is_mesh_bss(a) && is_mesh_bss(b)) { 405 if (is_mesh_bss(a) && is_mesh_bss(b)) {
406 a_ies = rcu_access_pointer(a->ies);
407 if (!a_ies)
408 return -1;
409 b_ies = rcu_access_pointer(b->ies);
410 if (!b_ies)
411 return 1;
412
398 r = cmp_ies(WLAN_EID_MESH_ID, 413 r = cmp_ies(WLAN_EID_MESH_ID,
399 a->information_elements, 414 a_ies->data, a_ies->len,
400 a->len_information_elements, 415 b_ies->data, b_ies->len);
401 b->information_elements,
402 b->len_information_elements);
403 if (r) 416 if (r)
404 return r; 417 return r;
405 return cmp_ies(WLAN_EID_MESH_CONFIG, 418 return cmp_ies(WLAN_EID_MESH_CONFIG,
406 a->information_elements, 419 a_ies->data, a_ies->len,
407 a->len_information_elements, 420 b_ies->data, b_ies->len);
408 b->information_elements,
409 b->len_information_elements);
410 } 421 }
411 422
412 /* 423 /*
@@ -419,21 +430,28 @@ static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b)
419static int cmp_bss(struct cfg80211_bss *a, 430static int cmp_bss(struct cfg80211_bss *a,
420 struct cfg80211_bss *b) 431 struct cfg80211_bss *b)
421{ 432{
433 const struct cfg80211_bss_ies *a_ies, *b_ies;
422 int r; 434 int r;
423 435
424 r = cmp_bss_core(a, b); 436 r = cmp_bss_core(a, b);
425 if (r) 437 if (r)
426 return r; 438 return r;
427 439
440 a_ies = rcu_access_pointer(a->ies);
441 if (!a_ies)
442 return -1;
443 b_ies = rcu_access_pointer(b->ies);
444 if (!b_ies)
445 return 1;
446
428 return cmp_ies(WLAN_EID_SSID, 447 return cmp_ies(WLAN_EID_SSID,
429 a->information_elements, 448 a_ies->data, a_ies->len,
430 a->len_information_elements, 449 b_ies->data, b_ies->len);
431 b->information_elements,
432 b->len_information_elements);
433} 450}
434 451
435static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b) 452static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b)
436{ 453{
454 const struct cfg80211_bss_ies *a_ies, *b_ies;
437 const u8 *ie1; 455 const u8 *ie1;
438 const u8 *ie2; 456 const u8 *ie2;
439 int i; 457 int i;
@@ -443,12 +461,15 @@ static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b)
443 if (r) 461 if (r)
444 return r; 462 return r;
445 463
446 ie1 = cfg80211_find_ie(WLAN_EID_SSID, 464 a_ies = rcu_access_pointer(a->ies);
447 a->information_elements, 465 if (!a_ies)
448 a->len_information_elements); 466 return -1;
449 ie2 = cfg80211_find_ie(WLAN_EID_SSID, 467 b_ies = rcu_access_pointer(b->ies);
450 b->information_elements, 468 if (!b_ies)
451 b->len_information_elements); 469 return 1;
470
471 ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len);
472 ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len);
452 473
453 /* 474 /*
454 * Key comparator must use same algorithm in any rb-tree 475 * Key comparator must use same algorithm in any rb-tree
@@ -633,126 +654,84 @@ static void
633copy_hidden_ies(struct cfg80211_internal_bss *res, 654copy_hidden_ies(struct cfg80211_internal_bss *res,
634 struct cfg80211_internal_bss *hidden) 655 struct cfg80211_internal_bss *hidden)
635{ 656{
636 if (unlikely(res->pub.beacon_ies)) 657 const struct cfg80211_bss_ies *ies;
637 return; 658
638 if (WARN_ON(!hidden->pub.beacon_ies)) 659 if (rcu_access_pointer(res->pub.beacon_ies))
639 return; 660 return;
640 661
641 res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC); 662 ies = rcu_access_pointer(hidden->pub.beacon_ies);
642 if (unlikely(!res->pub.beacon_ies)) 663 if (WARN_ON(!ies))
643 return; 664 return;
644 665
645 res->beacon_ies_allocated = true; 666 ies = kmemdup(ies, sizeof(*ies) + ies->len, GFP_ATOMIC);
646 res->pub.len_beacon_ies = hidden->pub.len_beacon_ies; 667 if (unlikely(!ies))
647 memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies, 668 return;
648 res->pub.len_beacon_ies); 669 rcu_assign_pointer(res->pub.beacon_ies, ies);
649} 670}
650 671
651static struct cfg80211_internal_bss * 672static struct cfg80211_internal_bss *
652cfg80211_bss_update(struct cfg80211_registered_device *dev, 673cfg80211_bss_update(struct cfg80211_registered_device *dev,
653 struct cfg80211_internal_bss *res) 674 struct cfg80211_internal_bss *tmp)
654{ 675{
655 struct cfg80211_internal_bss *found = NULL; 676 struct cfg80211_internal_bss *found = NULL;
656 677
657 /* 678 if (WARN_ON(!tmp->pub.channel))
658 * The reference to "res" is donated to this function.
659 */
660
661 if (WARN_ON(!res->pub.channel)) {
662 kref_put(&res->ref, bss_release);
663 return NULL; 679 return NULL;
664 }
665 680
666 res->ts = jiffies; 681 tmp->ts = jiffies;
667 682
668 spin_lock_bh(&dev->bss_lock); 683 spin_lock_bh(&dev->bss_lock);
669 684
670 found = rb_find_bss(dev, res); 685 if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) {
686 spin_unlock_bh(&dev->bss_lock);
687 return NULL;
688 }
689
690 found = rb_find_bss(dev, tmp);
671 691
672 if (found) { 692 if (found) {
673 found->pub.beacon_interval = res->pub.beacon_interval; 693 found->pub.beacon_interval = tmp->pub.beacon_interval;
674 found->pub.tsf = res->pub.tsf; 694 found->pub.tsf = tmp->pub.tsf;
675 found->pub.signal = res->pub.signal; 695 found->pub.signal = tmp->pub.signal;
676 found->pub.capability = res->pub.capability; 696 found->pub.capability = tmp->pub.capability;
677 found->ts = res->ts; 697 found->ts = tmp->ts;
678 698
679 /* Update IEs */ 699 /* Update IEs */
680 if (res->pub.proberesp_ies) { 700 if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
681 size_t used = dev->wiphy.bss_priv_size + sizeof(*res); 701 const struct cfg80211_bss_ies *old;
682 size_t ielen = res->pub.len_proberesp_ies; 702
683 703 old = rcu_access_pointer(found->pub.proberesp_ies);
684 if (found->pub.proberesp_ies &&
685 !found->proberesp_ies_allocated &&
686 ksize(found) >= used + ielen) {
687 memcpy(found->pub.proberesp_ies,
688 res->pub.proberesp_ies, ielen);
689 found->pub.len_proberesp_ies = ielen;
690 } else {
691 u8 *ies = found->pub.proberesp_ies;
692
693 if (found->proberesp_ies_allocated)
694 ies = krealloc(ies, ielen, GFP_ATOMIC);
695 else
696 ies = kmalloc(ielen, GFP_ATOMIC);
697
698 if (ies) {
699 memcpy(ies, res->pub.proberesp_ies,
700 ielen);
701 found->proberesp_ies_allocated = true;
702 found->pub.proberesp_ies = ies;
703 found->pub.len_proberesp_ies = ielen;
704 }
705 }
706 704
705 rcu_assign_pointer(found->pub.proberesp_ies,
706 tmp->pub.proberesp_ies);
707 /* Override possible earlier Beacon frame IEs */ 707 /* Override possible earlier Beacon frame IEs */
708 found->pub.information_elements = 708 rcu_assign_pointer(found->pub.ies,
709 found->pub.proberesp_ies; 709 tmp->pub.proberesp_ies);
710 found->pub.len_information_elements = 710 if (old)
711 found->pub.len_proberesp_ies; 711 kfree_rcu((struct cfg80211_bss_ies *)old,
712 } 712 rcu_head);
713 } else if (rcu_access_pointer(tmp->pub.beacon_ies)) {
714 const struct cfg80211_bss_ies *old, *ies;
713 715
714 if (res->pub.beacon_ies) { 716 old = rcu_access_pointer(found->pub.beacon_ies);
715 size_t used = dev->wiphy.bss_priv_size + sizeof(*res); 717 ies = rcu_access_pointer(found->pub.ies);
716 size_t ielen = res->pub.len_beacon_ies; 718
717 bool information_elements_is_beacon_ies = 719 rcu_assign_pointer(found->pub.beacon_ies,
718 (found->pub.information_elements == 720 tmp->pub.beacon_ies);
719 found->pub.beacon_ies);
720
721 if (found->pub.beacon_ies &&
722 !found->beacon_ies_allocated &&
723 ksize(found) >= used + ielen) {
724 memcpy(found->pub.beacon_ies,
725 res->pub.beacon_ies, ielen);
726 found->pub.len_beacon_ies = ielen;
727 } else {
728 u8 *ies = found->pub.beacon_ies;
729
730 if (found->beacon_ies_allocated)
731 ies = krealloc(ies, ielen, GFP_ATOMIC);
732 else
733 ies = kmalloc(ielen, GFP_ATOMIC);
734
735 if (ies) {
736 memcpy(ies, res->pub.beacon_ies,
737 ielen);
738 found->beacon_ies_allocated = true;
739 found->pub.beacon_ies = ies;
740 found->pub.len_beacon_ies = ielen;
741 }
742 }
743 721
744 /* Override IEs if they were from a beacon before */ 722 /* Override IEs if they were from a beacon before */
745 if (information_elements_is_beacon_ies) { 723 if (old == ies)
746 found->pub.information_elements = 724 rcu_assign_pointer(found->pub.ies,
747 found->pub.beacon_ies; 725 tmp->pub.beacon_ies);
748 found->pub.len_information_elements =
749 found->pub.len_beacon_ies;
750 }
751 }
752 726
753 kref_put(&res->ref, bss_release); 727 if (old)
728 kfree_rcu((struct cfg80211_bss_ies *)old,
729 rcu_head);
730 }
754 } else { 731 } else {
732 struct cfg80211_internal_bss *new;
755 struct cfg80211_internal_bss *hidden; 733 struct cfg80211_internal_bss *hidden;
734 struct cfg80211_bss_ies *ies;
756 735
757 /* First check if the beacon is a probe response from 736 /* First check if the beacon is a probe response from
758 * a hidden bss. If so, copy beacon ies (with nullified 737 * a hidden bss. If so, copy beacon ies (with nullified
@@ -763,14 +742,32 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
763 /* TODO: The code is not trying to update existing probe 742 /* TODO: The code is not trying to update existing probe
764 * response bss entries when beacon ies are 743 * response bss entries when beacon ies are
765 * getting changed. */ 744 * getting changed. */
766 hidden = rb_find_hidden_bss(dev, res); 745 hidden = rb_find_hidden_bss(dev, tmp);
767 if (hidden) 746 if (hidden)
768 copy_hidden_ies(res, hidden); 747 copy_hidden_ies(tmp, hidden);
769 748
770 /* this "consumes" the reference */ 749 /*
771 list_add_tail(&res->list, &dev->bss_list); 750 * create a copy -- the "res" variable that is passed in
772 rb_insert_bss(dev, res); 751 * is allocated on the stack since it's not needed in the
773 found = res; 752 * more common case of an update
753 */
754 new = kzalloc(sizeof(*new) + dev->wiphy.bss_priv_size,
755 GFP_ATOMIC);
756 if (!new) {
757 ies = (void *)rcu_dereference(tmp->pub.beacon_ies);
758 if (ies)
759 kfree_rcu(ies, rcu_head);
760 ies = (void *)rcu_dereference(tmp->pub.proberesp_ies);
761 if (ies)
762 kfree_rcu(ies, rcu_head);
763 spin_unlock_bh(&dev->bss_lock);
764 return NULL;
765 }
766 memcpy(new, tmp, sizeof(*new));
767 kref_init(&new->ref);
768 list_add_tail(&new->list, &dev->bss_list);
769 rb_insert_bss(dev, new);
770 found = new;
774 } 771 }
775 772
776 dev->bss_generation++; 773 dev->bss_generation++;
@@ -819,14 +816,12 @@ cfg80211_inform_bss(struct wiphy *wiphy,
819 u16 beacon_interval, const u8 *ie, size_t ielen, 816 u16 beacon_interval, const u8 *ie, size_t ielen,
820 s32 signal, gfp_t gfp) 817 s32 signal, gfp_t gfp)
821{ 818{
822 struct cfg80211_internal_bss *res; 819 struct cfg80211_bss_ies *ies;
823 size_t privsz; 820 struct cfg80211_internal_bss tmp = {}, *res;
824 821
825 if (WARN_ON(!wiphy)) 822 if (WARN_ON(!wiphy))
826 return NULL; 823 return NULL;
827 824
828 privsz = wiphy->bss_priv_size;
829
830 if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC && 825 if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
831 (signal < 0 || signal > 100))) 826 (signal < 0 || signal > 100)))
832 return NULL; 827 return NULL;
@@ -835,36 +830,33 @@ cfg80211_inform_bss(struct wiphy *wiphy,
835 if (!channel) 830 if (!channel)
836 return NULL; 831 return NULL;
837 832
838 res = kzalloc(sizeof(*res) + privsz + ielen, gfp); 833 memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
839 if (!res) 834 tmp.pub.channel = channel;
840 return NULL; 835 tmp.pub.signal = signal;
841 836 tmp.pub.tsf = tsf;
842 memcpy(res->pub.bssid, bssid, ETH_ALEN); 837 tmp.pub.beacon_interval = beacon_interval;
843 res->pub.channel = channel; 838 tmp.pub.capability = capability;
844 res->pub.signal = signal;
845 res->pub.tsf = tsf;
846 res->pub.beacon_interval = beacon_interval;
847 res->pub.capability = capability;
848 /* 839 /*
849 * Since we do not know here whether the IEs are from a Beacon or Probe 840 * Since we do not know here whether the IEs are from a Beacon or Probe
850 * Response frame, we need to pick one of the options and only use it 841 * Response frame, we need to pick one of the options and only use it
851 * with the driver that does not provide the full Beacon/Probe Response 842 * with the driver that does not provide the full Beacon/Probe Response
852 * frame. Use Beacon frame pointer to avoid indicating that this should 843 * frame. Use Beacon frame pointer to avoid indicating that this should
853 * override the information_elements pointer should we have received an 844 * override the iies pointer should we have received an earlier
854 * earlier indication of Probe Response data. 845 * indication of Probe Response data.
855 * 846 *
856 * The initial buffer for the IEs is allocated with the BSS entry and 847 * The initial buffer for the IEs is allocated with the BSS entry and
857 * is located after the private area. 848 * is located after the private area.
858 */ 849 */
859 res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz; 850 ies = kmalloc(sizeof(*ies) + ielen, gfp);
860 memcpy(res->pub.beacon_ies, ie, ielen); 851 if (!ies)
861 res->pub.len_beacon_ies = ielen; 852 return NULL;
862 res->pub.information_elements = res->pub.beacon_ies; 853 ies->len = ielen;
863 res->pub.len_information_elements = res->pub.len_beacon_ies; 854 memcpy(ies->data, ie, ielen);
864 855
865 kref_init(&res->ref); 856 rcu_assign_pointer(tmp.pub.beacon_ies, ies);
857 rcu_assign_pointer(tmp.pub.ies, ies);
866 858
867 res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); 859 res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp);
868 if (!res) 860 if (!res)
869 return NULL; 861 return NULL;
870 862
@@ -883,10 +875,10 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
883 struct ieee80211_mgmt *mgmt, size_t len, 875 struct ieee80211_mgmt *mgmt, size_t len,
884 s32 signal, gfp_t gfp) 876 s32 signal, gfp_t gfp)
885{ 877{
886 struct cfg80211_internal_bss *res; 878 struct cfg80211_internal_bss tmp = {}, *res;
879 struct cfg80211_bss_ies *ies;
887 size_t ielen = len - offsetof(struct ieee80211_mgmt, 880 size_t ielen = len - offsetof(struct ieee80211_mgmt,
888 u.probe_resp.variable); 881 u.probe_resp.variable);
889 size_t privsz;
890 882
891 BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) != 883 BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
892 offsetof(struct ieee80211_mgmt, u.beacon.variable)); 884 offsetof(struct ieee80211_mgmt, u.beacon.variable));
@@ -906,45 +898,31 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
906 if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable))) 898 if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
907 return NULL; 899 return NULL;
908 900
909 privsz = wiphy->bss_priv_size;
910
911 channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable, 901 channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
912 ielen, channel); 902 ielen, channel);
913 if (!channel) 903 if (!channel)
914 return NULL; 904 return NULL;
915 905
916 res = kzalloc(sizeof(*res) + privsz + ielen, gfp); 906 ies = kmalloc(sizeof(*ies) + ielen, gfp);
917 if (!res) 907 if (!ies)
918 return NULL; 908 return NULL;
909 ies->len = ielen;
910 memcpy(ies->data, mgmt->u.probe_resp.variable, ielen);
919 911
920 memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN); 912 if (ieee80211_is_probe_resp(mgmt->frame_control))
921 res->pub.channel = channel; 913 rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
922 res->pub.signal = signal; 914 else
923 res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); 915 rcu_assign_pointer(tmp.pub.beacon_ies, ies);
924 res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); 916 rcu_assign_pointer(tmp.pub.ies, ies);
925 res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); 917
926 /* 918 memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
927 * The initial buffer for the IEs is allocated with the BSS entry and 919 tmp.pub.channel = channel;
928 * is located after the private area. 920 tmp.pub.signal = signal;
929 */ 921 tmp.pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
930 if (ieee80211_is_probe_resp(mgmt->frame_control)) { 922 tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
931 res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz; 923 tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
932 memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable, 924
933 ielen); 925 res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp);
934 res->pub.len_proberesp_ies = ielen;
935 res->pub.information_elements = res->pub.proberesp_ies;
936 res->pub.len_information_elements = res->pub.len_proberesp_ies;
937 } else {
938 res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz;
939 memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen);
940 res->pub.len_beacon_ies = ielen;
941 res->pub.information_elements = res->pub.beacon_ies;
942 res->pub.len_information_elements = res->pub.len_beacon_ies;
943 }
944
945 kref_init(&res->ref);
946
947 res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
948 if (!res) 926 if (!res)
949 return NULL; 927 return NULL;
950 928
@@ -1136,22 +1114,21 @@ int cfg80211_wext_siwscan(struct net_device *dev,
1136EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); 1114EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan);
1137 1115
1138static void ieee80211_scan_add_ies(struct iw_request_info *info, 1116static void ieee80211_scan_add_ies(struct iw_request_info *info,
1139 struct cfg80211_bss *bss, 1117 const struct cfg80211_bss_ies *ies,
1140 char **current_ev, char *end_buf) 1118 char **current_ev, char *end_buf)
1141{ 1119{
1142 u8 *pos, *end, *next; 1120 const u8 *pos, *end, *next;
1143 struct iw_event iwe; 1121 struct iw_event iwe;
1144 1122
1145 if (!bss->information_elements || 1123 if (!ies)
1146 !bss->len_information_elements)
1147 return; 1124 return;
1148 1125
1149 /* 1126 /*
1150 * If needed, fragment the IEs buffer (at IE boundaries) into short 1127 * If needed, fragment the IEs buffer (at IE boundaries) into short
1151 * enough fragments to fit into IW_GENERIC_IE_MAX octet messages. 1128 * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
1152 */ 1129 */
1153 pos = bss->information_elements; 1130 pos = ies->data;
1154 end = pos + bss->len_information_elements; 1131 end = pos + ies->len;
1155 1132
1156 while (end - pos > IW_GENERIC_IE_MAX) { 1133 while (end - pos > IW_GENERIC_IE_MAX) {
1157 next = pos + 2 + pos[1]; 1134 next = pos + 2 + pos[1];
@@ -1162,7 +1139,8 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
1162 iwe.cmd = IWEVGENIE; 1139 iwe.cmd = IWEVGENIE;
1163 iwe.u.data.length = next - pos; 1140 iwe.u.data.length = next - pos;
1164 *current_ev = iwe_stream_add_point(info, *current_ev, 1141 *current_ev = iwe_stream_add_point(info, *current_ev,
1165 end_buf, &iwe, pos); 1142 end_buf, &iwe,
1143 (void *)pos);
1166 1144
1167 pos = next; 1145 pos = next;
1168 } 1146 }
@@ -1172,7 +1150,8 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
1172 iwe.cmd = IWEVGENIE; 1150 iwe.cmd = IWEVGENIE;
1173 iwe.u.data.length = end - pos; 1151 iwe.u.data.length = end - pos;
1174 *current_ev = iwe_stream_add_point(info, *current_ev, 1152 *current_ev = iwe_stream_add_point(info, *current_ev,
1175 end_buf, &iwe, pos); 1153 end_buf, &iwe,
1154 (void *)pos);
1176 } 1155 }
1177} 1156}
1178 1157
@@ -1191,10 +1170,11 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
1191 struct cfg80211_internal_bss *bss, char *current_ev, 1170 struct cfg80211_internal_bss *bss, char *current_ev,
1192 char *end_buf) 1171 char *end_buf)
1193{ 1172{
1173 const struct cfg80211_bss_ies *ies;
1194 struct iw_event iwe; 1174 struct iw_event iwe;
1175 const u8 *ie;
1195 u8 *buf, *cfg, *p; 1176 u8 *buf, *cfg, *p;
1196 u8 *ie = bss->pub.information_elements; 1177 int rem, i, sig;
1197 int rem = bss->pub.len_information_elements, i, sig;
1198 bool ismesh = false; 1178 bool ismesh = false;
1199 1179
1200 memset(&iwe, 0, sizeof(iwe)); 1180 memset(&iwe, 0, sizeof(iwe));
@@ -1259,7 +1239,17 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
1259 current_ev = iwe_stream_add_point(info, current_ev, end_buf, 1239 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
1260 &iwe, ""); 1240 &iwe, "");
1261 1241
1262 while (rem >= 2) { 1242 rcu_read_lock();
1243 ies = rcu_dereference(bss->pub.ies);
1244 if (ies) {
1245 rem = ies->len;
1246 ie = ies->data;
1247 } else {
1248 rem = 0;
1249 ie = NULL;
1250 }
1251
1252 while (ies && rem >= 2) {
1263 /* invalid data */ 1253 /* invalid data */
1264 if (ie[1] > rem - 2) 1254 if (ie[1] > rem - 2)
1265 break; 1255 break;
@@ -1271,7 +1261,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
1271 iwe.u.data.length = ie[1]; 1261 iwe.u.data.length = ie[1];
1272 iwe.u.data.flags = 1; 1262 iwe.u.data.flags = 1;
1273 current_ev = iwe_stream_add_point(info, current_ev, end_buf, 1263 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
1274 &iwe, ie + 2); 1264 &iwe, (u8 *)ie + 2);
1275 break; 1265 break;
1276 case WLAN_EID_MESH_ID: 1266 case WLAN_EID_MESH_ID:
1277 memset(&iwe, 0, sizeof(iwe)); 1267 memset(&iwe, 0, sizeof(iwe));
@@ -1279,7 +1269,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
1279 iwe.u.data.length = ie[1]; 1269 iwe.u.data.length = ie[1];
1280 iwe.u.data.flags = 1; 1270 iwe.u.data.flags = 1;
1281 current_ev = iwe_stream_add_point(info, current_ev, end_buf, 1271 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
1282 &iwe, ie + 2); 1272 &iwe, (u8 *)ie + 2);
1283 break; 1273 break;
1284 case WLAN_EID_MESH_CONFIG: 1274 case WLAN_EID_MESH_CONFIG:
1285 ismesh = true; 1275 ismesh = true;
@@ -1288,7 +1278,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
1288 buf = kmalloc(50, GFP_ATOMIC); 1278 buf = kmalloc(50, GFP_ATOMIC);
1289 if (!buf) 1279 if (!buf)
1290 break; 1280 break;
1291 cfg = ie + 2; 1281 cfg = (u8 *)ie + 2;
1292 memset(&iwe, 0, sizeof(iwe)); 1282 memset(&iwe, 0, sizeof(iwe));
1293 iwe.cmd = IWEVCUSTOM; 1283 iwe.cmd = IWEVCUSTOM;
1294 sprintf(buf, "Mesh Network Path Selection Protocol ID: " 1284 sprintf(buf, "Mesh Network Path Selection Protocol ID: "
@@ -1386,7 +1376,8 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
1386 kfree(buf); 1376 kfree(buf);
1387 } 1377 }
1388 1378
1389 ieee80211_scan_add_ies(info, &bss->pub, &current_ev, end_buf); 1379 ieee80211_scan_add_ies(info, ies, &current_ev, end_buf);
1380 rcu_read_unlock();
1390 1381
1391 return current_ev; 1382 return current_ev;
1392} 1383}