Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mac80211: fix work removal on deauth request

When deauth is requested while an auth or assoc
work item is in progress, we currently delete it
without regard for any state it might need to
clean up. Fix it by cleaning up for those items.

In the case Pontus found, the problem manifested
itself as such:

authenticate with 00:23:69:aa:dd:7b (try 1)
authenticated
failed to insert Dummy STA entry for the AP (error -17)
deauthenticating from 00:23:69:aa:dd:7b by local choice (reason=2)

It could also happen differently if the driver
uses the tx_sync callback.

We can't just call the ->done() method of the work
items because that will lock up due to the locking
in cfg80211. This fix isn't very clean, but that
seems acceptable since I have patches pending to
remove this code completely.

Cc: stable@vger.kernel.org
Reported-by: Pontus Fuchs <pontus.fuchs@gmail.com>
Tested-by: Pontus Fuchs <pontus.fuchs@gmail.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

authored by

Johannes Berg and committed by
John W. Linville
bc4934bc 65e8b0cc

+27 -11
+27 -11
net/mac80211/mlme.c
··· 2750 2750 { 2751 2751 struct ieee80211_local *local = sdata->local; 2752 2752 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 2753 - struct ieee80211_work *wk; 2754 2753 u8 bssid[ETH_ALEN]; 2755 2754 bool assoc_bss = false; 2756 2755 ··· 2762 2763 assoc_bss = true; 2763 2764 } else { 2764 2765 bool not_auth_yet = false; 2766 + struct ieee80211_work *tmp, *wk = NULL; 2765 2767 2766 2768 mutex_unlock(&ifmgd->mtx); 2767 2769 2768 2770 mutex_lock(&local->mtx); 2769 - list_for_each_entry(wk, &local->work_list, list) { 2770 - if (wk->sdata != sdata) 2771 + list_for_each_entry(tmp, &local->work_list, list) { 2772 + if (tmp->sdata != sdata) 2771 2773 continue; 2772 2774 2773 - if (wk->type != IEEE80211_WORK_DIRECT_PROBE && 2774 - wk->type != IEEE80211_WORK_AUTH && 2775 - wk->type != IEEE80211_WORK_ASSOC && 2776 - wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT) 2775 + if (tmp->type != IEEE80211_WORK_DIRECT_PROBE && 2776 + tmp->type != IEEE80211_WORK_AUTH && 2777 + tmp->type != IEEE80211_WORK_ASSOC && 2778 + tmp->type != IEEE80211_WORK_ASSOC_BEACON_WAIT) 2777 2779 continue; 2778 2780 2779 - if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN)) 2781 + if (memcmp(req->bss->bssid, tmp->filter_ta, ETH_ALEN)) 2780 2782 continue; 2781 2783 2782 - not_auth_yet = wk->type == IEEE80211_WORK_DIRECT_PROBE; 2783 - list_del_rcu(&wk->list); 2784 - free_work(wk); 2784 + not_auth_yet = tmp->type == IEEE80211_WORK_DIRECT_PROBE; 2785 + list_del_rcu(&tmp->list); 2786 + synchronize_rcu(); 2787 + wk = tmp; 2785 2788 break; 2786 2789 } 2787 2790 mutex_unlock(&local->mtx); 2791 + 2792 + if (wk && wk->type == IEEE80211_WORK_ASSOC) { 2793 + /* clean up dummy sta & TX sync */ 2794 + sta_info_destroy_addr(wk->sdata, wk->filter_ta); 2795 + if (wk->assoc.synced) 2796 + drv_finish_tx_sync(local, wk->sdata, 2797 + wk->filter_ta, 2798 + IEEE80211_TX_SYNC_ASSOC); 2799 + } else if (wk && wk->type == IEEE80211_WORK_AUTH) { 2800 + if (wk->probe_auth.synced) 2801 + drv_finish_tx_sync(local, wk->sdata, 2802 + wk->filter_ta, 2803 + IEEE80211_TX_SYNC_AUTH); 2804 + } 2805 + kfree(wk); 2788 2806 2789 2807 /* 2790 2808 * If somebody requests authentication and we haven't