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 <j@w1.fi>
This commit is contained in:
Jouni Malinen 2023-12-10 11:23:28 +02:00
parent 97f7f9ce9b
commit 8e294c3a2c
2 changed files with 23 additions and 0 deletions

View 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) {

View 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;