]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
P2P: Recover from successfully requested, but not started, listen
authorJouni Malinen <j@w1.fi>
Sun, 10 Dec 2023 09:23:28 +0000 (11:23 +0200)
committerJouni Malinen <j@w1.fi>
Sun, 10 Dec 2023 17:30:44 +0000 (19:30 +0200)
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 <j@w1.fi>
src/p2p/p2p.c
src/p2p/p2p_i.h

index f6d92a390a852999c8b3ad6952987eb1d2da2ba3..7e11abff4e037baa36e8200688a4356a3d611205 100644 (file)
@@ -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) {
index d6405c049001205402e38037ae4d755aacd5c8a7..2612be5ad3f2cbf192535374ad50caf3d82e268a 100644 (file)
@@ -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;