diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 02292931d..0d8813334 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -5461,7 +5461,7 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, 60); eapol_sm_notify_logoff(wpa_s->eapol, FALSE); - radio_remove_unstarted_work(wpa_s, NULL); + radio_remove_works(wpa_s, NULL, 1); } @@ -5513,6 +5513,10 @@ static void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit) struct wpa_external_work *ework = work->ctx; if (deinit) { + if (work->started) + eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, + work, NULL); + os_free(ework); return; } diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index a72f2fae7..47434e4b3 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -3231,6 +3231,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #endif /* CONFIG_P2P */ wpa_supplicant_mark_disassoc(wpa_s); + radio_remove_works(wpa_s, NULL, 0); + wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); break; case EVENT_CHANNEL_LIST_CHANGED: diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c index abcb391a1..b2558470d 100644 --- a/wpa_supplicant/gas_query.c +++ b/wpa_supplicant/gas_query.c @@ -573,6 +573,12 @@ static void gas_query_start_cb(struct wpa_radio_work *work, int deinit) struct gas_query *gas = query->gas; if (deinit) { + if (work->started) { + gas->work = NULL; + gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT); + return; + } + gas_query_free(query, 1); return; } diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 1495ea426..fa824914c 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -123,6 +123,7 @@ static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx); static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, int group_added); static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s); +static void wpas_stop_listen(void *ctx); /* @@ -252,7 +253,12 @@ static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit) int ret; if (deinit) { - wpa_scan_free_params(params); + if (!work->started) { + wpa_scan_free_params(params); + return; + } + + wpa_s->p2p_scan_work = NULL; return; } @@ -355,7 +361,7 @@ static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, break; } - radio_remove_unstarted_work(wpa_s, "p2p-scan"); + radio_remove_works(wpa_s, "p2p-scan", 0); if (radio_add_work(wpa_s, 0, "p2p-scan", 0, wpas_p2p_trigger_scan_cb, params) < 0) goto fail; @@ -1001,6 +1007,12 @@ static void wpas_send_action_cb(struct wpa_radio_work *work, int deinit) struct send_action_work *awork = work->ctx; if (deinit) { + if (work->started) { + eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, + wpa_s, NULL); + wpa_s->p2p_send_action_work = NULL; + offchannel_send_action_done(wpa_s); + } os_free(awork); return; } @@ -1746,6 +1758,10 @@ static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit) struct wpas_p2p_listen_work *lwork = work->ctx; if (deinit) { + if (work->started) { + wpa_s->p2p_listen_work = NULL; + wpas_stop_listen(wpa_s); + } wpas_p2p_listen_work_free(lwork); return; } diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 18d243ebd..6c742d6d2 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -148,7 +148,13 @@ static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit) int ret; if (deinit) { - wpa_scan_free_params(params); + if (!work->started) { + wpa_scan_free_params(params); + return; + } + wpa_supplicant_notify_scanning(wpa_s, 0); + wpas_notify_scan_done(wpa_s, 0); + wpa_s->scan_work = NULL; return; } diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 451f5aebc..e712ac439 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -444,6 +444,9 @@ static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit) struct wpa_supplicant *wpa_s = work->wpa_s; if (deinit) { + if (work->started) + wpa_s->connect_work = NULL; + wpas_connect_work_free(cwork); return; } diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index e21c653a7..e942b6225 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1401,6 +1401,13 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) #endif /* CONFIG_HT_OVERRIDES */ if (deinit) { + if (work->started) { + wpa_s->connect_work = NULL; + + /* cancel possible auth. timeout */ + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, + NULL); + } wpas_connect_work_free(cwork); return; } @@ -3077,25 +3084,40 @@ static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx) } -void radio_remove_unstarted_work(struct wpa_supplicant *wpa_s, const char *type) +/* + * This function removes both started and pending radio works running on + * the provided interface's radio. + * Prior to the removal of the radio work, its callback (cb) is called with + * deinit set to be 1. Each work's callback is responsible for clearing its + * internal data and restoring to a correct state. + * @wpa_s: wpa_supplicant data + * @type: type of works to be removed + * @remove_all: 1 to remove all the works on this radio, 0 to remove only + * this interface's works. + */ +void radio_remove_works(struct wpa_supplicant *wpa_s, + const char *type, int remove_all) { struct wpa_radio_work *work, *tmp; struct wpa_radio *radio = wpa_s->radio; dl_list_for_each_safe(work, tmp, &radio->work, struct wpa_radio_work, list) { - if (type && (work->started || os_strcmp(type, work->type) != 0)) + if (type && os_strcmp(type, work->type) != 0) continue; - if (work->started) { - wpa_dbg(wpa_s, MSG_DEBUG, "Leaving started radio work '%s'@%p in the list", - work->type, work); + + /* skip other ifaces' works */ + if (!remove_all && work->wpa_s != wpa_s) continue; - } - wpa_dbg(wpa_s, MSG_DEBUG, "Remove unstarted radio work '%s'@%p", - work->type, work); + + wpa_dbg(wpa_s, MSG_DEBUG, "Remove radio work '%s'@%p%s", + work->type, work, work->started ? " (started)" : ""); work->cb(work, 1); radio_work_free(work); } + + /* in case we removed the started work */ + radio_work_check_next(wpa_s); } @@ -3115,7 +3137,7 @@ static void radio_remove_interface(struct wpa_supplicant *wpa_s) } wpa_printf(MSG_DEBUG, "Remove radio %s", radio->name); - radio_remove_unstarted_work(wpa_s, NULL); + radio_remove_works(wpa_s, NULL, 0); eloop_cancel_timeout(radio_start_next_work, radio, NULL); wpa_s->radio = NULL; os_free(radio); diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 4ca030689..bcdb4d036 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -305,8 +305,8 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, void (*cb)(struct wpa_radio_work *work, int deinit), void *ctx); void radio_work_done(struct wpa_radio_work *work); -void radio_remove_unstarted_work(struct wpa_supplicant *wpa_s, - const char *type); +void radio_remove_works(struct wpa_supplicant *wpa_s, + const char *type, int remove_all); void radio_work_check_next(struct wpa_supplicant *wpa_s); struct wpa_connect_work {