pico-sdk icon indicating copy to clipboard operation
pico-sdk copied to clipboard

Possible race condition between async_context_task() and async_context_freertos_deinit()

Open ptahpeteh opened this issue 6 months ago • 0 comments

Tested with pico_cyw43_arch_lwip_sys_freertos and commit ee68c78.

If the thread executing async_context_freertos_deinit() has higher priority than async_context_task() then the async_context_task() does not end immediately and async_context_freertos_deinit() clears the async_context_freertos_t struct before async_context_task() exit. This results in async_context_task() using already freed freertos objects.

The course of events:

  1. When async_context_freertos_deinit() is called, it adds a synch work that sets task_should_exit = true requesting ending of the async_context_task(). Then async_context_freertos_deinit() pends on semaphore which will signal completing of the work.
  2. Context is switched to async_context_task() which will execute queued work and set task_should_exit = true. Then the semaphore is signaled as the work is completed.
  3. Since higher priority task, that is async_context_freertos_deinit(), is now unblocked, the scheduler wakes it up.
  4. async_context_freertos_deinit() then destroys all the allocated freertos object and clears the async_context_freertos_t struct (memset() at the end of the function). This effectively sets task_should_exit to false again.
  5. At later time when scheduler wakes up the async_context_task(), it finds task_should_exit false and continues to run trying to take semaphore (by calling async_context_freertos_acquire_lock_blocking()) which was already freed by the async_context_freertos_deinit() and is NULL now. This will cause the following assert() error: assertion "pxMutex" failed: file "FreeRTOS-Kernel/queue.c", line 826.

Steps to reproduce:

  1. Call the cyw43_arch_deinit() from a task that has priority equal or higher to tskIDLE_PRIORITY + 4.

Possible solutions:

  1. Setting default value of CYW43_TASK_PRIORITY to (configMAX_PRIORITIES - 1) instead of (tskIDLE_PRIORITY + 4).
  2. Implementing synchronization between async_context_freertos_deinit() and async_context_task(). For example, async_context_task() could signal an event after going out of do-while loop.

ptahpeteh avatar May 23 '25 22:05 ptahpeteh