Skip to content

WEBSOCKET Double free in esp_websocket_client during error/teardown race (IDFGH-16555) #898

@filzek

Description

@filzek

Answers checklist.

  • I have read the documentation for esp-protocols components and the issue is not addressed there.
  • I have updated my esp-protocols branch (master or release) to the latest version and checked that the issue is present there.
  • I have searched the issue tracker for a similar issue and not found a similar issue.

What component are you using? If you choose Other, provide details in More Information.

esp_websocket_client

component version

1.5.0

IDF version.

v5.4.1-18-g6f30fb434d

More Information.

Hi @glmfe, the websocket double free/tls exception still happening frequently.

Description
When a WebSocket connection aborts unexpectedly, the client sometimes crashes with a TLSF assertion failure indicating a double free. This appears to happen because both the error path and the send/abort path attempt to close and free the same TLS transport, leading to heap corruption.

Observed Behavior

Connection is aborted (errno=113, mbedtls read error :-0x004C).

WEBSOCKET_EVENT_ERROR is fired.

Application code attempts to send or abort, which calls into esp_websocket_client_abort_connection().

The transport is closed twice (esp_transport_close appears twice in the backtrace).

TLS tries to free already-freed memory → assert failed: tlsf_free ... "block already marked as free".

Device reboots with heap corruption.

Backtrace (excerpt)

assert failed: tlsf_free tlsf.c:629 (!block_is_free(block) && "block already marked as free")
esp_tls_conn_destroy -> base_close -> esp_transport_close
ws_close -> esp_transport_close
esp_websocket_client_abort_connection
esp_websocket_client_send_text
EWC_process_queue_tosend
...

Expected Behavior

The WebSocket client should handle error/teardown idempotently.

A single close/free of the TLS transport should occur, even if application code calls abort/stop concurrently with internal error handling.

No heap corruption or double free should be possible.

Steps to Reproduce

Establish a TLS WebSocket connection.

Force the server to close the connection abruptly (RST).

Allow the client task to attempt a send just after the error event is triggered.

Observe crash with TLSF double free assertion.

Environment

ESP-IDF: special build (14.2_20240403 toolchain)

Component: esp_websocket_client + esp-tls + tcp_transport

Target: ESP32 (WROVER module)

Impact
This results in device resets in production when WebSocket connections drop under load or unstable networks.

Possible Root Cause
esp_websocket_client_abort_connection() can trigger esp_transport_close() on a transport that has already been closed by the error path. Without internal idempotency checks, the TLS handle is freed twice, leading to heap metadata corruption.

Suggested Fixes

Make esp_websocket_client_abort_connection() idempotent with an internal “closing” flag.

Ensure ws_close() nullifies the transport pointer before returning.

Optionally enforce a mutex inside esp_websocket_client to serialize close/send/abort.

Additional Notes
App-level guards help, but the library itself should guarantee that teardown paths are safe to call once or multiple times.

