Possible race condition between async_context_task() and async_context_freertos_deinit() #2487

Closed
opened 2025-05-23 17:24:07 -05:00 by ptahpeteh · 4 comments
ptahpeteh commented 2025-05-23 17:24:07 -05:00 (Migrated from github.com)

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.
Tested with pico_cyw43_arch_lwip_sys_freertos and commit [ee68c78](https://github.com/raspberrypi/pico-sdk/commit/ee68c78d0afae2b69c03ae1a72bf5cc267a2d94c). 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.
kilograham commented 2025-07-22 17:14:34 -05:00 (Migrated from github.com)

before i make my own setup @peterharperuk any idea if this is a duplicate of something already fixed?

before i make my own setup @peterharperuk any idea if this is a duplicate of something already fixed?
peterharperuk commented 2025-07-23 05:51:10 -05:00 (Migrated from github.com)

No I don't think we've fixed this. I can reproduce after making changes to the freertos example. See github.com/peterharperuk/pico-examples@998120d697

#0  _exit (status=status@entry=1)
    at /home/peterh/source/pico-sdk1/src/rp2_common/pico_clib_interface/newlib_interface.c:45
#1  0x10006eb6 in __assert_func (file=file@entry=0x1000be0c "/home/peterh/source/FreeRTOS-kernel/queue.c",
    line=line@entry=826, func=func@entry=0x1000dd48 <__func__.19> "xQueueTakeMutexRecursive",
    failedexpr=failedexpr@entry=0x1000bf90 "pxMutex")
    at /home/peterh/source/pico-sdk1/src/rp2_common/pico_clib_interface/newlib_interface.c:173
#2  0x10002340 in xQueueTakeMutexRecursive (xMutex=<optimized out>, xTicksToWait=xTicksToWait@entry=4294967295)
    at /home/peterh/source/FreeRTOS-kernel/queue.c:826
#3  0x10000490 in async_context_freertos_acquire_lock_blocking (
    self_base=self_base@entry=0x20000f18 <async_context_instance>)
    at /home/peterh/source/pico-sdk1/src/rp2_common/pico_async_context/async_context_freertos.c:191
#4  0x10000652 in async_context_task (vself=0x20000f18 <async_context_instance>)
    at /home/peterh/source/pico-sdk1/src/rp2_common/pico_async_context/async_context_freertos.c:71
#5  0x10000e04 in ?? ()
No I don't think we've fixed this. I can reproduce after making changes to the freertos example. See https://github.com/peterharperuk/pico-examples/commit/998120d697115d19877e562530227f4cbd61a51d ``` #0 _exit (status=status@entry=1) at /home/peterh/source/pico-sdk1/src/rp2_common/pico_clib_interface/newlib_interface.c:45 #1 0x10006eb6 in __assert_func (file=file@entry=0x1000be0c "/home/peterh/source/FreeRTOS-kernel/queue.c", line=line@entry=826, func=func@entry=0x1000dd48 <__func__.19> "xQueueTakeMutexRecursive", failedexpr=failedexpr@entry=0x1000bf90 "pxMutex") at /home/peterh/source/pico-sdk1/src/rp2_common/pico_clib_interface/newlib_interface.c:173 #2 0x10002340 in xQueueTakeMutexRecursive (xMutex=<optimized out>, xTicksToWait=xTicksToWait@entry=4294967295) at /home/peterh/source/FreeRTOS-kernel/queue.c:826 #3 0x10000490 in async_context_freertos_acquire_lock_blocking ( self_base=self_base@entry=0x20000f18 <async_context_instance>) at /home/peterh/source/pico-sdk1/src/rp2_common/pico_async_context/async_context_freertos.c:191 #4 0x10000652 in async_context_task (vself=0x20000f18 <async_context_instance>) at /home/peterh/source/pico-sdk1/src/rp2_common/pico_async_context/async_context_freertos.c:71 #5 0x10000e04 in ?? () ```
kilograham commented 2025-07-23 11:04:15 -05:00 (Migrated from github.com)

thanks @ptahpeteh for the very detailed bug report

thanks @ptahpeteh for the very detailed bug report
kilograham commented 2025-07-24 17:19:05 -05:00 (Migrated from github.com)

fix merged into devleop

fix merged into devleop
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-raspberrypi/pico-sdk#2487
No description provided.