mac80211_hwsim: fix locking when iterating radios during ns exit
authorMartin Willi <martin@strongswan.org>
Tue, 25 Sep 2018 07:41:13 +0000 (09:41 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 26 Sep 2018 09:20:13 +0000 (11:20 +0200)
The cleanup of radios during namespace exit has recently been reworked
to directly delete a radio while temporarily releasing the spinlock,
fixing a race condition between the work-queue execution and namespace
exits. However, the temporary unlock allows unsafe modifications on the
iterated list, resulting in a potential crash when continuing the
iteration of additional radios.

Move radios about to destroy to a temporary list, and clean that up
after releasing the spinlock once iteration is complete.

Fixes: 8cfd36a0b53a ("mac80211_hwsim: fix use-after-free bug in hwsim_exit_net")
Signed-off-by: Martin Willi <martin@strongswan.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
drivers/net/wireless/mac80211_hwsim.c

index 1068757..f1150d3 100644 (file)
@@ -3646,6 +3646,7 @@ static __net_init int hwsim_init_net(struct net *net)
 static void __net_exit hwsim_exit_net(struct net *net)
 {
        struct mac80211_hwsim_data *data, *tmp;
+       LIST_HEAD(list);
 
        spin_lock_bh(&hwsim_radio_lock);
        list_for_each_entry_safe(data, tmp, &hwsim_radios, list) {
@@ -3656,17 +3657,19 @@ static void __net_exit hwsim_exit_net(struct net *net)
                if (data->netgroup == hwsim_net_get_netgroup(&init_net))
                        continue;
 
-               list_del(&data->list);
+               list_move(&data->list, &list);
                rhashtable_remove_fast(&hwsim_radios_rht, &data->rht,
                                       hwsim_rht_params);
                hwsim_radios_generation++;
-               spin_unlock_bh(&hwsim_radio_lock);
+       }
+       spin_unlock_bh(&hwsim_radio_lock);
+
+       list_for_each_entry_safe(data, tmp, &list, list) {
+               list_del(&data->list);
                mac80211_hwsim_del_radio(data,
                                         wiphy_name(data->hw->wiphy),
                                         NULL);
-               spin_lock_bh(&hwsim_radio_lock);
        }
-       spin_unlock_bh(&hwsim_radio_lock);
 
        ida_simple_remove(&hwsim_netgroup_ida, hwsim_net_get_netgroup(net));
 }