Message ID | 1ff58acb-4171-46ff-8a33-821600a8d8e4@yandex.ru |
---|---|
State | New |
Headers | show |
Series | Managing debugfs entries and https://syzkaller.appspot.com/bug?extid=d5dc2801166df6d34774 | expand |
Hi, hmm, doubt that would work. Fundamentally, the problem is that when we switch between MLO and non-MLO (multi-link operation), we need to recreate the debugfs because the layout changes. However, in that case we should not have any active stations and running ieee80211_debugfs_recreate_netdev should usually not be problematic. So, the simple way to prevent this error is to make sure that ieee80211_debugfs_recreate_netdev is never called while we have a station. In the case of this report we seem to be getting there via a mac address change (i.e. ieee80211_change_mac) and the sane thing would be to just return -EBUSY instead of permitting the operation to continue. To fix the error, one could possibly prevent the stations debugfs entries from being deleted by ieee80211_debugfs_recreate_netdev or also recreate them. However, keeping them is not really correct unless MLO is not toggled and I am not sure how straight forward it would be to recreate them. Benjamin On Thu, 2024-07-18 at 20:03 +0300, Dmitry Antipov wrote: > The following quirk looks like a (briefly tested with CONFIG_KMEMLEAK) > fix for https://syzkaller.appspot.com/bug?extid=d5dc2801166df6d34774: > > diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c > index 1e9389c49a57..8224257e5d93 100644 > --- a/net/mac80211/debugfs_sta.c > +++ b/net/mac80211/debugfs_sta.c > @@ -1284,7 +1284,9 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) > > void ieee80211_sta_debugfs_remove(struct sta_info *sta) > { > - debugfs_remove_recursive(sta->debugfs_dir); > + if (!sta->debugfs_shared) > + debugfs_remove_recursive(sta->debugfs_dir); > + sta->debugfs_shared = false; > sta->debugfs_dir = NULL; > } > > @@ -1319,6 +1321,7 @@ void ieee80211_link_sta_debugfs_add(struct link_sta_info *link_sta) > return; > > link_sta->debugfs_dir = link_sta->sta->debugfs_dir; > + link_sta->sta->debugfs_shared = true; > } > > DEBUGFS_ADD(ht_capa); > diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h > index 9195d5a2de0a..d76ba36ca794 100644 > --- a/net/mac80211/sta_info.h > +++ b/net/mac80211/sta_info.h > @@ -708,6 +708,7 @@ struct sta_info { > > #ifdef CONFIG_MAC80211_DEBUGFS > struct dentry *debugfs_dir; > + bool debugfs_shared; > #endif > > struct codel_params cparams; > > So what about managing debugfs entries wih krefs? E.g.: > > diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h > index 9195d5a2de0a..1f4561533530 100644 > --- a/net/mac80211/sta_info.h > +++ b/net/mac80211/sta_info.h > @@ -466,6 +466,15 @@ struct ieee80211_fragment_cache { > unsigned int next; > }; > > +#ifdef CONFIG_MAC80211_DEBUGFS > + > +struct sta_debugfs_entry { > + struct dentry *debugfs_dir; > + struct kref kref; > +}; > + > +#endif /* CONFIG_MAC80211_DEBUGFS */ > + > /* > * The bandwidth threshold below which the per-station CoDel parameters will be > * scaled to be more lenient (to prevent starvation of slow stations). This > @@ -563,7 +572,7 @@ struct link_sta_info { > enum ieee80211_sta_rx_bandwidth cur_max_bandwidth; > > #ifdef CONFIG_MAC80211_DEBUGFS > - struct dentry *debugfs_dir; > + struct sta_debugfs_entry *debugfs_entry; > #endif > > struct ieee80211_link_sta *pub; > @@ -707,7 +716,7 @@ struct sta_info { > struct sta_ampdu_mlme ampdu_mlme; > > #ifdef CONFIG_MAC80211_DEBUGFS > - struct dentry *debugfs_dir; > + struct sta_debugfs_entry *debugfs_entry; > #endif > > struct codel_params cparams; > > Dmitry Intel Deutschland GmbH Registered Address: Am Campeon 10, 85579 Neubiberg, Germany Tel: +49 89 99 8853-0, www.intel.de Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva Chairperson of the Supervisory Board: Nicole Lau Registered Office: Munich Commercial Register: Amtsgericht Muenchen HRB 186928
On 7/19/24 12:38 PM, Berg, Benjamin wrote: > So, the simple way to prevent this error is to make sure that > ieee80211_debugfs_recreate_netdev is never called while we have a > station. In the case of this report we seem to be getting there via a > mac address change (i.e. ieee80211_change_mac) and the sane thing would > be to just return -EBUSY instead of permitting the operation to > continue. Just to check whether I understand this: diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a3485e4c6132..d5adbe5b3e51 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1173,6 +1173,8 @@ struct ieee80211_sub_if_data { u16 restart_active_links; + u32 sta_count; + #ifdef CONFIG_MAC80211_DEBUGFS struct { struct dentry *subdir_stations; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index b4ad66af3af3..d8e6e411d754 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -215,6 +215,9 @@ static int ieee80211_can_powered_addr_change(struct ieee80211_sub_if_data *sdata if (netif_carrier_ok(sdata->dev)) return -EBUSY; + if (sdata->sta_count) + return -EBUSY; + /* First check no ROC work is happening on this iface */ list_for_each_entry(roc, &local->roc_list, list) { if (roc->sdata != sdata) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index aa22f09e6d14..42657afb6d22 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -443,6 +443,7 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) #endif sta_info_free_link(&sta->deflink); + sta->sdata->sta_count--; kfree(sta); } @@ -691,6 +692,7 @@ __sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta->cparams.ce_threshold_mask = 0; sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); + sdata->sta_count++; return sta; Dmitry
On Tue, 2024-07-23 at 14:19 +0300, Dmitry Antipov wrote: > On 7/19/24 12:38 PM, Berg, Benjamin wrote: > > > So, the simple way to prevent this error is to make sure that > > ieee80211_debugfs_recreate_netdev is never called while we have a > > station. In the case of this report we seem to be getting there via a > > mac address change (i.e. ieee80211_change_mac) and the sane thing would > > be to just return -EBUSY instead of permitting the operation to > > continue. > > Just to check whether I understand this: > > diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h > index a3485e4c6132..d5adbe5b3e51 100644 > --- a/net/mac80211/ieee80211_i.h > +++ b/net/mac80211/ieee80211_i.h > @@ -1173,6 +1173,8 @@ struct ieee80211_sub_if_data { > > u16 restart_active_links; > > + u32 sta_count; That's probably one way of doing it, but it's rather ad-hoc, really what we should be doing is check more things in whether we allow the change or not, it looks like now it can only happen in the window between starting auth/assoc and actually having a connection, which is anyway wrong to allow it in. So more like below. johannes --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -204,7 +204,6 @@ static int ieee80211_can_powered_addr_change(struct ieee80211_sub_if_data *sdata struct ieee80211_roc_work *roc; struct ieee80211_local *local = sdata->local; struct ieee80211_sub_if_data *scan_sdata; - int ret = 0; lockdep_assert_wiphy(local->hw.wiphy); @@ -220,10 +219,8 @@ static int ieee80211_can_powered_addr_change(struct ieee80211_sub_if_data *sdata if (roc->sdata != sdata) continue; - if (roc->started) { - ret = -EBUSY; - goto unlock; - } + if (roc->started) + return -EBUSY; } /* And if this iface is scanning */ @@ -231,7 +228,7 @@ static int ieee80211_can_powered_addr_change(struct ieee80211_sub_if_data *sdata scan_sdata = rcu_dereference_protected(local->scan_sdata, lockdep_is_held(&local->hw.wiphy->mtx)); if (sdata == scan_sdata) - ret = -EBUSY; + return -EBUSY; } switch (sdata->vif.type) { @@ -240,13 +237,15 @@ static int ieee80211_can_powered_addr_change(struct ieee80211_sub_if_data *sdata /* More interface types could be added here but changing the * address while powered makes the most sense in client modes. */ + if (sdata->u.mgd.auth_data || sdata->u.mgd.assoc_data || + sdata->u.mgd.associated) + return -EBUSY; break; default: - ret = -EOPNOTSUPP; + return -EOPNOTSUPP; } -unlock: - return ret; + return 0; } static int _ieee80211_change_mac(struct ieee80211_sub_if_data *sdata,
On 7/23/24 2:35 PM, Johannes Berg wrote:
> So more like below.
Thanks.
BTW please also look at https://lore.kernel.org/linux-hardening/202407161121.C00AC44@keescook/T/#t.
Dmitry
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 1e9389c49a57..8224257e5d93 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -1284,7 +1284,9 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) void ieee80211_sta_debugfs_remove(struct sta_info *sta) { - debugfs_remove_recursive(sta->debugfs_dir); + if (!sta->debugfs_shared) + debugfs_remove_recursive(sta->debugfs_dir); + sta->debugfs_shared = false; sta->debugfs_dir = NULL; } @@ -1319,6 +1321,7 @@ void ieee80211_link_sta_debugfs_add(struct link_sta_info *link_sta) return; link_sta->debugfs_dir = link_sta->sta->debugfs_dir; + link_sta->sta->debugfs_shared = true; } DEBUGFS_ADD(ht_capa); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 9195d5a2de0a..d76ba36ca794 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -708,6 +708,7 @@ struct sta_info { #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *debugfs_dir; + bool debugfs_shared; #endif struct codel_params cparams; So what about managing debugfs entries wih krefs? E.g.: diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 9195d5a2de0a..1f4561533530 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -466,6 +466,15 @@ struct ieee80211_fragment_cache { unsigned int next; }; +#ifdef CONFIG_MAC80211_DEBUGFS + +struct sta_debugfs_entry { + struct dentry *debugfs_dir; + struct kref kref; +}; + +#endif /* CONFIG_MAC80211_DEBUGFS */ + /* * The bandwidth threshold below which the per-station CoDel parameters will be * scaled to be more lenient (to prevent starvation of slow stations). This @@ -563,7 +572,7 @@ struct link_sta_info { enum ieee80211_sta_rx_bandwidth cur_max_bandwidth; #ifdef CONFIG_MAC80211_DEBUGFS - struct dentry *debugfs_dir; + struct sta_debugfs_entry *debugfs_entry; #endif struct ieee80211_link_sta *pub; @@ -707,7 +716,7 @@ struct sta_info { struct sta_ampdu_mlme ampdu_mlme; #ifdef CONFIG_MAC80211_DEBUGFS - struct dentry *debugfs_dir; + struct sta_debugfs_entry *debugfs_entry; #endif struct codel_params cparams;