pico-sdk
pico-sdk copied to clipboard
Possible race condition between async_context_task() and async_context_freertos_deinit()
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:
- When async_context_freertos_deinit() is called, it adds a synch work that sets
task_should_exit = truerequesting ending of the async_context_task(). Then async_context_freertos_deinit() pends on semaphore which will signal completing of the work. - 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. - Since higher priority task, that is async_context_freertos_deinit(), is now unblocked, the scheduler wakes it up.
- async_context_freertos_deinit() then destroys all the allocated freertos object and clears the
async_context_freertos_tstruct (memset() at the end of the function). This effectively setstask_should_exittofalseagain. - At later time when scheduler wakes up the async_context_task(), it finds
task_should_exitfalseand 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 isNULLnow. This will cause the following assert() error:assertion "pxMutex" failed: file "FreeRTOS-Kernel/queue.c", line 826.
Steps to reproduce:
- Call the cyw43_arch_deinit() from a task that has priority equal or higher to
tskIDLE_PRIORITY + 4.
Possible solutions:
- Setting default value of
CYW43_TASK_PRIORITYto(configMAX_PRIORITIES - 1)instead of(tskIDLE_PRIORITY + 4). - 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.