REPORT BUG:
2025-09-30 18:44:10 RECEIVED [4145156]
2025-09-30 18:44:10 E (18:44:11.958) transport_base: poll_write select error 113, errno = Software caused connection abort, fd = 31
2025-09-30 18:44:10 E (18:44:11.968) esp-tls-mbedtls: read error :-0x004C
2025-09-30 18:44:10 E (18:44:11.971) transport_ws: Error transport_poll_write
2025-09-30 18:44:10 E (18:44:11.975) transport_base: esp_tls_conn_read error, errno=Software caused connection abort
2025-09-30 18:44:10 E (18:44:12.001) transport_ws: Error read data
2025-09-30 18:44:10 _write() returned -1, transport_error=ESP_OK, tls_error_code=0, tls_flags=0, errno=0
2025-09-30 18:44:10 E (18:44:12.001) transport_ws: Error read data
2025-09-30 18:44:10 WebSocket Event: WEBSOCKET_EVENT_ERROR (0)
2025-09-30 18:44:10 E (18:44:12.030) websocket_client: esp_transport_read() failed with -76, transport_error=ESP_OK, tls_error_code=76, tls_flags=0, errno=113
2025-09-30 18:44:10 WebSocket error
2025-09-30 18:44:10 WebSocket Event: WEBSOCKET_EVENT_ERROR (0)
2025-09-30 18:44:10 WebSocket error
2025-09-30 18:44:10 E (18:44:12.087) websocket_client: Error receive data
2025-09-30 18:44:10
2025-09-30 18:44:10 assert failed: tlsf_free tlsf.c:629 (!block_is_free(block) && "block already marked as free")
HINT: CORRUPT HEAP: heap metadata corrupted resulting in TLSF malfunction.
Make sure you are not making out of bound writing on the memory you allocate in your application.
Make sure you are not writing on freed memory.
For more information run 'idf.py docs -sp api-reference/system/heap_debug.html'.
2025-09-30 18:44:10
2025-09-30 18:44:10
2025-09-30 18:44:11 Backtrace: 0x40081e24:0x3ffd6a60 0x40096171:0x3ffd6a80 0x4009902d:0x3ffd6aa0 0x400d96f7:0x3ffd6bd0 0x400d8852:0x3ffd6bf0 0x400da157:0x3ffd6c10 0x40099065:0x3ffd6c30 0x4017ced1:0x3ffd6c50 0x4017cc52:0x3ffd6c70 0x4017eba8:0x3ffd6c90 0x401fec9d:0x3ffd6cb0 0x4017f621:0x3ffd6cd0 0x401fec9d:0x3ffd6cf0 0x4012f3e4:0x3ffd6d10 0x401308bf:0x3ffd6d40 0x401308e1:0x3ffd6d80 0x40110eeb:0x3ffd6da0 0x40110f6b:0x3ffd6de0 0x400e650b:0x3ffd6e00 0x4011066f:0x3ffd6e20 0x401109b9:0x3ffd6e70 0x400f5038:0x3ffd6e90 0x401036e4:0x3ffd76f0 0x4009662a:0x3ffd7810
2025-09-30 18:44:11 --- 0x40081e24: panic_abort at C:/Espressif/frameworks/esp-idf-special/components/esp_system/panic.c:454
--- 0x40096171:
2025-09-30 18:44:11
2025-09-30 18:44:11
2025-09-30 18:44:11
2025-09-30 18:44:11 ELF file SHA256: 81bacc41e
esp_system_abort at C:/Espressif/frameworks/esp-idf-special/components/esp_system/port/esp_system_chip.c:87
--- 0x4009902d: __assert_func at C:/Espressif/frameworks/esp-idf-special/components/newlib/assert.c:80
--- 0x400d96f7: block_next at C:/Espressif/frameworks/esp-idf-special/components/heap/tlsf/tlsf_block_functions.h:161
--- (inlined by) block_link_next at C:/Espressif/frameworks/esp-idf-special/components/heap/tlsf/tlsf_block_functions.h:168
--- (inlined by) block_mark_as_free at C:/Espressif/frameworks/esp-idf-special/components/heap/tlsf/tlsf_block_functions.h:176
--- (inlined by) tlsf_free at C:/Espressif/frameworks/esp-idf-special/components/heap/tlsf/tlsf.c:630
--- 0x400d8852: multi_heap_free_impl at C:/Espressif/frameworks/esp-idf-special/components/heap/multi_heap.c:233
--- (inlined by) multi_heap_free_impl at C:/Espressif/frameworks/esp-idf-special/components/heap/multi_heap.c:222
--- 0x400da157: heap_caps_free at C:/Espressif/frameworks/esp-idf-special/components/heap/heap_caps_base.c:75
--- 0x40099065: free at C:/Espressif/frameworks/esp-idf-special/components/newlib/heap.c:39
--- 0x4017ced1: esp_tls_internal_event_tracker_destroy at C:/Espressif/frameworks/esp-idf-special/components/esp-tls/esp_tls_error_capture.c:50
--- 0x4017cc52: esp_tls_conn_destroy at C:/Espressif/frameworks/esp-idf-special/components/esp-tls/esp_tls.c:160
--- 0x4017eba8: base_close at C:/Espressif/frameworks/esp-idf-special/components/tcp_transport/transport_ssl.c:335
--- 0x401fec9d: esp_transport_close at C:/Espressif/frameworks/esp-idf-special/components/tcp_transport/transport.c:172
--- 0x4017f621: ws_close at C:/Espressif/frameworks/esp-idf-special/components/tcp_transport/transport_ws.c:680
--- 0x401fec9d: esp_transport_close at C:/Espressif/frameworks/esp-idf-special/components/tcp_transport/transport.c:172
--- 0x4012f3e4: esp_websocket_client_abort_connection at D:/Dropbox/Dev/wisehome_project/managed_components/espressif__esp_websocket_client/esp_websocket_client.c:240
--- 0x401308bf: esp_websocket_client_send_with_exact_opcode at D:/Dropbox/Dev/wisehome_project/managed_components/espressif__esp_websocket_client/esp_websocket_client.c:662
--- 0x401308e1: esp_websocket_client_send_with_opcode at D:/Dropbox/Dev/wisehome_project/managed_components/espressif__esp_websocket_client/esp_websocket_client.c:1352
--- (inlined by) esp_websocket_client_send_text at D:/Dropbox/Dev/wisehome_project/managed_components/espressif__esp_websocket_client/esp_websocket_client.c:1322
--- 0x40110eeb: EWC_process_queue_tosend at D:/Dropbox/Dev/wisehome_project/main/tasks/websocket/taskWebsocket.c:1121
--- 0x40110f6b: EWC_request_flush at D:/Dropbox/Dev/wisehome_project/main/tasks/websocket/taskWebsocket.c:1244
--- 0x400e650b: WISEON_request_flush at D:/Dropbox/Dev/wisehome_project/main/include/websocket_lib.c:1782
--- 0x4011066f: EWC_enqueue_item_reply at D:/Dropbox/Dev/wisehome_project/main/tasks/websocket/taskWebsocket.c:454
--- (inlined by) EWC_enqueue_item_reply at D:/Dropbox/Dev/wisehome_project/main/tasks/websocket/taskWebsocket.c:369
--- 0x401109b9: WISEON_enqueue_item_reply at D:/Dropbox/Dev/wisehome_project/main/tasks/websocket/taskWebsocket.c:1260
--- 0x400f5038: executar at D:/Dropbox/Dev/wisehome_project/main/include/exec_utils.c:6815
--- 0x401036e4: taskExecCMD at D:/Dropbox/Dev/wisehome_project/main/tasks/taskSentinela.c:4018
--- 0x4009662a: vPortTaskWrapper at C:/Espressif/frameworks/esp-idf-special/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:139
2025-09-30 18:44:11
2025-09-30 18:44:11 Entering gdb stub now.
2025-09-30 18:44:11 $T04#b8GNU gdb (esp-gdb) 14.2_20240403
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html
This is free software: you are free to change and redistribute it.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions