From 8e294c3a2c8f1d13a0979f9b6e2081d67d4d5e04 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 10 Dec 2023 11:23:28 +0200 Subject: [PATCH] P2P: Recover from successfully requested, but not started, listen It is possible for the start_listen() callback to be called to request the driver to start a driver operation and stop_listen() called immediately after that (e.g., due to a request to transmit a P2P Public Action frame) before the driver has had time to start ROC and send an event to notify of that. Such a sequence could result in p2p->pending_listen_freq being left to a nonzero value without getting a call to p2p_listen_cb() to clear it. This would stop an ongoing P2P listen operation since no following p2p_listen() call would start the listen due to the pending command being assumed to be in effect. Fix this by detecting this particular sequence and clearing p2p->pending_listen_freq. This was found with the p2p_listen_and_offchannel_tx test case with the new kernel scheduled and UML time-travel. Signed-off-by: Jouni Malinen --- src/p2p/p2p.c | 22 ++++++++++++++++++++++ src/p2p/p2p_i.h | 1 + 2 files changed, 23 insertions(+) diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index f6d92a390..7e11abff4 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -300,6 +300,8 @@ static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) ies) < 0) { p2p_dbg(p2p, "Failed to start listen mode"); p2p->pending_listen_freq = 0; + } else { + p2p->pending_listen_wait_drv = true; } wpabuf_free(ies); } @@ -349,6 +351,7 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout) wpabuf_free(ies); return -1; } + p2p->pending_listen_wait_drv = true; wpabuf_free(ies); p2p_set_state(p2p, P2P_LISTEN_ONLY); @@ -1027,6 +1030,7 @@ static void p2p_search(struct p2p_data *p2p) return; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->pending_listen_wait_drv = false; if (p2p->find_pending_full && (p2p->find_type == P2P_FIND_PROGRESSIVE || @@ -1244,6 +1248,7 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, p2p->pending_listen_freq = 0; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->pending_listen_wait_drv = false; p2p->find_pending_full = 0; p2p->find_type = type; if (freq != 2412 && freq != 2437 && freq != 2462 && freq != 60480) @@ -1338,6 +1343,10 @@ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq) void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq) { + p2p_dbg(p2p, + "%s(freq=%d) pending_listen_freq=%d in_listen=%d drv_in_listen=%d", + __func__, freq, p2p->pending_listen_freq, p2p->in_listen, + p2p->drv_in_listen); if (freq > 0 && ((p2p->drv_in_listen == freq && p2p->in_listen) || p2p->pending_listen_freq == (unsigned int) freq)) { @@ -1357,7 +1366,15 @@ void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq) p2p_dbg(p2p, "Clear drv_in_listen (%d)", p2p->drv_in_listen); p2p->drv_in_listen = 0; } + if (p2p->pending_listen_freq && + p2p->pending_listen_freq != (unsigned int) freq && + !p2p->drv_in_listen && p2p->pending_listen_wait_drv) { + p2p_dbg(p2p, + "Clear pending_listen_freq since the started listen did not complete before being stopped"); + p2p->pending_listen_freq = 0; + } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->pending_listen_wait_drv = false; } @@ -2021,6 +2038,7 @@ static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx) p2p->pending_listen_freq = 0; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->pending_listen_wait_drv = false; p2p->go_neg_peer->status = P2P_SC_SUCCESS; /* * Set new timeout to make sure a previously set one does not expire @@ -2041,6 +2059,7 @@ static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx) p2p->pending_listen_freq = 0; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->pending_listen_wait_drv = false; p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr, p2p->invite_dev_pw_id); } @@ -3865,6 +3884,7 @@ void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, p2p_dbg(p2p, "Starting Listen timeout(%u,%u) on freq=%u based on callback", p2p->pending_listen_sec, p2p->pending_listen_usec, p2p->pending_listen_freq); + p2p->pending_listen_wait_drv = false; p2p->in_listen = 1; p2p->drv_in_listen = freq; if (p2p->pending_listen_sec || p2p->pending_listen_usec) { @@ -4020,6 +4040,7 @@ static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p) p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation"); p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->pending_listen_wait_drv = false; if (p2p->pending_listen_freq) { p2p_dbg(p2p, "Clear pending_listen_freq for %s", __func__); p2p->pending_listen_freq = 0; @@ -4147,6 +4168,7 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) if (p2p->drv_in_listen) { p2p_dbg(p2p, "Driver is still in listen state - stop it"); p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->pending_listen_wait_drv = false; } switch (p2p->state) { diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index d6405c049..2612be5ad 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -389,6 +389,7 @@ struct p2p_data { unsigned int pending_listen_freq; unsigned int pending_listen_sec; unsigned int pending_listen_usec; + bool pending_listen_wait_drv; u8 dev_capab;