From 3e4cb845858b7a6fce56c493421a6a65ffe0e76a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 14 Mar 2026 15:41:09 -1000 Subject: [PATCH] adjust --- esphome/components/rp2040/helpers.cpp | 22 +++++++++++++------ .../components/socket/lwip_raw_tcp_impl.cpp | 3 ++- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/esphome/components/rp2040/helpers.cpp b/esphome/components/rp2040/helpers.cpp index a9888eb7ae..5b033f4c9c 100644 --- a/esphome/components/rp2040/helpers.cpp +++ b/esphome/components/rp2040/helpers.cpp @@ -8,6 +8,8 @@ #if defined(USE_WIFI) #include #include // For cyw43_arch_lwip_begin/end (LwIPLock) +#elif defined(USE_ETHERNET) +#include // For ethernet_arch_lwip_begin/end (LwIPLock) #endif #include #include @@ -40,18 +42,24 @@ bool random_bytes(uint8_t *data, size_t len) { IRAM_ATTR InterruptLock::InterruptLock() { state_ = save_and_disable_interrupts(); } IRAM_ATTR InterruptLock::~InterruptLock() { restore_interrupts(state_); } -// On RP2040 (Pico W), arduino-pico sets PICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1. -// This means lwip callbacks run from a low-priority user IRQ context, not the +// On RP2040, lwip callbacks run from a low-priority user IRQ context, not the // main loop (see low_priority_irq_handler() in pico-sdk -// async_context_threadsafe_background.c). cyw43_arch_lwip_begin/end acquires the -// async_context recursive mutex to prevent IRQ callbacks from firing during -// critical sections. See esphome#10681. +// async_context_threadsafe_background.c). This applies to both WiFi (CYW43) and +// Ethernet (W5500) — both use async_context_threadsafe_background. // -// When CYW43 is not available (non-WiFi RP2040 boards), LwIPLock is -// defined inline as a no-op in helpers.h. +// Without locking, recv_fn() from IRQ context races with read_locked_() on the +// main loop, corrupting the shared rx_buf_ pbuf chain (use-after-free, pbuf_cat +// assertion failures). See esphome#10681. +// +// WiFi uses cyw43_arch_lwip_begin/end; Ethernet uses ethernet_arch_lwip_begin/end. +// Both acquire the async_context recursive mutex to prevent IRQ callbacks from +// firing during critical sections. #if defined(USE_WIFI) LwIPLock::LwIPLock() { cyw43_arch_lwip_begin(); } LwIPLock::~LwIPLock() { cyw43_arch_lwip_end(); } +#elif defined(USE_ETHERNET) +LwIPLock::LwIPLock() { ethernet_arch_lwip_begin(); } +LwIPLock::~LwIPLock() { ethernet_arch_lwip_end(); } #else LwIPLock::LwIPLock() {} LwIPLock::~LwIPLock() {} diff --git a/esphome/components/socket/lwip_raw_tcp_impl.cpp b/esphome/components/socket/lwip_raw_tcp_impl.cpp index 96328e68c7..3bcbd88085 100644 --- a/esphome/components/socket/lwip_raw_tcp_impl.cpp +++ b/esphome/components/socket/lwip_raw_tcp_impl.cpp @@ -130,7 +130,8 @@ void socket_wake() { // code (CONT context) — they never preempt each other, so no locking is needed. // // esphome::LwIPLock is the platform-provided RAII guard (see helpers.h/helpers.cpp). -// On RP2040, it acquires cyw43_arch_lwip_begin/end. On ESP8266, it's a no-op. +// On RP2040, it acquires cyw43_arch_lwip_begin/end (WiFi) or ethernet_arch_lwip_begin/end +// (Ethernet). On ESP8266, it's a no-op. #define LWIP_LOCK() esphome::LwIPLock lwip_lock_guard // NOLINT static const char *const TAG = "socket.lwip";