[PATCH v2] AP MLD: never keep a per-link wpa_sm

Benjamin Berg benjamin at sipsolutions.net
Mon Jun 23 04:28:11 PDT 2025


From: Benjamin Berg <benjamin.berg at intel.com>

The __checK_assoc_ies function needs to create a per-link wpa_sm
temporarily. However, later on sta->wpa_sm should just be a reference to
the main wpa_sm.

Change the code so that the temporary state is self-contained to
__check_assoc_ies. This fixes a memory leak should __check_assoc_ies
return an error.

Also change the function to pass the wpa_sm for the assoc_link instead
of just a link boolean. The internal lookup appears to not have worked
and this also simplifies the code.

Fixes: 5f5db9366cde ("AP: MLO: Process Multi-Link element from (Re)Association Request frame")
Fixes: 84d2a36da02a ("AP MLD: Require same AKM and pairwise cipher for all links")

Signed-off-by: Benjamin Berg <benjamin.berg at intel.com>

---

v2:
- rebased

Signed-off-by: Benjamin Berg <benjamin.berg at intel.com>
---
 src/ap/ieee802_11.c | 135 ++++++++++++++++++++++++++------------------
 1 file changed, 81 insertions(+), 54 deletions(-)

diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 866a1c1c89..7c37d6a66a 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -4220,56 +4220,56 @@ static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta,
 static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 			     const u8 *ies, size_t ies_len,
 			     struct ieee802_11_elems *elems,
-			     enum link_parse_type type, bool link)
+			     enum link_parse_type type,
+			     struct wpa_state_machine *assoc_wpa_sm)
 {
 	int resp;
 	const u8 *wpa_ie;
 	size_t wpa_ie_len;
 	const u8 *p2p_dev_addr = NULL;
-	struct hostapd_data *assoc_hapd;
-	struct sta_info *assoc_sta = NULL;
 
 	if (type != LINK_PARSE_RECONF) {
 		resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len);
 		if (resp != WLAN_STATUS_SUCCESS)
-			return resp;
+			goto out;
 	}
 
 	resp = check_wmm(hapd, sta, elems->wmm, elems->wmm_len);
 	if (resp != WLAN_STATUS_SUCCESS)
-		return resp;
+		goto out;
 	resp = check_ext_capab(hapd, sta, elems->ext_capab,
 			       elems->ext_capab_len);
 	if (resp != WLAN_STATUS_SUCCESS)
-		return resp;
+		goto out;
 	resp = copy_supp_rates(hapd, sta, elems);
 	if (resp != WLAN_STATUS_SUCCESS)
-		return resp;
+		goto out;
 
 	resp = check_multi_ap(hapd, sta, elems->multi_ap, elems->multi_ap_len);
 	if (resp != WLAN_STATUS_SUCCESS)
-		return resp;
+		goto out;
 
 	resp = copy_sta_ht_capab(hapd, sta, elems->ht_capabilities);
 	if (resp != WLAN_STATUS_SUCCESS)
-		return resp;
+		goto out;
 	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
 	    !(sta->flags & WLAN_STA_HT)) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_INFO, "Station does not support "
 			       "mandatory HT PHY - reject association");
-		return WLAN_STATUS_ASSOC_DENIED_NO_HT;
+		resp = WLAN_STATUS_ASSOC_DENIED_NO_HT;
+		goto out;
 	}
 
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->iconf->ieee80211ac) {
 		resp = copy_sta_vht_capab(hapd, sta, elems->vht_capabilities);
 		if (resp != WLAN_STATUS_SUCCESS)
-			return resp;
+			goto out;
 
 		resp = set_sta_vht_opmode(hapd, sta, elems->opmode_notif);
 		if (resp != WLAN_STATUS_SUCCESS)
-			return resp;
+			goto out;
 	}
 
 	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht &&
@@ -4277,14 +4277,15 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_INFO, "Station does not support "
 			       "mandatory VHT PHY - reject association");
-		return WLAN_STATUS_ASSOC_DENIED_NO_VHT;
+		resp = WLAN_STATUS_ASSOC_DENIED_NO_VHT;
+		goto out;
 	}
 
 	if (hapd->conf->vendor_vht && !elems->vht_capabilities) {
 		resp = copy_sta_vendor_vht(hapd, sta, elems->vendor_vht,
 					   elems->vendor_vht_len);
 		if (resp != WLAN_STATUS_SUCCESS)
-			return resp;
+			goto out;
 	}
 #endif /* CONFIG_IEEE80211AC */
 #ifdef CONFIG_IEEE80211AX
@@ -4293,14 +4294,15 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 					 elems->he_capabilities,
 					 elems->he_capabilities_len);
 		if (resp != WLAN_STATUS_SUCCESS)
-			return resp;
+			goto out;
 
 		if (hapd->iconf->require_he && !(sta->flags & WLAN_STA_HE)) {
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE80211,
 				       HOSTAPD_LEVEL_INFO,
 				       "Station does not support mandatory HE PHY - reject association");
-			return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+			resp = WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+			goto out;
 		}
 
 		if (is_6ghz_op_class(hapd->iconf->op_class)) {
@@ -4309,12 +4311,13 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 					       HOSTAPD_MODULE_IEEE80211,
 					       HOSTAPD_LEVEL_INFO,
 					       "Station does not support mandatory HE PHY - reject association");
-				return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+				resp = WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
+				goto out;
 			}
 			resp = copy_sta_he_6ghz_capab(hapd, sta,
 						      elems->he_6ghz_band_cap);
 			if (resp != WLAN_STATUS_SUCCESS)
-				return resp;
+				goto out;
 		}
 	}
 #endif /* CONFIG_IEEE80211AX */
@@ -4326,12 +4329,12 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 					  elems->eht_capabilities,
 					  elems->eht_capabilities_len);
 		if (resp != WLAN_STATUS_SUCCESS)
-			return resp;
+			goto out;
 
-		if (!link) {
+		if (!assoc_wpa_sm) {
 			resp = hostapd_process_ml_assoc_req(hapd, elems, sta);
 			if (resp != WLAN_STATUS_SUCCESS)
-				return resp;
+				goto out;
 		}
 	}
 #endif /* CONFIG_IEEE80211BE */
@@ -4388,7 +4391,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 		if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) {
 			wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in "
 				   "(Re)Association Request - reject");
-			return WLAN_STATUS_INVALID_IE;
+			resp = WLAN_STATUS_INVALID_IE;
+			goto out;
 		}
 	} else if (hapd->conf->wps_state && wpa_ie == NULL) {
 		wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in "
@@ -4400,7 +4404,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_INFO,
 			       "No WPA/RSN IE in association request");
-		return WLAN_STATUS_INVALID_IE;
+		resp = WLAN_STATUS_INVALID_IE;
+		goto out;
 	}
 
 	if (hapd->conf->wpa && wpa_ie) {
@@ -4414,10 +4419,10 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 		wpa_ie_len += 2;
 
 		if (!sta->wpa_sm) {
-			if (!link)
-				assoc_sta = hostapd_ml_get_assoc_sta(
-					hapd, sta, &assoc_hapd);
-
+			/* NOTE: For links other than the assoc-link the
+			 * separate wpa_sm is only allocated internally to this
+			 * function.
+			 */
 			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
 							sta->addr,
 							p2p_dev_addr);
@@ -4425,7 +4430,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 			if (!sta->wpa_sm) {
 				wpa_printf(MSG_WARNING,
 					   "Failed to initialize RSN state machine");
-				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto out;
 			}
 		}
 
@@ -4452,11 +4458,11 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 					  0,
 					  elems->mdie, elems->mdie_len,
 					  elems->owe_dh, elems->owe_dh_len,
-					  assoc_sta ? assoc_sta->wpa_sm : NULL,
+					  assoc_wpa_sm,
 					  ap_sta_is_mld(hapd, sta));
 		resp = wpa_res_to_status_code(res);
 		if (resp != WLAN_STATUS_SUCCESS)
-			return resp;
+			goto out;
 
 		if (wpa_auth_uses_mfp(sta->wpa_sm))
 			sta->flags |= WLAN_STA_MFP;
@@ -4475,17 +4481,18 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 					   "to use association (not "
 					   "re-association) with FT auth_alg",
 					   MAC2STR(sta->addr));
-				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto out;
 			}
 
 			resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies,
 						       ies_len);
 			if (resp != WLAN_STATUS_SUCCESS)
-				return resp;
+				goto out;
 		}
 #endif /* CONFIG_IEEE80211R_AP */
 
-		if (link)
+		if (assoc_wpa_sm)
 			goto skip_sae_owe;
 #ifdef CONFIG_SAE
 		if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae &&
@@ -4500,7 +4507,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 				wpa_printf(MSG_DEBUG,
 					   "SAE: No PMKSA cache entry found for "
 					   MACSTR, MAC2STR(sta->addr));
-				return WLAN_STATUS_INVALID_PMKID;
+				resp = WLAN_STATUS_INVALID_PMKID;
+				goto out;
 			}
 			wpa_printf(MSG_DEBUG, "SAE: " MACSTR
 				   " using PMKSA caching", MAC2STR(sta->addr));
@@ -4511,7 +4519,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 			wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use "
 				   "SAE AKM after non-SAE auth_alg %u",
 				   MAC2STR(sta->addr), sta->auth_alg);
