diff --git a/esphome/components/socket/bsd_sockets_impl.h b/esphome/components/socket/bsd_sockets_impl.h index 64b6adf94a8..57c1a430a2b 100644 --- a/esphome/components/socket/bsd_sockets_impl.h +++ b/esphome/components/socket/bsd_sockets_impl.h @@ -124,9 +124,13 @@ class BSDSocketImpl { // destructor / double-close path just check fd_ < 0. int fd_{-1}; #ifdef USE_LWIP_FAST_SELECT - // Non-null iff this socket is being monitored for read events. Replaces loop_monitored_ - // on the fast-select path: the pointer itself carries the "monitored" bit. - struct lwip_sock *cached_sock_{nullptr}; // Cached for direct rcvevent read in ready() + // Cached lwip_sock pointer used for direct rcvevent reads in ready() on the + // fast-select path. Replaces loop_monitored_: null means this socket is not being + // monitored for read events — either monitoring was not requested, the fd was + // invalid, or esphome_lwip_get_sock() failed. Non-null means the netconn event + // callback was hooked and notifications are flowing. close() nulls this to prevent + // use-after-free via a recycled lwip slot. + struct lwip_sock *cached_sock_{nullptr}; #else bool loop_monitored_{false}; #endif diff --git a/esphome/components/socket/lwip_sockets_impl.h b/esphome/components/socket/lwip_sockets_impl.h index 6e275eb6d17..7f3b706cd8d 100644 --- a/esphome/components/socket/lwip_sockets_impl.h +++ b/esphome/components/socket/lwip_sockets_impl.h @@ -90,9 +90,13 @@ class LwIPSocketImpl { // destructor / double-close path just check fd_ < 0. int fd_{-1}; #ifdef USE_LWIP_FAST_SELECT - // Non-null iff this socket is being monitored for read events. Replaces loop_monitored_ - // on the fast-select path: the pointer itself carries the "monitored" bit. - struct lwip_sock *cached_sock_{nullptr}; // Cached for direct rcvevent read in ready() + // Cached lwip_sock pointer used for direct rcvevent reads in ready() on the + // fast-select path. Replaces loop_monitored_: null means this socket is not being + // monitored for read events — either monitoring was not requested, the fd was + // invalid, or esphome_lwip_get_sock() failed. Non-null means the netconn event + // callback was hooked and notifications are flowing. close() nulls this to prevent + // use-after-free via a recycled lwip slot. + struct lwip_sock *cached_sock_{nullptr}; #else bool loop_monitored_{false}; #endif diff --git a/esphome/core/application.h b/esphome/core/application.h index cfa26a0345c..3cee2742017 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -290,11 +290,13 @@ class Application { Scheduler scheduler; - /// Register/unregister a socket to be monitored for read events. - /// WARNING: These functions are NOT thread-safe. They must only be called from the main loop. #ifdef USE_HOST - /// Fallback select() path: monitors file descriptors. + /// Register/unregister a socket file descriptor with the host select() fallback loop. + /// USE_LWIP_FAST_SELECT builds do not use this API — sockets hook the lwIP netconn + /// event_callback directly (see socket.h fast_select_hook_fd) and rely on FreeRTOS + /// task notifications for wake-up. /// NOTE: File descriptors >= FD_SETSIZE (typically 10 on ESP) will be rejected with an error. + /// WARNING: These functions are NOT thread-safe. They must only be called from the main loop. /// @return true if registration was successful, false if fd exceeds limits bool register_socket_fd(int fd); void unregister_socket_fd(int fd);