From 8c96ff8b610593bba892f76392e2ca46bd8ce2ee Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 1 Mar 2026 08:43:27 -1000 Subject: [PATCH] [socket] Use tcp_close for listen PCB cleanup in destructor tcp_abort()/tcp_abandon() asserts pcb->state != LISTEN and accesses fields beyond tcp_pcb_listen. Use tcp_close() instead and null pcb_ so the base destructor skips tcp_abort. --- esphome/components/socket/lwip_raw_tcp_impl.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/esphome/components/socket/lwip_raw_tcp_impl.cpp b/esphome/components/socket/lwip_raw_tcp_impl.cpp index f3ac03bedb..430356592f 100644 --- a/esphome/components/socket/lwip_raw_tcp_impl.cpp +++ b/esphome/components/socket/lwip_raw_tcp_impl.cpp @@ -552,7 +552,14 @@ ssize_t LWIPRawImpl::writev(const struct iovec *iov, int iovcnt) { // ---- LWIPRawListenImpl methods ---- LWIPRawListenImpl::~LWIPRawListenImpl() { - // Base class destructor handles pcb_ cleanup via tcp_abort + // Listen PCBs must use tcp_close(), not tcp_abort(). + // tcp_abandon() asserts pcb->state != LISTEN and would access + // fields that don't exist in the smaller tcp_pcb_listen struct. + // Close here and null pcb_ so the base destructor skips tcp_abort. + if (this->pcb_ != nullptr) { + tcp_close(this->pcb_); + this->pcb_ = nullptr; + } } void LWIPRawListenImpl::init() {