Commit f0a6fd15 authored by Johannes Berg's avatar Johannes Berg

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: default avatarToke Høiland-Jørgensen <toke@redhat.com>
Fixes: ea6b2098 ("cfg80211: fix locking in netlink owner interface destruction")
Tested-by: default avatarToke Høiland-Jørgensen <toke@redhat.com>
Link: https://lore.kernel.org/r/20220201130951.22093-1-johannes@sipsolutions.netSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 5f06f6bf
...@@ -5,7 +5,7 @@ ...@@ -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) ...@@ -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)
return;
wiphy_lock(&rdev->wiphy); 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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment