Merge branch 'bugfix/concurrency_hostapd_sae_queue_v5.4' into 'release/v5.4'

fix(wifi): Fix concurrency issues for hostapd_sae_queue for softap (v5.4)

See merge request espressif/esp-idf!48366
This commit is contained in:
Jiang Jiang Jian
2026-05-18 14:06:57 +08:00
7 changed files with 494 additions and 109 deletions
@@ -61,6 +61,9 @@ void *hostap_init(void)
wifi_pmf_config_t pmf_cfg = {0};
uint8_t authmode;
uint8_t sae_ext = 0;
#ifdef CONFIG_SAE
struct hostapd_sae_commit_queue *q, *tmp;
#endif
sae_ext = esp_wifi_ap_get_sae_ext_config_internal();
@@ -205,7 +208,6 @@ void *hostap_init(void)
}
#ifdef CONFIG_SAE
dl_list_init(&hapd->sae_commit_queue);
auth_conf->sae_require_mfp = 1;
#endif /* CONFIG_SAE */
@@ -225,6 +227,35 @@ void *hostap_init(void)
return (void *)hapd;
fail:
#ifdef CONFIG_SAE
if (hapd->sta_list_lock) {
if (wpa3_hostap_auth_deinit()) {
if (g_wpa3_hostap_auth_api_lock) {
/* Block until WPA3 task gives the API lock after SIG_TASK_DEL teardown */
WPA3_HOSTAP_AUTH_API_LOCK();
WPA3_HOSTAP_AUTH_API_UNLOCK();
}
} else {
wpa_printf(MSG_ERROR,
"hostap_init fail: failed to post SIG_TASK_DEL, skipping WPA3 API lock wait");
}
HOSTAPD_STA_LIST_LOCK(hapd);
/*
* hostap_init() failed before global_hapd was assigned, so the WPA3
* hostap task has no registered hapd to drain this queue on exit;
* free queued commits here before releasing hapd.
*/
dl_list_for_each_safe(q, tmp, &hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list) {
dl_list_del(&q->list);
os_free(q);
}
HOSTAPD_STA_LIST_UNLOCK(hapd);
os_mutex_delete(hapd->sta_list_lock);
hapd->sta_list_lock = NULL;
}
#endif /* CONFIG_SAE */
if (hapd->conf->ssid.wpa_passphrase != NULL) {
os_free(hapd->conf->ssid.wpa_passphrase);
}
@@ -254,19 +285,6 @@ void hostapd_cleanup(struct hostapd_data *hapd)
hapd->conf = NULL;
}
#ifdef CONFIG_SAE
struct hostapd_sae_commit_queue *q, *tmp;
if (!dl_list_empty(&hapd->sae_commit_queue)) {
dl_list_for_each_safe(q, tmp, &hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list) {
dl_list_del(&q->list);
os_free(q);
}
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_WPS_REGISTRAR
if (esp_wifi_get_wps_type_internal() != WPS_TYPE_DISABLE ||
esp_wifi_get_wps_status_internal() != WPS_STATUS_DISABLE) {
@@ -292,11 +310,15 @@ bool hostap_deinit(void *data)
wifi_ap_wps_disable_internal();
#endif
#ifdef CONFIG_SAE
wpa3_hostap_auth_deinit();
/* Wait till lock is released by wpa3 task */
if (g_wpa3_hostap_auth_api_lock &&
WPA3_HOSTAP_AUTH_API_LOCK() == pdTRUE) {
WPA3_HOSTAP_AUTH_API_UNLOCK();
if (wpa3_hostap_auth_deinit()) {
/* Block until WPA3 task gives the API lock after SIG_TASK_DEL teardown */
if (g_wpa3_hostap_auth_api_lock) {
WPA3_HOSTAP_AUTH_API_LOCK();
WPA3_HOSTAP_AUTH_API_UNLOCK();
}
} else {
wpa_printf(MSG_ERROR,
"hostap_deinit: failed to post SIG_TASK_DEL, skipping WPA3 API lock wait");
}
#endif /* CONFIG_SAE */
@@ -457,14 +479,31 @@ send_resp:
#ifdef CONFIG_WPS_REGISTRAR
static void ap_free_sta_timeout(void *ctx, void *data)
{
struct hostapd_data *hapd = (struct hostapd_data *) ctx;
u8 *addr = (u8 *) data;
struct sta_info *sta = ap_get_sta(hapd, addr);
struct hostapd_data *hapd = (struct hostapd_data *)ctx;
u8 *addr = (u8 *)data;
struct sta_info *sta;
HOSTAPD_STA_LIST_LOCK(hapd);
sta = ap_get_sta_internal(hapd, addr);
if (sta) {
#ifdef CONFIG_SAE
if (sta->lock) {
if (os_semphr_take(sta->lock, 0)) {
HOSTAPD_STA_LIST_UNLOCK(hapd);
ap_free_sta(hapd, sta);
} else {
atomic_store(&sta->remove_pending, true);
HOSTAPD_STA_LIST_UNLOCK(hapd);
}
goto done;
}
#endif /* CONFIG_SAE */
HOSTAPD_STA_LIST_UNLOCK(hapd);
ap_free_sta(hapd, sta);
} else {
HOSTAPD_STA_LIST_UNLOCK(hapd);
}
done:
os_free(addr);
}
#endif
@@ -472,42 +511,51 @@ static void ap_free_sta_timeout(void *ctx, void *data)
bool wpa_ap_remove(u8* bssid)
{
struct hostapd_data *hapd = hostapd_get_hapd_data();
struct sta_info *sta;
if (!hapd) {
return false;
}
struct sta_info *sta = ap_get_sta(hapd, bssid);
HOSTAPD_STA_LIST_LOCK(hapd);
sta = ap_get_sta_internal(hapd, bssid);
if (!sta) {
HOSTAPD_STA_LIST_UNLOCK(hapd);
return false;
}
#ifdef CONFIG_SAE
if (sta->lock) {
if (os_semphr_take(sta->lock, 0)) {
ap_free_sta(hapd, sta);
} else {
sta->remove_pending = true;
}
return true;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_WPS_REGISTRAR
wpa_printf(MSG_DEBUG, "wps_status=%d", wps_get_status());
if (wps_get_status() == WPS_STATUS_PENDING) {
HOSTAPD_STA_LIST_UNLOCK(hapd);
u8 *addr = os_malloc(ETH_ALEN);
if (!addr) {
return false;
}
os_memcpy(addr, sta->addr, ETH_ALEN);
os_memcpy(addr, bssid, ETH_ALEN);
if (eloop_register_timeout(0, 10000, ap_free_sta_timeout, hapd, addr) != 0) {
os_free(addr);
return false;
}
} else
#endif
ap_free_sta(hapd, sta);
return true;
}
#endif /* CONFIG_WPS_REGISTRAR */
#ifdef CONFIG_SAE
if (sta->lock) {
if (os_semphr_take(sta->lock, 0)) {
HOSTAPD_STA_LIST_UNLOCK(hapd);
ap_free_sta(hapd, sta);
} else {
atomic_store(&sta->remove_pending, true);
HOSTAPD_STA_LIST_UNLOCK(hapd);
}
return true;
}
#endif /* CONFIG_SAE */
HOSTAPD_STA_LIST_UNLOCK(hapd);
ap_free_sta(hapd, sta);
return true;
}
@@ -197,6 +197,11 @@ static esp_err_t wpa3_build_sae_confirm(void)
void esp_wpa3_free_sae_data(void)
{
if (g_sae_token) {
wpabuf_free(g_sae_token);
g_sae_token = NULL;
}
if (g_sae_commit) {
wpabuf_free(g_sae_commit);
g_sae_commit = NULL;
@@ -416,6 +421,7 @@ void esp_wifi_unregister_wpa3_cb(void)
static TaskHandle_t g_wpa3_hostap_task_hdl = NULL;
static QueueHandle_t g_wpa3_hostap_evt_queue = NULL;
/* Global API lock - created once, never deleted */
SemaphoreHandle_t g_wpa3_hostap_auth_api_lock = NULL;
int wpa3_hostap_post_evt(uint32_t evt_id, uint32_t data)
@@ -436,13 +442,21 @@ int wpa3_hostap_post_evt(uint32_t evt_id, uint32_t data)
wpa_printf(MSG_DEBUG, "g_wpa3_hostap_auth_api_lock not found");
return ESP_FAIL;
}
if (evt.id == SIG_WPA3_RX_CONFIRM || evt.id == SIG_TASK_DEL) {
if (evt.id == SIG_WPA3_RX_CONFIRM) {
/* prioritising confirm for completing handshake for committed sta */
if (os_queue_send_to_front(g_wpa3_hostap_evt_queue, &evt, 0) != pdPASS) {
WPA3_HOSTAP_AUTH_API_UNLOCK();
wpa_printf(MSG_DEBUG, "failed to add msg to queue front");
return ESP_FAIL;
}
} else if (evt.id == SIG_TASK_DEL) {
/* Wi-Fi blocks until SIG_TASK_DEL is queued; only Wi-Fi calls wpa3_hostap_post_evt. */
/* Hence there will be no deadlock for g_wpa3_hostap_auth_api_lock. */
if (os_queue_send_to_front(g_wpa3_hostap_evt_queue, &evt, portMAX_DELAY) != pdPASS) {
WPA3_HOSTAP_AUTH_API_UNLOCK();
wpa_printf(MSG_DEBUG, "failed to add msg to queue front");
return ESP_FAIL;
}
} else {
if (os_queue_send(g_wpa3_hostap_evt_queue, &evt, 0) != pdPASS) {
WPA3_HOSTAP_AUTH_API_UNLOCK();
@@ -463,17 +477,26 @@ static void wpa3_process_rx_commit(wpa3_hostap_auth_event_t *evt)
struct hostapd_data *hapd = (struct hostapd_data *)esp_wifi_get_hostap_private_internal();
struct sta_info *sta = NULL;
int ret;
if (!hapd || !hapd->sta_list_lock) {
wpa_printf(MSG_ERROR, "hapd or sta_list_lock not initialized in %s", __func__);
return;
}
HOSTAPD_STA_LIST_LOCK(hapd);
frm = dl_list_first(&hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list);
if (!frm) {
HOSTAPD_STA_LIST_UNLOCK(hapd);
return;
}
dl_list_del(&frm->list);
wpa_printf(MSG_DEBUG, "SAE: Process next available message from queue");
sta = ap_get_sta(hapd, frm->bssid);
sta = ap_get_sta_internal(hapd, frm->bssid);
if (!sta) {
HOSTAPD_STA_LIST_UNLOCK(hapd);
sta = ap_sta_add(hapd, frm->bssid);
if (!sta) {
wpa_printf(MSG_DEBUG, "ap_sta_add() failed");
@@ -483,19 +506,30 @@ static void wpa3_process_rx_commit(wpa3_hostap_auth_event_t *evt)
0) != 0) {
wpa_printf(MSG_INFO, "esp_send_sae_auth_reply: send failed");
}
goto free;
os_free(frm);
return;
}
HOSTAPD_STA_LIST_LOCK(hapd);
sta = ap_get_sta_internal(hapd, frm->bssid);
if (!sta) {
HOSTAPD_STA_LIST_UNLOCK(hapd);
os_free(frm);
return;
}
}
if (sta->lock && os_semphr_take(sta->lock, 0)) {
sta->sae_commit_processing = true;
atomic_store(&sta->sae_commit_processing, true);
HOSTAPD_STA_LIST_UNLOCK(hapd);
ret = handle_auth_sae(hapd, sta, frm->msg, frm->len, frm->bssid, frm->auth_transaction, frm->status);
if (sta->remove_pending) {
if (atomic_load(&sta->remove_pending)) {
ap_free_sta(hapd, sta);
goto free;
os_free(frm);
return;
}
sta->sae_commit_processing = false;
atomic_store(&sta->sae_commit_processing, false);
os_semphr_give(sta->lock);
uint16_t aid = 0;
if (ret != WLAN_STATUS_SUCCESS &&
@@ -505,9 +539,11 @@ static void wpa3_process_rx_commit(wpa3_hostap_auth_event_t *evt)
esp_wifi_ap_deauth_internal(frm->bssid, ret);
}
}
os_free(frm);
return;
}
free:
HOSTAPD_STA_LIST_UNLOCK(hapd);
os_free(frm);
}
@@ -517,45 +553,70 @@ static void wpa3_process_rx_confirm(wpa3_hostap_auth_event_t *evt)
struct sta_info *sta = NULL;
int ret = WLAN_STATUS_SUCCESS;
struct sae_hostap_confirm_data *frm = (struct sae_hostap_confirm_data *)evt->data;
if (!frm) {
return;
}
sta = ap_get_sta(hapd, frm->bssid);
if (!sta) {
if (!hapd || !hapd->sta_list_lock) {
wpa_printf(MSG_ERROR, "hapd or sta_list_lock not initialized in %s", __func__);
os_free(frm);
return;
}
if (sta->lock && os_semphr_take(sta->lock, 0)) {
ret = handle_auth_sae(hapd, sta, frm->msg, frm->len, frm->bssid, frm->auth_transaction, frm->status);
HOSTAPD_STA_LIST_LOCK(hapd);
sta = ap_get_sta_internal(hapd, frm->bssid);
if (!sta) {
HOSTAPD_STA_LIST_UNLOCK(hapd);
os_free(frm);
return;
}
if (sta->remove_pending) {
if (!sta->lock) {
wpa_printf(MSG_DEBUG, "SAE: sta->lock is NULL for " MACSTR, MAC2STR(frm->bssid));
HOSTAPD_STA_LIST_UNLOCK(hapd);
os_free(frm);
return;
}
if (os_semphr_take(sta->lock, 0) != pdTRUE) {
HOSTAPD_STA_LIST_UNLOCK(hapd);
os_free(frm);
return;
}
HOSTAPD_STA_LIST_UNLOCK(hapd);
ret = handle_auth_sae(hapd, sta, frm->msg, frm->len, frm->bssid, frm->auth_transaction, frm->status);
if (atomic_load(&sta->remove_pending)) {
ap_free_sta(hapd, sta);
goto done;
}
if (ret == WLAN_STATUS_SUCCESS) {
if (sta->sae_data && esp_send_sae_auth_reply(hapd, sta->addr, frm->bssid, WLAN_AUTH_SAE, 2,
WLAN_STATUS_SUCCESS, wpabuf_head(sta->sae_data), wpabuf_len(sta->sae_data)) != ESP_OK) {
ap_free_sta(hapd, sta);
goto done;
}
if (ret == WLAN_STATUS_SUCCESS) {
if (sta->sae_data && esp_send_sae_auth_reply(hapd, sta->addr, frm->bssid, WLAN_AUTH_SAE, 2,
WLAN_STATUS_SUCCESS, wpabuf_head(sta->sae_data), wpabuf_len(sta->sae_data)) != ESP_OK) {
ap_free_sta(hapd, sta);
goto done;
}
if (esp_wifi_ap_notify_node_sae_auth_done(frm->bssid) != true) {
ap_free_sta(hapd, sta);
goto done;
}
if (esp_wifi_ap_notify_node_sae_auth_done(frm->bssid) != true) {
ap_free_sta(hapd, sta);
goto done;
}
os_semphr_give(sta->lock);
if (ret != WLAN_STATUS_SUCCESS) {
uint16_t aid = 0;
esp_wifi_ap_get_sta_aid(frm->bssid, &aid);
if (aid == 0) {
esp_wifi_ap_deauth_internal(frm->bssid, ret);
} else {
if (sta && sta->sae_data) {
wpabuf_free(sta->sae_data);
sta->sae_data = NULL;
}
} else {
uint16_t aid = 0;
esp_wifi_ap_get_sta_aid(frm->bssid, &aid);
if (aid == 0) {
os_semphr_give(sta->lock);
esp_wifi_ap_deauth_internal(frm->bssid, ret);
} else {
if (sta->sae_data) {
wpabuf_free(sta->sae_data);
sta->sae_data = NULL;
}
os_semphr_give(sta->lock);
}
}
done:
@@ -601,34 +662,68 @@ static void esp_wpa3_hostap_task(void *pvParameters)
os_queue_delete(g_wpa3_hostap_evt_queue);
g_wpa3_hostap_evt_queue = NULL;
struct hostapd_data *hapd = hostapd_get_hapd_data();
if (hapd && hapd->sta_list_lock) {
struct hostapd_sae_commit_queue *q, *tmp;
HOSTAPD_STA_LIST_LOCK(hapd);
dl_list_for_each_safe(q, tmp, &hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list) {
dl_list_del(&q->list);
os_free(q);
}
HOSTAPD_STA_LIST_UNLOCK(hapd);
/*
* Safe to delete sta_list_lock after unlock: only the WPA3 hostap task and
* the Wi-Fi task take this lock; the Wi-Fi task is blocked while posting
* SIG_TASK_DEL, so it cannot acquire sta_list_lock again until teardown
* sequencing completes.
*/
os_mutex_delete(hapd->sta_list_lock);
hapd->sta_list_lock = NULL;
}
if (g_wpa3_hostap_auth_api_lock) {
WPA3_HOSTAP_AUTH_API_UNLOCK();
}
/* At this point, task is deleted*/
/* At this point, task is deleted */
os_task_delete(NULL);
}
int wpa3_hostap_auth_init(void *data)
{
struct hostapd_data *hapd = (struct hostapd_data *)data;
if (g_wpa3_hostap_evt_queue) {
wpa_printf(MSG_ERROR, "esp_wpa3_hostap_task has already been initialised");
return ESP_OK;
}
hapd->sta_list_lock = os_mutex_create();
if (!hapd->sta_list_lock) {
wpa_printf(MSG_ERROR, "wpa3_hostap_auth_init: failed to create sta_list_lock");
return ESP_FAIL;
}
/* g_wpa3_hostap_auth_api_lock is global - created once, never deleted */
if (g_wpa3_hostap_auth_api_lock == NULL) {
g_wpa3_hostap_auth_api_lock = os_semphr_create(1, 1);
if (!g_wpa3_hostap_auth_api_lock) {
wpa_printf(MSG_ERROR, "wpa3_hostap_auth_init: failed to create WPA3 hostap auth API lock");
os_mutex_delete(hapd->sta_list_lock);
hapd->sta_list_lock = NULL;
return ESP_FAIL;
}
}
g_wpa3_hostap_evt_queue = os_queue_create(10, sizeof(wpa3_hostap_auth_event_t));
g_wpa3_hostap_evt_queue = os_queue_create(10, sizeof(wpa3_hostap_auth_event_t));
if (!g_wpa3_hostap_evt_queue) {
wpa_printf(MSG_ERROR, "wpa3_hostap_auth_init: failed to create queue");
os_mutex_delete(hapd->sta_list_lock);
hapd->sta_list_lock = NULL;
return ESP_FAIL;
}
dl_list_init(&hapd->sae_commit_queue);
if (os_task_create(esp_wpa3_hostap_task, "esp_wpa3_hostap_task",
WPA3_HOSTAP_HANDLE_AUTH_TASK_STACK_SIZE, NULL,
WPA3_HOSTAP_HANDLE_AUTH_TASK_PRIORITY,
@@ -636,6 +731,8 @@ int wpa3_hostap_auth_init(void *data)
wpa_printf(MSG_ERROR, "wpa3_hostap_auth_init: failed to create task");
os_queue_delete(g_wpa3_hostap_evt_queue);
g_wpa3_hostap_evt_queue = NULL;
os_mutex_delete(hapd->sta_list_lock);
hapd->sta_list_lock = NULL;
return ESP_FAIL;
}
@@ -647,9 +744,8 @@ bool wpa3_hostap_auth_deinit(void)
if (wpa3_hostap_post_evt(SIG_TASK_DEL, 0) != 0) {
wpa_printf(MSG_DEBUG, "failed to send task delete event");
return false;
} else {
return true;
}
return true;
}
static int wpa3_hostap_handle_auth(u8 *buf, size_t len, u32 auth_transaction, u16 status, u8 *bssid)
@@ -658,16 +754,25 @@ static int wpa3_hostap_handle_auth(u8 *buf, size_t len, u32 auth_transaction, u1
if (!hapd) {
return ESP_FAIL;
}
struct sta_info *sta = ap_get_sta(hapd, bssid);
if (auth_transaction == SAE_MSG_COMMIT) {
if (sta && sta->sae_commit_processing) {
/* Ignore commit msg as we are already processing commit msg for this station */
HOSTAPD_STA_LIST_LOCK(hapd);
struct sta_info *sta = ap_get_sta_internal(hapd, bssid);
if (sta && atomic_load(&sta->sae_commit_processing)) {
HOSTAPD_STA_LIST_UNLOCK(hapd);
return ESP_OK;
}
HOSTAPD_STA_LIST_UNLOCK(hapd);
return auth_sae_queue(hapd, buf, len, bssid, status, auth_transaction);
}
if (sta && auth_transaction == SAE_MSG_CONFIRM) {
if (auth_transaction == SAE_MSG_CONFIRM) {
HOSTAPD_STA_LIST_LOCK(hapd);
bool sta_exists = (ap_get_sta_internal(hapd, bssid) != NULL);
HOSTAPD_STA_LIST_UNLOCK(hapd);
if (!sta_exists) {
return ESP_OK;
}
struct sae_hostap_confirm_data *frm = os_malloc(sizeof(struct sae_hostap_confirm_data) + len);
if (!frm) {
wpa_printf(MSG_ERROR, "failed to allocate memory for confirm event");
@@ -107,6 +107,10 @@ struct hostapd_data {
struct sta_info *sta_hash[STA_HASH_SIZE];
int num_sta; /* number of entries in sta_list */
#ifdef ESP_SUPPLICANT
void *sta_list_lock;
#endif /* ESP_SUPPLICANT */
struct eapol_authenticator *eapol_auth;
struct wpa_authenticator *wpa_auth;
@@ -169,4 +173,21 @@ const struct hostapd_eap_user *
hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
size_t identity_len, int phase2);
#ifdef ESP_SUPPLICANT
#define HOSTAPD_STA_LIST_LOCK(hapd) \
do { \
if ((hapd) && (hapd)->sta_list_lock) \
os_mutex_lock((hapd)->sta_list_lock); \
} while (0)
#define HOSTAPD_STA_LIST_UNLOCK(hapd) \
do { \
if ((hapd) && (hapd)->sta_list_lock) \
os_mutex_unlock((hapd)->sta_list_lock); \
} while (0)
#else
#define HOSTAPD_STA_LIST_LOCK(hapd) do { } while (0)
#define HOSTAPD_STA_LIST_UNLOCK(hapd) do { } while (0)
#endif /* ESP_SUPPLICANT */
#endif /* HOSTAPD_H */
+37 -6
View File
@@ -151,7 +151,7 @@ static int auth_sae_send_commit(struct hostapd_data *hapd,
}
#ifdef ESP_SUPPLICANT
if (sta->remove_pending) {
if (atomic_load(&sta->remove_pending)) {
reply_res = -1;
} else {
reply_res = esp_send_sae_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1,
@@ -177,7 +177,7 @@ static int auth_sae_send_confirm(struct hostapd_data *hapd,
}
#ifdef ESP_SUPPLICANT
if (sta->remove_pending) {
if (atomic_load(&sta->remove_pending)) {
reply_res = -1;
wpabuf_free(data);
} else {
@@ -204,6 +204,10 @@ static int use_sae_anti_clogging(struct hostapd_data *hapd)
return 1;
}
#ifdef ESP_SUPPLICANT
HOSTAPD_STA_LIST_LOCK(hapd);
#endif /* ESP_SUPPLICANT */
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (sta->sae &&
(sta->sae->state == SAE_COMMITTED ||
@@ -211,6 +215,9 @@ static int use_sae_anti_clogging(struct hostapd_data *hapd)
open++;
}
if (open >= hapd->conf->sae_anti_clogging_threshold) {
#ifdef ESP_SUPPLICANT
HOSTAPD_STA_LIST_UNLOCK(hapd);
#endif /* ESP_SUPPLICANT */
return 1;
}
}
@@ -220,9 +227,16 @@ static int use_sae_anti_clogging(struct hostapd_data *hapd)
* potentially result in too many open sessions. */
if (open + dl_list_len(&hapd->sae_commit_queue) >=
hapd->conf->sae_anti_clogging_threshold) {
#ifdef ESP_SUPPLICANT
HOSTAPD_STA_LIST_UNLOCK(hapd);
#endif /* ESP_SUPPLICANT */
return 1;
}
#ifdef ESP_SUPPLICANT
HOSTAPD_STA_LIST_UNLOCK(hapd);
#endif /* ESP_SUPPLICANT */
return 0;
}
@@ -243,7 +257,7 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
sta->flags |= WLAN_STA_AUTH;
#ifdef ESP_SUPPLICANT
sta->sae_commit_processing = false;
atomic_store(&sta->sae_commit_processing, false);
#endif /* ESP_SUPPLICANT */
sta->auth_alg = WLAN_AUTH_SAE;
@@ -591,7 +605,7 @@ int handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
#ifdef ESP_SUPPLICANT
sta->sae_commit_processing = false;
atomic_store(&sta->sae_commit_processing, false);
#endif /* ESP_SUPPLICANT */
goto reply;
@@ -660,7 +674,7 @@ reply:
data = wpabuf_alloc_copy(pos, 2);
}
#ifdef ESP_SUPPLICANT
if (!sta->remove_pending) {
if (!atomic_load(&sta->remove_pending)) {
esp_send_sae_auth_reply(hapd, bssid, bssid, WLAN_AUTH_SAE,
auth_transaction, resp,
data ? wpabuf_head(data) : (u8 *) "",
@@ -681,11 +695,23 @@ int auth_sae_queue(struct hostapd_data *hapd,
struct hostapd_sae_commit_queue *q, *q2;
unsigned int queue_len;
#ifdef ESP_SUPPLICANT
if (!hapd->sta_list_lock) {
wpa_printf(MSG_DEBUG,
"SAE: sta_list_lock not set (no WPA3 hostap path), drop from " MACSTR,
MAC2STR(bssid));
return -1;
}
HOSTAPD_STA_LIST_LOCK(hapd);
#endif /* ESP_SUPPLICANT */
queue_len = dl_list_len(&hapd->sae_commit_queue);
if (queue_len >= hapd->conf->max_num_sta) {
wpa_printf(MSG_DEBUG,
"SAE: No more room in message queue - drop the new frame from "
MACSTR, MAC2STR(bssid));
#ifdef ESP_SUPPLICANT
HOSTAPD_STA_LIST_UNLOCK(hapd);
#endif /* ESP_SUPPLICANT */
return 0;
}
@@ -694,6 +720,9 @@ int auth_sae_queue(struct hostapd_data *hapd,
queue_len);
q = os_zalloc(sizeof(*q) + len);
if (!q) {
#ifdef ESP_SUPPLICANT
HOSTAPD_STA_LIST_UNLOCK(hapd);
#endif /* ESP_SUPPLICANT */
return -1;
}
@@ -731,11 +760,13 @@ queued:
if (wpa3_hostap_post_evt(SIG_WPA3_RX_COMMIT, 0) != 0) {
wpa_printf(MSG_ERROR, "failed to queue commit build event");
dl_list_del(&q->list);
HOSTAPD_STA_LIST_UNLOCK(hapd);
os_free(q);
return -1;
}
return 0;
HOSTAPD_STA_LIST_UNLOCK(hapd);
#endif /* ESP_SUPPLICANT */
return 0;
}
+88 -17
View File
@@ -23,29 +23,62 @@
static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx);
void hostapd_wps_eap_completed(struct hostapd_data *hapd);
/*
* CAUTION: cb is invoked while HOSTAPD_STA_LIST_LOCK is held.
* The mutex is non-recursive (os_mutex_create), so cb must NEVER
* call ap_get_sta, ap_free_sta, ap_sta_add, or any other function
* that acquires HOSTAPD_STA_LIST_LOCK — doing so will deadlock.
* Use the lock-free variants (e.g. ap_get_sta_internal) if needed.
*/
int ap_for_each_sta(struct hostapd_data *hapd,
int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
void *ctx),
void *ctx)
{
struct sta_info *sta;
int ret;
HOSTAPD_STA_LIST_LOCK(hapd);
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (cb(hapd, sta, ctx))
return 1;
if (cb(hapd, sta, ctx)) {
ret = 1;
HOSTAPD_STA_LIST_UNLOCK(hapd);
return ret;
}
}
HOSTAPD_STA_LIST_UNLOCK(hapd);
return 0;
}
/**
* ap_get_sta - Look up a station by MAC address
* @hapd: hostapd data structure
* @sta: MAC address to look up
* Returns: Pointer to sta_info structure, or NULL if not found
*
* This function acquires and releases HOSTAPD_STA_LIST_LOCK internally.
* The returned pointer is NOT protected after the lock is released.
* Use-after-free can occur if the station is freed by another task
* (via ap_free_sta) between the unlock here and the caller's use.
*
* For safe access to sta fields, callers should either:
* - Use ap_get_sta_internal() while holding HOSTAPD_STA_LIST_LOCK, OR
* - Take sta->lock immediately after (for SAE stations), OR
* - Copy needed data while lock is still held
*
* Note: This is particularly important for wpa3_task callers.
*/
struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
{
struct sta_info *s;
HOSTAPD_STA_LIST_LOCK(hapd);
s = hapd->sta_hash[STA_HASH(sta)];
while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
s = s->hnext;
HOSTAPD_STA_LIST_UNLOCK(hapd);
return s;
}
@@ -69,6 +102,17 @@ static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta)
tmp->next = sta->next;
}
/* Caller MUST hold HOSTAPD_STA_LIST_LOCK (see sta_info.h). */
struct sta_info * ap_get_sta_internal(struct hostapd_data *hapd, const u8 *sta)
{
struct sta_info *s;
s = hapd->sta_hash[STA_HASH(sta)];
while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
s = s->hnext;
return s;
}
void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta)
{
@@ -100,10 +144,12 @@ static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta)
void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
{
HOSTAPD_STA_LIST_LOCK(hapd);
ap_sta_hash_del(hapd, sta);
ap_sta_list_del(hapd, sta);
hapd->num_sta--;
HOSTAPD_STA_LIST_UNLOCK(hapd);
#ifdef CONFIG_SAE
sae_clear_data(sta->sae);
@@ -113,10 +159,12 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
os_mutex_delete(sta->lock);
sta->lock = NULL;
}
#ifdef ESP_SUPPLICANT
if (sta->sae_data) {
wpabuf_free(sta->sae_data);
sta->sae_data = NULL;
}
#endif /* ESP_SUPPLICANT */
#endif /* CONFIG_SAE */
wpa_auth_sta_deinit(sta->wpa_sm);
#ifdef CONFIG_WPS_REGISTRAR
@@ -132,18 +180,28 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
}
/* Called during teardown after WPA3 task is stopped, so sta->lock is always available. */
void hostapd_free_stas(struct hostapd_data *hapd)
{
struct sta_info *sta, *prev;
struct sta_info *sta;
sta = hapd->sta_list;
while (sta) {
prev = sta;
sta = sta->next;
wpa_printf(MSG_DEBUG, "Removing station " MACSTR,
MAC2STR(prev->addr));
ap_free_sta(hapd, prev);
while (1) {
HOSTAPD_STA_LIST_LOCK(hapd);
sta = hapd->sta_list;
if (!sta) {
HOSTAPD_STA_LIST_UNLOCK(hapd);
break;
}
#ifdef CONFIG_SAE
if (sta->lock) {
os_semphr_take(sta->lock, OS_BLOCK);
HOSTAPD_STA_LIST_UNLOCK(hapd);
ap_free_sta(hapd, sta);
continue;
}
#endif /* CONFIG_SAE */
HOSTAPD_STA_LIST_UNLOCK(hapd);
ap_free_sta(hapd, sta);
}
}
@@ -152,35 +210,48 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta;
sta = ap_get_sta(hapd, addr);
if (sta)
HOSTAPD_STA_LIST_LOCK(hapd);
sta = ap_get_sta_internal(hapd, addr);
if (sta) {
HOSTAPD_STA_LIST_UNLOCK(hapd);
return sta;
}
wpa_printf(MSG_DEBUG, " New STA");
if (hapd->num_sta >= hapd->conf->max_num_sta) {
/* FIX: might try to remove some old STAs first? */
wpa_printf(MSG_DEBUG, "no more room for new STAs (%d/%d)",
hapd->num_sta, hapd->conf->max_num_sta);
HOSTAPD_STA_LIST_UNLOCK(hapd);
return NULL;
}
sta = os_zalloc(sizeof(struct sta_info));
if (sta == NULL) {
wpa_printf(MSG_ERROR, "malloc failed");
HOSTAPD_STA_LIST_UNLOCK(hapd);
return NULL;
}
/* initialize STA info data */
os_memcpy(sta->addr, addr, ETH_ALEN);
sta->next = hapd->sta_list;
#ifdef CONFIG_SAE
sta->sae_commit_processing = false;
sta->remove_pending = false;
atomic_init(&sta->sae_commit_processing, false);
atomic_init(&sta->remove_pending, false);
sta->lock = os_semphr_create(1, 1);
if (!sta->lock) {
wpa_printf(MSG_ERROR, "Failed to create sta->lock for " MACSTR,
MAC2STR(addr));
HOSTAPD_STA_LIST_UNLOCK(hapd);
os_free(sta);
return NULL;
}
#endif /* CONFIG_SAE */
sta->next = hapd->sta_list;
hapd->sta_list = sta;
hapd->num_sta++;
ap_sta_hash_add(hapd, sta);
HOSTAPD_STA_LIST_UNLOCK(hapd);
return sta;
}
+8 -2
View File
@@ -9,6 +9,10 @@
#ifndef STA_INFO_H
#define STA_INFO_H
#ifdef CONFIG_SAE
#include <stdatomic.h>
#endif
/* STA flags */
#define WLAN_STA_AUTH BIT(0)
#define WLAN_STA_ASSOC BIT(1)
@@ -62,9 +66,9 @@ struct sta_info {
#ifdef CONFIG_SAE
void *lock;
struct sae_data *sae;
bool sae_commit_processing; /* halt queuing commit while we are
atomic_bool sae_commit_processing; /* halt queuing commit while we are
* processing commit for that station */
bool remove_pending; /* Flag to indicate to free station when
atomic_bool remove_pending; /* Flag to indicate to free station when
* whose mutex is taken by task */
struct wpabuf *sae_data;
#endif /* CONFIG_SAE */
@@ -96,6 +100,8 @@ int ap_for_each_sta(struct hostapd_data *hapd,
void *ctx),
void *ctx);
struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
/* Caller must hold HOSTAPD_STA_LIST_LOCK(hapd) for the duration of the lookup. */
struct sta_info * ap_get_sta_internal(struct hostapd_data *hapd, const u8 *sta);
void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
void hostapd_free_stas(struct hostapd_data *hapd);
+106 -3
View File
@@ -136,17 +136,26 @@ static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
}
#ifdef CONFIG_SAE
struct sta_info *sta = ap_get_sta(hapd, addr);
/* wpa_auth_get_psk runs on the Wi-Fi task only, so sae_pmk_copy is not shared with any other task. */
static u8 sae_pmk_copy[PMK_LEN];
HOSTAPD_STA_LIST_LOCK(hapd);
struct sta_info *sta = ap_get_sta_internal(hapd, addr);
if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
if (!sta->sae || prev_psk)
if (!sta->sae || prev_psk) {
HOSTAPD_STA_LIST_UNLOCK(hapd);
return NULL;
return sta->sae->pmk;
}
os_memcpy(sae_pmk_copy, sta->sae->pmk, PMK_LEN);
HOSTAPD_STA_LIST_UNLOCK(hapd);
return sae_pmk_copy;
}
if (sta && wpa_auth_uses_sae(sta->wpa_sm)) {
wpa_printf(MSG_DEBUG,
"No PSK for STA trying to use SAE with PMKSA caching");
HOSTAPD_STA_LIST_UNLOCK(hapd);
return NULL;
}
HOSTAPD_STA_LIST_UNLOCK(hapd);
#endif /*CONFIG_SAE*/
return (u8*)hostapd_get_psk(hapd->conf, addr, prev_psk);
@@ -210,10 +219,104 @@ wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
return hostapd_send_eapol(wpa_auth->addr, addr, data, data_len);
}
/*
* Modified to handle Wi-Fi vs WPA3 task concurrency only when CONFIG_SAE is enabled.
* Snapshot SM indices under HOSTAPD_STA_LIST_LOCK, then per entry try
* sta->lock (timeout 0) when present; if it is not acquired, skip
* cb for that station; otherwise run cb.
*/
int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
int (*cb)(struct wpa_state_machine *sm, void *ctx),
void *cb_ctx)
{
struct hostapd_data *hapd = hostapd_get_hapd_data();
struct sta_info *sta;
u8 idx_snap[WPA_SM_MAX_INDEX];
unsigned int n = 0;
unsigned int i;
#ifdef CONFIG_SAE
void *sta_lk = NULL;
u8 sta_mac[ETH_ALEN];
#endif /* CONFIG_SAE */
int cb_ret;
if (hapd == NULL)
return 1;
HOSTAPD_STA_LIST_LOCK(hapd);
for (sta = hapd->sta_list; sta; sta = sta->next) {
struct wpa_state_machine *sm = sta->wpa_sm;
if (!sm || n >= WPA_SM_MAX_INDEX)
continue;
if (sm->index >= WPA_SM_MAX_INDEX)
continue;
if (!(BIT(sm->index) & s_sm_valid_bitmap))
continue;
if (s_sm_table[sm->index] != sm)
continue;
idx_snap[n++] = (u8) sm->index;
}
HOSTAPD_STA_LIST_UNLOCK(hapd);
for (i = 0; i < n; i++) {
struct wpa_state_machine *sm;
#ifdef CONFIG_SAE
sta_lk = NULL;
#endif /* CONFIG_SAE */
HOSTAPD_STA_LIST_LOCK(hapd);
sm = wpa_auth_get_sm(idx_snap[i]);
if (!sm) {
HOSTAPD_STA_LIST_UNLOCK(hapd);
continue;
}
#ifdef CONFIG_SAE
sta = ap_get_sta_internal(hapd, sm->addr);
if (sta && sta->wpa_sm == sm && sta->lock) {
/* Take sta->lock with timeout 0.
* Skip cb for this STA if sta->lock is not taken (WPA3 task may be holding the lock). */
if (!os_semphr_take(sta->lock, 0)) {
HOSTAPD_STA_LIST_UNLOCK(hapd);
continue;
}
sta_lk = sta->lock;
os_memcpy(sta_mac, sta->addr, ETH_ALEN);
}
#endif /* CONFIG_SAE */
HOSTAPD_STA_LIST_UNLOCK(hapd);
cb_ret = cb(sm, cb_ctx);
#ifdef CONFIG_SAE
/*
* Give only when re-lookup finds sta->lock == sta_lk; otherwise skip
* os_semphr_give(sta_lk) (sta_lk is not dereferenced on that path).
* ap_free_sta() give+deletes sta->lock and clears the field before freeing sta.
* ESP softAP: wpa_ap_remove and this walk usually run on the same Wi-Fi task as
* cb(), so STA removal does not run concurrently with cb() in the typical model.
*/
if (sta_lk) {
HOSTAPD_STA_LIST_LOCK(hapd);
sta = ap_get_sta_internal(hapd, sta_mac);
if (sta && sta->lock == sta_lk) {
os_semphr_give(sta_lk);
} else if (!sta) {
wpa_printf(MSG_DEBUG,
"WPA: sta->lock not released (STA " MACSTR
" gone); ap_free_sta released semaphore",
MAC2STR(sta_mac));
}
HOSTAPD_STA_LIST_UNLOCK(hapd);
sta_lk = NULL;
}
#endif /* CONFIG_SAE */
if (cb_ret)
return 1;
}
return 0;
}