cfg80211: fix race in netlink owner interface destruction
My previous fix here to fix the deadlock left a race where the exact same deadlock (see the original commit referenced below) can still happen if cfg80211_destroy_ifaces() already runs while nl80211_netlink_notify() is still marking some interfaces as nl_owner_dead. The race happens because we have two loops here - first we dev_close() all the netdevs, and then we destroy them. If we also have two netdevs (first one need only be a wdev though) then we can find one during the first iteration, close it, and go to the second iteration -- but then find two, and try to destroy also the one we didn't close yet. Fix this by only iterating once. Reported-by: Toke Høiland-Jørgensen <toke@redhat.com> Fixes: ea6b2098dd02 ("cfg80211: fix locking in netlink owner interface destruction") Tested-by: Toke Høiland-Jørgensen <toke@redhat.com> Link: https://lore.kernel.org/r/20220201130951.22093-1-johannes@sipsolutions.net Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
5f06f6bf8d
commit
f0a6fd1527
@ -5,7 +5,7 @@
|
|||||||
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
|
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
|
||||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||||
* Copyright 2015-2017 Intel Deutschland GmbH
|
* Copyright 2015-2017 Intel Deutschland GmbH
|
||||||
* Copyright (C) 2018-2021 Intel Corporation
|
* Copyright (C) 2018-2022 Intel Corporation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
@ -332,29 +332,20 @@ static void cfg80211_event_work(struct work_struct *work)
|
|||||||
void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev)
|
void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev)
|
||||||
{
|
{
|
||||||
struct wireless_dev *wdev, *tmp;
|
struct wireless_dev *wdev, *tmp;
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
ASSERT_RTNL();
|
ASSERT_RTNL();
|
||||||
|
|
||||||
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
|
list_for_each_entry_safe(wdev, tmp, &rdev->wiphy.wdev_list, list) {
|
||||||
if (wdev->nl_owner_dead) {
|
if (wdev->nl_owner_dead) {
|
||||||
if (wdev->netdev)
|
if (wdev->netdev)
|
||||||
dev_close(wdev->netdev);
|
dev_close(wdev->netdev);
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
wiphy_lock(&rdev->wiphy);
|
||||||
return;
|
|
||||||
|
|
||||||
wiphy_lock(&rdev->wiphy);
|
|
||||||
list_for_each_entry_safe(wdev, tmp, &rdev->wiphy.wdev_list, list) {
|
|
||||||
if (wdev->nl_owner_dead) {
|
|
||||||
cfg80211_leave(rdev, wdev);
|
cfg80211_leave(rdev, wdev);
|
||||||
rdev_del_virtual_intf(rdev, wdev);
|
rdev_del_virtual_intf(rdev, wdev);
|
||||||
|
wiphy_unlock(&rdev->wiphy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wiphy_unlock(&rdev->wiphy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cfg80211_destroy_iface_wk(struct work_struct *work)
|
static void cfg80211_destroy_iface_wk(struct work_struct *work)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user