-			return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+			resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+			goto out;
 		}
 
 		if (hapd->conf->sae_pwe == SAE_PWE_BOTH &&
@@ -4522,7 +4531,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 			wpa_printf(MSG_INFO, "SAE: " MACSTR
 				   " indicates support for SAE H2E, but did not use it",
 				   MAC2STR(sta->addr));
-			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto out;
 		}
 #endif /* CONFIG_SAE */
 
@@ -4533,7 +4543,7 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 			resp = owe_process_assoc_req(hapd, sta, elems->owe_dh,
 						     elems->owe_dh_len);
 			if (resp != WLAN_STATUS_SUCCESS)
-				return resp;
+				goto out;
 		}
 #endif /* CONFIG_OWE */
 	skip_sae_owe:
@@ -4561,7 +4571,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 					    elems->owe_dh_len) < 0) {
 				dpp_pfs_free(sta->dpp_pfs);
 				sta->dpp_pfs = NULL;
-				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto out;
 			}
 		}
 
@@ -4577,7 +4588,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 				       HOSTAPD_LEVEL_INFO,
 				       "Station tried to use TKIP with HT "
 				       "association");
-			return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+			resp = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+			goto out;
 		}
 
 		wpa_auth_set_ssid_protection(
@@ -4609,7 +4621,8 @@ skip_wpa_ies:
 			wpa_printf(MSG_DEBUG,
 				   "HS 2.0: PMF not negotiated by release %d station "
 				   MACSTR, release, MAC2STR(sta->addr));
-			return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+			resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+			goto out;
 		}
 	} else {
 		sta->hs20_ie = NULL;
@@ -4640,7 +4653,8 @@ skip_wpa_ies:
 	    hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 		wpa_printf(MSG_INFO,
 			   "MBO: Reject WPA2 association without PMF");
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto out;
 	}
 #endif /* CONFIG_MBO */
 
@@ -4658,14 +4672,17 @@ skip_wpa_ies:
 		if (hostapd_drv_channel_info(hapd, &ci) != 0) {
 			wpa_printf(MSG_WARNING,
 				   "Failed to get channel info to validate received OCI in FILS (Re)Association Request frame");
-			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto out;
 		}
 
 		if (get_sta_tx_parameters(sta->wpa_sm,
 					  channel_width_to_int(ci.chanwidth),
 					  ci.seg1_idx, &tx_chanwidth,
-					  &tx_seg1_idx) < 0)
-			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+					  &tx_seg1_idx) < 0) {
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto out;
+		}
 
 		res = ocv_verify_tx_params(elems->oci, elems->oci_len, &ci,
 					   tx_chanwidth, tx_seg1_idx);
@@ -4681,7 +4698,8 @@ skip_wpa_ies:
 			wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="
 				MACSTR " frame=fils-reassoc-req error=%s",
 				MAC2STR(sta->addr), ocv_errorstr);
-			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto out;
 		}
 	}
 #endif /* CONFIG_FILS && CONFIG_OCV */
@@ -4720,7 +4738,21 @@ skip_wpa_ies:
 		hostapd_wfa_capab(hapd, sta, elems->wfa_capab,
 				  elems->wfa_capab + elems->wfa_capab_len);
 
-	return WLAN_STATUS_SUCCESS;
+out:
+	if (resp != WLAN_STATUS_SUCCESS || assoc_wpa_sm) {
+		wpa_auth_sta_deinit(sta->wpa_sm);
+
+		/* Only keep a reference to the main wpa_sm and drop the
+		 * per-link instance.
+		 * This reference is needed during group rekey handling.
+		 */
+		if (resp == WLAN_STATUS_SUCCESS)
+			sta->wpa_sm = assoc_wpa_sm;
+		else
+			sta->wpa_sm = NULL;
+	}
+
+	return resp;
 }
 
 
@@ -4737,7 +4769,7 @@ static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
-	return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, type, false);
+	return __check_assoc_ies(hapd, sta, ies, ies_len, &elems, type, NULL);
 }
 
 
@@ -4860,7 +4892,8 @@ int ieee80211_ml_process_link(struct hostapd_data *hapd,
 	sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
 	ap_sta_set_mld(sta, true);
 
-	status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, type, true);
+	status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, type,
+				   origin_sta->wpa_sm);
 	if (status != WLAN_STATUS_SUCCESS) {
 		wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
 		goto out;
@@ -4896,12 +4929,6 @@ int ieee80211_ml_process_link(struct hostapd_data *hapd,
 			ieee802_11_update_beacons(hapd->iface);
 	}
 
-	/* Maintain state machine reference on all link STAs, this is needed
-	 * during group rekey handling.
-	 */
-	wpa_auth_sta_deinit(sta->wpa_sm);
-	sta->wpa_sm = origin_sta->wpa_sm;
-
 	/*
 	 * Do not initialize the EAPOL state machine.
 	 * TODO: Maybe it is needed?
-- 
2.49.0




More information about the Hostap mailing list