Commit 9bdaf3b9 authored by Johannes Berg's avatar Johannes Berg

cfg80211: initialize wdev data earlier

There's a race condition in the netdev registration in that
NETDEV_REGISTER actually happens after the netdev is available,
and so if we initialize things only there, we might get called
with an uninitialized wdev through nl80211 - not using a wdev
but using a netdev interface index.

I found this while looking into a syzbot report, but it doesn't
really seem to be related, and unfortunately there's no repro
for it (yet). I can't (yet) explain how it managed to get into
cfg80211_release_pmsr() from nl80211_netlink_notify() without
the wdev having been initialized, as the latter only iterates
the wdevs that are linked into the rdev, which even without the
change here happened after init.

However, looking at this, it seems fairly clear that the init
needs to be done earlier, otherwise we might even re-init on a
netns move, when data might still be pending.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Link: https://lore.kernel.org/r/20201009135821.fdcbba3aad65.Ie9201d91dbcb7da32318812effdc1561aeaf4cdc@changeidSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 14f46c1e
...@@ -1250,8 +1250,7 @@ void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, ...@@ -1250,8 +1250,7 @@ void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
} }
EXPORT_SYMBOL(cfg80211_stop_iface); EXPORT_SYMBOL(cfg80211_stop_iface);
void cfg80211_init_wdev(struct cfg80211_registered_device *rdev, void cfg80211_init_wdev(struct wireless_dev *wdev)
struct wireless_dev *wdev)
{ {
mutex_init(&wdev->mtx); mutex_init(&wdev->mtx);
INIT_LIST_HEAD(&wdev->event_list); INIT_LIST_HEAD(&wdev->event_list);
...@@ -1262,6 +1261,30 @@ void cfg80211_init_wdev(struct cfg80211_registered_device *rdev, ...@@ -1262,6 +1261,30 @@ void cfg80211_init_wdev(struct cfg80211_registered_device *rdev,
spin_lock_init(&wdev->pmsr_lock); spin_lock_init(&wdev->pmsr_lock);
INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk); INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
#ifdef CONFIG_CFG80211_WEXT
wdev->wext.default_key = -1;
wdev->wext.default_mgmt_key = -1;
wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
#endif
if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT)
wdev->ps = true;
else
wdev->ps = false;
/* allow mac80211 to determine the timeout */
wdev->ps_timeout = -1;
if ((wdev->iftype == NL80211_IFTYPE_STATION ||
wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
wdev->netdev->priv_flags |= IFF_DONT_BRIDGE;
INIT_WORK(&wdev->disconnect_wk, cfg80211_autodisconnect_wk);
}
void cfg80211_register_wdev(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev)
{
/* /*
* We get here also when the interface changes network namespaces, * We get here also when the interface changes network namespaces,
* as it's registered into the new one, but we don't want it to * as it's registered into the new one, but we don't want it to
...@@ -1295,6 +1318,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, ...@@ -1295,6 +1318,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
switch (state) { switch (state) {
case NETDEV_POST_INIT: case NETDEV_POST_INIT:
SET_NETDEV_DEVTYPE(dev, &wiphy_type); SET_NETDEV_DEVTYPE(dev, &wiphy_type);
wdev->netdev = dev;
/* can only change netns with wiphy */
dev->features |= NETIF_F_NETNS_LOCAL;
cfg80211_init_wdev(wdev);
break; break;
case NETDEV_REGISTER: case NETDEV_REGISTER:
/* /*
...@@ -1302,35 +1330,12 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, ...@@ -1302,35 +1330,12 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
* called within code protected by it when interfaces * called within code protected by it when interfaces
* are added with nl80211. * are added with nl80211.
*/ */
/* can only change netns with wiphy */
dev->features |= NETIF_F_NETNS_LOCAL;
if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
"phy80211")) { "phy80211")) {
pr_err("failed to add phy80211 symlink to netdev!\n"); pr_err("failed to add phy80211 symlink to netdev!\n");
} }
wdev->netdev = dev;
#ifdef CONFIG_CFG80211_WEXT
wdev->wext.default_key = -1;
wdev->wext.default_mgmt_key = -1;
wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
#endif
if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT)
wdev->ps = true;
else
wdev->ps = false;
/* allow mac80211 to determine the timeout */
wdev->ps_timeout = -1;
if ((wdev->iftype == NL80211_IFTYPE_STATION ||
wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
dev->priv_flags |= IFF_DONT_BRIDGE;
INIT_WORK(&wdev->disconnect_wk, cfg80211_autodisconnect_wk);
cfg80211_init_wdev(rdev, wdev); cfg80211_register_wdev(rdev, wdev);
break; break;
case NETDEV_GOING_DOWN: case NETDEV_GOING_DOWN:
cfg80211_leave(rdev, wdev); cfg80211_leave(rdev, wdev);
......
...@@ -209,8 +209,9 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); ...@@ -209,8 +209,9 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx);
int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
struct net *net); struct net *net);
void cfg80211_init_wdev(struct cfg80211_registered_device *rdev, void cfg80211_init_wdev(struct wireless_dev *wdev);
struct wireless_dev *wdev); void cfg80211_register_wdev(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
static inline void wdev_lock(struct wireless_dev *wdev) static inline void wdev_lock(struct wireless_dev *wdev)
__acquires(wdev) __acquires(wdev)
......
...@@ -3885,7 +3885,8 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) ...@@ -3885,7 +3885,8 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
* P2P Device and NAN do not have a netdev, so don't go * P2P Device and NAN do not have a netdev, so don't go
* through the netdev notifier and must be added here * through the netdev notifier and must be added here
*/ */
cfg80211_init_wdev(rdev, wdev); cfg80211_init_wdev(wdev);
cfg80211_register_wdev(rdev, wdev);
break; break;
default: default:
break; break;
......
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