From 6a252ece24a0b9b850b2312b8267dc0d59cc9310 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 7 Feb 2018 18:03:58 +0200 Subject: [PATCH] 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 --- wpa_supplicant/gas_query.c | 9 +++++++++ wpa_supplicant/wpa_supplicant.c | 16 ++++++++++++++++ wpa_supplicant/wpa_supplicant_i.h | 1 + 3 files changed, 26 insertions(+) diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c index e4c3b1b96..3567fa27d 100644 --- a/wpa_supplicant/gas_query.c +++ b/wpa_supplicant/gas_query.c @@ -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) { 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); return 0; } diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index c35121ec3..fcb267764 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -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) { struct wpa_radio *radio = wpa_s->radio; diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index b154d3e48..6f05d4592 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -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_remove_works(struct wpa_supplicant *wpa_s, 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); struct wpa_radio_work * radio_work_pending(struct wpa_supplicant *wpa_s, const char *type);