Skip to content

Commit cc3f80a

Browse files
committed
Check event queue on null close
When calling _tcp_close_api, guarantee that no events for that client remain in the queue, even if the pcb was shut down. This fixes a race where an error callback is processed by the LwIP thread, invalidating the pcb*, after the async thread has dequeued an event that will lead to a close (such as a fin or final ack) but before close() is called.
1 parent 15fc8ae commit cc3f80a

1 file changed

Lines changed: 11 additions & 2 deletions

File tree

src/AsyncTCP.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -615,18 +615,27 @@ static esp_err_t _tcp_recved(tcp_pcb **pcb, size_t len) {
615615
}
616616

617617
static err_t _tcp_close_api(struct tcpip_api_call_data *api_call_msg) {
618+
// Unlike the other calls, this is not a direct wrapper of the LwIP function;
619+
// we perform the AsyncClient teardown interlocked safely with the LwIP task.
620+
621+
// As a postcondition, the queue must not have any events referencing
622+
// this AsyncClient. This is because it is possible for an error event
623+
// to have been queued, clearing the pcb*, but after the async thread
624+
// has committed to closing/destructing the AsyncClient object.
625+
618626
tcp_api_call_t *msg = (tcp_api_call_t *)api_call_msg;
619627
msg->err = ERR_CONN;
620628
if (*msg->pcb) {
621-
// Unlike the other calls, this is not a direct wrapper of the LwIP function;
622-
// we perform the AsyncClient teardown interlocked safely with the LwIP task.
623629
tcp_pcb *pcb = *msg->pcb;
624630
_reset_tcp_callbacks(pcb, msg->close);
625631
msg->err = tcp_close(pcb);
626632
if (msg->err != ERR_OK) {
627633
tcp_abort(pcb);
628634
}
629635
*msg->pcb = nullptr; // PCB is now the property of LwIP
636+
} else {
637+
// Ensure there is not an error event queued for this client
638+
_remove_events_for_client(msg->close);
630639
}
631640
return msg->err;
632641
}

0 commit comments

Comments
 (0)