]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
DPP: Fix GAS query removal race condition on DPP_STOP_LISTEN
authorJouni Malinen <jouni@codeaurora.org>
Wed, 7 Feb 2018 16:03:58 +0000 (18:03 +0200)
committerJouni Malinen <j@w1.fi>
Wed, 7 Feb 2018 16:03:58 +0000 (18:03 +0200)
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>
wpa_supplicant/gas_query.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index e4c3b1b96444f6717c07ae10622eecb99685e402..3567fa27d1f25feaa0a7440179fe0d6bb4ffc020 100644 (file)
@@ -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;
                }
index c35121ec3cd0d65f4355ab1893560fafe7473869..fcb267764bdec1b8d12d49b3894badcda5937101 100644 (file)
@@ -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;
index b154d3e4855c7f0fcfb823ac626293f2b94454b8..6f05d4592f4920f749221af190acf7310a4289a6 100644 (file)
@@ -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);