From 0a120fa7bd338e8d705dcdbf77503d405a392fef Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 10 Mar 2026 16:03:04 -1000 Subject: [PATCH] [socket] Fix potential pbuf leak when recv callback gets err != ERR_OK Defensively free pb when err != ERR_OK but pb != nullptr in both recv_fn and s_queued_recv_fn. In practice lwip never sends data with an error code, but this prevents a leak if it ever does. Also add comment noting tcp_recved is deferred to read(). --- esphome/components/socket/lwip_raw_tcp_impl.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/esphome/components/socket/lwip_raw_tcp_impl.cpp b/esphome/components/socket/lwip_raw_tcp_impl.cpp index 2081349a18..fd1b8a9554 100644 --- a/esphome/components/socket/lwip_raw_tcp_impl.cpp +++ b/esphome/components/socket/lwip_raw_tcp_impl.cpp @@ -463,6 +463,9 @@ err_t LWIPRawImpl::recv_fn(struct pbuf *pb, err_t err) { if (err != 0) { // "An error code if there has been an error receiving Only return ERR_ABRT if you have // called tcp_abort from within the callback function!" + if (pb != nullptr) { + pbuf_free(pb); + } this->rx_closed_ = true; return ERR_OK; } @@ -732,9 +735,13 @@ err_t LWIPRawListenImpl::s_queued_recv_fn(void *arg, struct tcp_pcb *pcb, struct auto *entry = reinterpret_cast(arg); if (pb == nullptr || err != ERR_OK) { // Remote closed or error + if (pb != nullptr) { + pbuf_free(pb); + } entry->rx_closed = true; return ERR_OK; } + // Buffer the data — tcp_recved() is deferred to read() after accept() creates the socket. if (entry->rx_buf == nullptr) { entry->rx_buf = pb; } else {