Skip to content

[v1.0.18] Serial-master OOM paths can delete invalid task handles (EPROT-75) #181

Description

@Adriaeik

Environment

  • esp-modbus: 1.0.18 / legacy master (a294764)
  • ESP-IDF: 5.5
  • Target: ESP32-S3, dual-core non-SMP FreeRTOS

Problem

When internal/DMA-capable RAM is exhausted during serial-master initialization, v1.0.18 does not fail transactionally.

In mbc_serial_master_create(), the interface is allocated with malloc(), the task handle is left indeterminate, and the failure path calls vTaskDelete() after xTaskCreatePinnedToCore() fails. On hardware this turned an ordinary allocation failure into a StoreProhibited write through address 0x8.

The UART startup path has the same class of bug: xMBMasterPortSerialInit() calls vTaskDelete(xMbTaskHandle) after failed task creation. A null task handle means the calling task in FreeRTOS; an uninitialized handle is worse.

The same initialization chain also has partial-allocation leaks:

  • portevent_m.c returns after individual allocation failures without rolling back earlier event groups/queue/semaphore.
  • porttimer_m.c leaks the timer context when esp_timer_create() fails.

The observed sequence was:

sdmmc_read_sectors: not enough mem
...
mbc_serial_master_create -> failed controller task allocation
vTaskDelete(invalid handle)
Guru Meditation Error: StoreProhibited, EXCVADDR=0x00000008

The SD error is useful context: even a one-sector DMA bounce allocation was already failing, so the following 4096-byte controller stack allocation was expected to fail. That failure should be recoverable.

Expected behavior

  • Return ESP_ERR_NO_MEM / FALSE without deleting an invalid or unrelated task.
  • Roll back only resources whose ownership was successfully acquired.
  • Leave close/destroy idempotent after a partially completed start.
  • Permit a later initialization retry.

Downstream validation

We tested a repository-owned 1.0.18 fork which:

  1. zero-initializes task/resource handles;
  2. never calls vTaskDelete() after failed task creation;
  3. transactionally unwinds controller, UART, event, and timer allocations;
  4. makes partial close/destroy idempotent; and
  5. synchronizes timer callbacks with close so a retiring context is not freed while a callback is in flight.

After the patch, repeated init -> batch/read -> deinit cycles passed on ESP32-S3 while a persistent WSS connection, HTTPS status sync, SD event writes, and remote console traffic were active. A live Modbus owner session also completed without panic. Allocation failure is now rejected cleanly instead of corrupting FreeRTOS state.

Current v2 main has redesigned/guarded cleanup and no longer contains these exact paths. Would Espressif accept a focused backport PR against legacy master for the v1.0.18 users still distributed through the component registry? I can split it into:

  1. controller/UART task-creation rollback; and
  2. event/timer partial-initialization rollback.

A deterministic regression test can fault-inject both task-creation calls and each resource allocation, assert a clean failure, then retry initialization successfully.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions