From a6b6b06b2a31fc9bc81cf5cadb539029c8607260 Mon Sep 17 00:00:00 2001 From: Dhimas Ardinata Date: Tue, 26 May 2026 12:48:55 +0700 Subject: [PATCH 1/2] esp_netif: retain pbuf RX free callback Store the driver RX-buffer free callback and driver handle inside each custom pbuf at allocation time. This keeps delayed pbuf_free() independent from later esp_netif driver config teardown. Add an esp_netif regression test that clears the driver config before freeing a custom pbuf. --- .../esp_netif/lwip/netif/esp_pbuf_ref.c | 13 +++--- .../main/esp_netif_test_lwip.c | 40 +++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/components/esp_netif/lwip/netif/esp_pbuf_ref.c b/components/esp_netif/lwip/netif/esp_pbuf_ref.c index 378f8e2e8921..9cb16c1fdd4e 100644 --- a/components/esp_netif/lwip/netif/esp_pbuf_ref.c +++ b/components/esp_netif/lwip/netif/esp_pbuf_ref.c @@ -6,12 +6,12 @@ /** * @file esp_pbuf reference * This file handles lwip custom pbufs interfacing with esp_netif - * and the L2 free function esp_netif_free_rx_buffer() + * and L2 driver RX buffer free callbacks */ #include "lwip/mem.h" #include "lwip/esp_pbuf_ref.h" -#include "esp_netif_net_stack.h" +#include "esp_netif_lwip_internal.h" /** * @brief Specific pbuf structure for pbufs allocated by ESP netif @@ -20,7 +20,8 @@ typedef struct esp_custom_pbuf { struct pbuf_custom p; - esp_netif_t *esp_netif; + void* driver_handle; + void (*driver_free_rx_buffer)(void *h, void* buffer); void* l2_buf; } esp_custom_pbuf_t; @@ -33,7 +34,7 @@ typedef struct esp_custom_pbuf static void esp_pbuf_free(struct pbuf *pbuf) { esp_custom_pbuf_t* esp_pbuf = (esp_custom_pbuf_t*)pbuf; - esp_netif_free_rx_buffer(esp_pbuf->esp_netif, esp_pbuf->l2_buf); + esp_pbuf->driver_free_rx_buffer(esp_pbuf->driver_handle, esp_pbuf->l2_buf); mem_free(pbuf); } @@ -54,7 +55,9 @@ struct pbuf* esp_pbuf_allocate(esp_netif_t *esp_netif, void *buffer, size_t len, return NULL; } esp_pbuf->p.custom_free_function = esp_pbuf_free; - esp_pbuf->esp_netif = esp_netif; + /* The pbuf can outlive esp_netif driver config teardown. */ + esp_pbuf->driver_handle = esp_netif->driver_handle; + esp_pbuf->driver_free_rx_buffer = esp_netif->driver_free_rx_buffer; esp_pbuf->l2_buf = l2_buff; p = pbuf_alloced_custom(PBUF_RAW, len, PBUF_REF, &esp_pbuf->p, buffer, len); if (p == NULL) { diff --git a/components/esp_netif/test_apps/test_app_esp_netif/main/esp_netif_test_lwip.c b/components/esp_netif/test_apps/test_app_esp_netif/main/esp_netif_test_lwip.c index f76cf9a5fc59..245bd590c9a2 100644 --- a/components/esp_netif/test_apps/test_app_esp_netif/main/esp_netif_test_lwip.c +++ b/components/esp_netif/test_apps/test_app_esp_netif/main/esp_netif_test_lwip.c @@ -18,6 +18,7 @@ #include "test_utils.h" #include "memory_checks.h" #include "lwip/netif.h" +#include "lwip/esp_pbuf_ref.h" #include "esp_netif_test.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -740,6 +741,44 @@ TEST(esp_netif, initial_mtu_config_applied) esp_netif_destroy(n2); } +static int s_l2_free_calls; +static void test_l2_free(void *h, void *buffer) +{ + TEST_ASSERT_EQUAL_PTR((void *)0x1234, h); + TEST_ASSERT_EQUAL_PTR(&s_l2_free_calls, buffer); + ++s_l2_free_calls; +} + +TEST(esp_netif, pbuf_uses_allocated_rx_free_callback) +{ + test_case_uses_tcpip(); + + esp_netif_driver_ifconfig_t driver_config = { + .handle = (void *)0x1234, + .driver_free_rx_buffer = test_l2_free, + }; + esp_netif_config_t cfg = { + .base = ESP_NETIF_BASE_DEFAULT_WIFI_STA, + .driver = &driver_config, + .stack = ESP_NETIF_NETSTACK_DEFAULT_WIFI_STA, + }; + esp_netif_t *esp_netif = esp_netif_new(&cfg); + TEST_ASSERT_NOT_NULL(esp_netif); + + uint8_t payload = 0; + s_l2_free_calls = 0; + struct pbuf *pbuf = esp_pbuf_allocate(esp_netif, &payload, sizeof(payload), &s_l2_free_calls); + TEST_ASSERT_NOT_NULL(pbuf); + + esp_netif_driver_ifconfig_t empty_driver_config = { }; + TEST_ESP_OK(esp_netif_set_driver_config(esp_netif, &empty_driver_config)); + + TEST_ASSERT_EQUAL(1, pbuf_free(pbuf)); + TEST_ASSERT_EQUAL(1, s_l2_free_calls); + + esp_netif_destroy(esp_netif); +} + TEST_GROUP_RUNNER(esp_netif) { /** @@ -770,6 +809,7 @@ TEST_GROUP_RUNNER(esp_netif) RUN_TEST_CASE(esp_netif, dhcp_server_state_transitions_mesh) #endif RUN_TEST_CASE(esp_netif, initial_mtu_config_applied) + RUN_TEST_CASE(esp_netif, pbuf_uses_allocated_rx_free_callback) RUN_TEST_CASE(esp_netif, route_priority) RUN_TEST_CASE(esp_netif, set_get_dnsserver) RUN_TEST_CASE(esp_netif, unified_netif_status_event) From eb14700d49a56342857fbbd81a6a7f9a6d9e40ba Mon Sep 17 00:00:00 2001 From: Dhimas Ardinata <112082563+dhimasardinata@users.noreply.github.com> Date: Wed, 27 May 2026 13:49:01 +0700 Subject: [PATCH 2/2] esp_netif: guard stored RX free callback --- components/esp_netif/lwip/netif/esp_pbuf_ref.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/esp_netif/lwip/netif/esp_pbuf_ref.c b/components/esp_netif/lwip/netif/esp_pbuf_ref.c index 9cb16c1fdd4e..540747f433e5 100644 --- a/components/esp_netif/lwip/netif/esp_pbuf_ref.c +++ b/components/esp_netif/lwip/netif/esp_pbuf_ref.c @@ -34,7 +34,9 @@ typedef struct esp_custom_pbuf static void esp_pbuf_free(struct pbuf *pbuf) { esp_custom_pbuf_t* esp_pbuf = (esp_custom_pbuf_t*)pbuf; - esp_pbuf->driver_free_rx_buffer(esp_pbuf->driver_handle, esp_pbuf->l2_buf); + if (esp_pbuf->driver_free_rx_buffer != NULL) { + esp_pbuf->driver_free_rx_buffer(esp_pbuf->driver_handle, esp_pbuf->l2_buf); + } mem_free(pbuf); }