diff mbox series

wifi: mac80211: fix cfg80211_bss always hold when assoc response fail for MLO connection

Message ID 20230821061355.18168-1-quic_wgong@quicinc.com (mailing list archive)
State Changes Requested
Delegated to: Johannes Berg
Headers show
Series wifi: mac80211: fix cfg80211_bss always hold when assoc response fail for MLO connection | expand

Commit Message

Wen Gong Aug. 21, 2023, 6:13 a.m. UTC
When connect to MLO AP with more than one link, and the assoc response of
AP is not success, then cfg80211_unhold_bss() is not called for all the
links' cfg80211_bss except the primary link which means the link used by
the latest successful association request. Thus the hold value of the
cfg80211_bss is not reset to 0 after the assoc fail, and then the
__cfg80211_unlink_bss() will not be called for the cfg80211_bss by
__cfg80211_bss_expire().

Then the AP always looks exist even the AP is shutdown or reconfigured
to another type, then it will lead error while connecting it again.

The detail info are as below.

When connect with muti-links AP, cfg80211_hold_bss() is called by
cfg80211_mlme_assoc() for each cfg80211_bss of all the links. When
assoc response from AP is not success(such as status_code==1), the
ieee80211_link_data of non-primary link(sdata->link[link_id]) is NULL
because ieee80211_assoc_success()->ieee80211_vif_update_links() is
not called for the links.

Then struct cfg80211_rx_assoc_resp resp in cfg80211_rx_assoc_resp() and
struct cfg80211_connect_resp_params cr in __cfg80211_connect_result()
will only have the data of the primary link, and finally function
cfg80211_connect_result_release_bsses() only call cfg80211_unhold_bss()
for the primary link. Then cfg80211_bss of the other links will never free
because its hold is always > 0 now.

Hence assign value for the bss and status from assoc_data since it is
valid for this case. Also assign value of addr from assoc_data when the
link is NULL because the addrs of assoc_data and link both represent the
local link addr and they are same value for success connection.

It is 100% ratio to reproduce the issue with this change.
Add "mgmt->u.assoc_resp.status_code = 1" in ieee80211_rx_mgmt_assoc_resp().

Fixes: 81151ce462e5 ("wifi: mac80211: support MLO authentication/association with one link")
Signed-off-by: Wen Gong <quic_wgong@quicinc.com>
---
 net/mac80211/mlme.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)


base-commit: 3f257461ab0ab19806bae2bfde4c3cd88dbf050e

Comments

Johannes Berg Aug. 21, 2023, 7:40 a.m. UTC | #1
On Mon, 2023-08-21 at 02:13 -0400, Wen Gong wrote:
> 
> +++ b/net/mac80211/mlme.c
> @@ -5429,17 +5429,22 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
>  	for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
>  		struct ieee80211_link_data *link;
>  
> -		link = sdata_dereference(sdata->link[link_id], sdata);
> -		if (!link)
> -			continue;
> -
>  		if (!assoc_data->link[link_id].bss)
>  			continue;
>  
>  		resp.links[link_id].bss = assoc_data->link[link_id].bss;
> -		resp.links[link_id].addr = link->conf->addr;
>  		resp.links[link_id].status = assoc_data->link[link_id].status;
>  
> +		link = sdata_dereference(sdata->link[link_id], sdata);
> +
> +		if (!link) {
> +			/* use the addr of assoc_data link which is set in ieee80211_mgd_assoc() */
> +			resp.links[link_id].addr = assoc_data->link[link_id].addr;

As I mentioned before, this cannot be done - it introduces use-after-
free since assoc_data is freed after the loop, and the
cfg80211_rx_assoc_resp() is after that.

> +			continue;
> +		}
> +
> +		resp.links[link_id].addr = link->conf->addr;
> 

Also, I don't see that we need to use two different assignments for the
two cases.

