diff options
author | Johannes Berg <johannes.berg@intel.com> | 2021-02-01 21:20:50 +0300 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2021-02-01 21:30:54 +0300 |
commit | 40c575d1ec71f7a61c73ba1603a69650c130559c (patch) | |
tree | fd01bc1a23dd89442544f2e9e56dd13269cdb00f | |
parent | 776a39b8196dbca4afb69669db0d9926ffac29ab (diff) | |
download | linux-40c575d1ec71f7a61c73ba1603a69650c130559c.tar.xz |
cfg80211: fix netdev registration deadlock
If register_netdevice() fails after having called cfg80211's
netdev notifier (cfg80211_netdev_notifier_call) it will call
the notifier again with UNREGISTER. This would then lock the
wiphy mutex because we're marked as registered, which causes
a deadlock.
Fix this by separately keeping track of whether or not we're
in the middle of registering to also skip the notifier call
on this unregister.
Reported-by: syzbot+2ae0ca9d7737ad1a62b7@syzkaller.appspotmail.com
Fixes: a05829a7222e ("cfg80211: avoid holding the RTNL when calling the driver")
Link: https://lore.kernel.org/r/20210201192048.ed8bad436737.I7cae042c44b15f80919a285799a15df467e9d42d@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r-- | include/net/cfg80211.h | 4 | ||||
-rw-r--r-- | net/wireless/core.c | 4 |
2 files changed, 6 insertions, 2 deletions
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 4741d71ead21..4cdd75449d73 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5299,6 +5299,8 @@ static inline void wiphy_unlock(struct wiphy *wiphy) * @wiphy: pointer to hardware description * @iftype: interface type * @registered: is this wdev already registered with cfg80211 + * @registering: indicates we're doing registration under wiphy lock + * for the notifier * @list: (private) Used to collect the interfaces * @netdev: (private) Used to reference back to the netdev, may be %NULL * @identifier: (private) Identifier used in nl80211 to identify this @@ -5382,7 +5384,7 @@ struct wireless_dev { struct mutex mtx; - bool use_4addr, is_running, registered; + bool use_4addr, is_running, registered, registering; u8 address[ETH_ALEN] __aligned(sizeof(u16)); diff --git a/net/wireless/core.c b/net/wireless/core.c index 18f9a5c214b5..a2785379df6e 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1346,6 +1346,7 @@ int cfg80211_register_netdevice(struct net_device *dev) /* we'll take care of this */ wdev->registered = true; + wdev->registering = true; ret = register_netdevice(dev); if (ret) goto out; @@ -1361,6 +1362,7 @@ int cfg80211_register_netdevice(struct net_device *dev) cfg80211_register_wdev(rdev, wdev); ret = 0; out: + wdev->registering = false; if (ret) wdev->registered = false; return ret; @@ -1403,7 +1405,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, * It is possible to get NETDEV_UNREGISTER multiple times, * so check wdev->registered. */ - if (wdev->registered) { + if (wdev->registered && !wdev->registering) { wiphy_lock(&rdev->wiphy); _cfg80211_unregister_wdev(wdev, false); wiphy_unlock(&rdev->wiphy); |