DPP: Fix GAS query removal race condition on DPP_STOP_LISTEN
If a DPP_STOP_LISTEN call happens to be received when there is a pending gas-query radio work that has not yet been started, it was possible for gas_query_stop() to go through gas_query_done() processing with gas->work == NULL and that ended up with the pending GAS query getting freed without removing the pending radio work that hold a reference to the now freed memory. Fix this by removing the pending non-started radio work for the GAS query in this specific corner case. Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
parent
27a8d93b07
commit
6a252ece24
3 changed files with 26 additions and 0 deletions
|
@ -862,6 +862,15 @@ int gas_query_stop(struct gas_query *gas, u8 dialog_token)
|
||||||
|
|
||||||
dl_list_for_each(query, &gas->pending, struct gas_query_pending, list) {
|
dl_list_for_each(query, &gas->pending, struct gas_query_pending, list) {
|
||||||
if (query->dialog_token == dialog_token) {
|
if (query->dialog_token == dialog_token) {
|
||||||
|
if (!gas->work) {
|
||||||
|
/* The pending radio work has not yet been
|
||||||
|
* started, but the pending entry has a
|
||||||
|
* reference to the soon to be freed query.
|
||||||
|
* Need to remove that radio work now to avoid
|
||||||
|
* leaving behind a reference to freed memory.
|
||||||
|
*/
|
||||||
|
radio_remove_pending_work(gas->wpa_s, query);
|
||||||
|
}
|
||||||
gas_query_done(gas, query, GAS_QUERY_STOPPED);
|
gas_query_done(gas, query, GAS_QUERY_STOPPED);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4955,6 +4955,22 @@ void radio_remove_works(struct wpa_supplicant *wpa_s,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void radio_remove_pending_work(struct wpa_supplicant *wpa_s, void *ctx)
|
||||||
|
{
|
||||||
|
struct wpa_radio_work *work;
|
||||||
|
struct wpa_radio *radio = wpa_s->radio;
|
||||||
|
|
||||||
|
dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
|
||||||
|
if (work->ctx != ctx)
|
||||||
|
continue;
|
||||||
|
wpa_dbg(wpa_s, MSG_DEBUG, "Free pending radio work '%s'@%p%s",
|
||||||
|
work->type, work, work->started ? " (started)" : "");
|
||||||
|
radio_work_free(work);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void radio_remove_interface(struct wpa_supplicant *wpa_s)
|
static void radio_remove_interface(struct wpa_supplicant *wpa_s)
|
||||||
{
|
{
|
||||||
struct wpa_radio *radio = wpa_s->radio;
|
struct wpa_radio *radio = wpa_s->radio;
|
||||||
|
|
|
@ -345,6 +345,7 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
|
||||||
void radio_work_done(struct wpa_radio_work *work);
|
void radio_work_done(struct wpa_radio_work *work);
|
||||||
void radio_remove_works(struct wpa_supplicant *wpa_s,
|
void radio_remove_works(struct wpa_supplicant *wpa_s,
|
||||||
const char *type, int remove_all);
|
const char *type, int remove_all);
|
||||||
|
void radio_remove_pending_work(struct wpa_supplicant *wpa_s, void *ctx);
|
||||||
void radio_work_check_next(struct wpa_supplicant *wpa_s);
|
void radio_work_check_next(struct wpa_supplicant *wpa_s);
|
||||||
struct wpa_radio_work *
|
struct wpa_radio_work *
|
||||||
radio_work_pending(struct wpa_supplicant *wpa_s, const char *type);
|
radio_work_pending(struct wpa_supplicant *wpa_s, const char *type);
|
||||||
|
|
Loading…
Reference in a new issue