johannes
Wen Gong Aug. 21, 2023, 7:48 a.m. UTC | #2
On 8/21/2023 3:40 PM, Johannes Berg wrote:
> On Mon, 2023-08-21 at 02:13 -0400, Wen Gong wrote:
>> +++ b/net/mac80211/mlme.c
>> @@ -5429,17 +5429,22 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
>>   	for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
>>   		struct ieee80211_link_data *link;
>>   
>> -		link = sdata_dereference(sdata->link[link_id], sdata);
>> -		if (!link)
>> -			continue;
>> -
>>   		if (!assoc_data->link[link_id].bss)
>>   			continue;
>>   
>>   		resp.links[link_id].bss = assoc_data->link[link_id].bss;
>> -		resp.links[link_id].addr = link->conf->addr;
>>   		resp.links[link_id].status = assoc_data->link[link_id].status;
>>   
>> +		link = sdata_dereference(sdata->link[link_id], sdata);
>> +
>> +		if (!link) {
>> +			/* use the addr of assoc_data link which is set in ieee80211_mgd_assoc() */
>> +			resp.links[link_id].addr = assoc_data->link[link_id].addr;
> As I mentioned before, this cannot be done - it introduces use-after-
> free since assoc_data is freed after the loop, and the
> cfg80211_rx_assoc_resp() is after that.

So I want to change the "const u8 *addr" to "u8 addr[ETH_ALEN] 
__aligned(2);" of struct

cfg80211_rx_assoc_resp and copy the value, then no use-after-free, is it OK?

>> +			continue;
>> +		}
>> +
>> +		resp.links[link_id].addr = link->conf->addr;
>>
> Also, I don't see that we need to use two different assignments for the
> two cases.
Then I will change to both use "assoc_data->link[link_id].addr", is it OK?
> johannes
Johannes Berg Aug. 21, 2023, 8:01 a.m. UTC | #3
On Mon, 2023-08-21 at 15:48 +0800, Wen Gong wrote:
> On 8/21/2023 3:40 PM, Johannes Berg wrote:
> > On Mon, 2023-08-21 at 02:13 -0400, Wen Gong wrote:
> > > +++ b/net/mac80211/mlme.c
> > > @@ -5429,17 +5429,22 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
> > >   	for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
> > >   		struct ieee80211_link_data *link;
> > >   
> > > -		link = sdata_dereference(sdata->link[link_id], sdata);
> > > -		if (!link)
> > > -			continue;
> > > -
> > >   		if (!assoc_data->link[link_id].bss)
> > >   			continue;
> > >   
> > >   		resp.links[link_id].bss = assoc_data->link[link_id].bss;
> > > -		resp.links[link_id].addr = link->conf->addr;
> > >   		resp.links[link_id].status = assoc_data->link[link_id].status;
> > >   
> > > +		link = sdata_dereference(sdata->link[link_id], sdata);
> > > +
> > > +		if (!link) {
> > > +			/* use the addr of assoc_data link which is set in ieee80211_mgd_assoc() */
> > > +			resp.links[link_id].addr = assoc_data->link[link_id].addr;
> > As I mentioned before, this cannot be done - it introduces use-after-
> > free since assoc_data is freed after the loop, and the
> > cfg80211_rx_assoc_resp() is after that.
> 
> So I want to change the "const u8 *addr" to "u8 addr[ETH_ALEN] 
> __aligned(2);" of struct
> 
> cfg80211_rx_assoc_resp and copy the value, then no use-after-free, is it OK?

Yeah I guess that works too.

> 
> > > +			continue;
> > > +		}
> > > +
> > > +		resp.links[link_id].addr = link->conf->addr;
> > > 
> > Also, I don't see that we need to use two different assignments for the
> > two cases.
> Then I will change to both use "assoc_data->link[link_id].addr", is it OK?

Sure

johannes
diff mbox series

Patch

diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index f93eb38ae0b8..370b2cd0cc6d 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -5429,17 +5429,22 @@  static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
 	for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
 		struct ieee80211_link_data *link;
 
-		link = sdata_dereference(sdata->link[link_id], sdata);
-		if (!link)
-			continue;
-
 		if (!assoc_data->link[link_id].bss)
 			continue;
 
 		resp.links[link_id].bss = assoc_data->link[link_id].bss;
-		resp.links[link_id].addr = link->conf->addr;
 		resp.links[link_id].status = assoc_data->link[link_id].status;
 
+		link = sdata_dereference(sdata->link[link_id], sdata);
+
+		if (!link) {
+			/* use the addr of assoc_data link which is set in ieee80211_mgd_assoc() */
+			resp.links[link_id].addr = assoc_data->link[link_id].addr;
+			continue;
+		}
+
+		resp.links[link_id].addr = link->conf->addr;
+
 		/* get uapsd queues configuration - same for all links */
 		resp.uapsd_queues = 0;
 		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)