From 6ddbfbdb18c7c26d61ba04fe2074d307050bb5a3 Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Wed, 1 Apr 2026 16:01:34 +0200 Subject: [PATCH 1/2] fix(freertos): hide mutex-owner check config --- components/freertos/Kconfig | 9 +++------ .../freertos/config/include/freertos/FreeRTOSConfig.h | 3 --- .../test_apps/freertos/sdkconfig.ci.code_coverage | 3 --- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index 92498e83df..7df72816bf 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -417,14 +417,11 @@ menu "FreeRTOS" hook function in their application. config FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER - # This feature is innately supported in FreeRTOS SMP, and hence not available as a config option when - # FreeRTOS SMP is enabled. + # TODO(IDF-15523): Remove this hidden compatibility option entirely. + # This feature is innately supported in FreeRTOS SMP, and IDF FreeRTOS always enables it. depends on !FREERTOS_SMP - bool "Check that mutex semaphore is given by owner task" + bool default y - help - If enabled, assert that when a mutex semaphore is given, the task giving the semaphore is the task - which is currently holding the mutex. config FREERTOS_ISR_STACKSIZE int "ISR stack size" diff --git a/components/freertos/config/include/freertos/FreeRTOSConfig.h b/components/freertos/config/include/freertos/FreeRTOSConfig.h index 1e3db20e98..4634a2dec0 100644 --- a/components/freertos/config/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/config/include/freertos/FreeRTOSConfig.h @@ -289,9 +289,6 @@ #ifdef CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS #define configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS 1 #endif /* CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS */ - #if CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER - #define configCHECK_MUTEX_GIVEN_BY_OWNER 1 - #endif /* CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER */ #endif /* !CONFIG_FREERTOS_SMP */ /* ------------------------------------------------ ESP-IDF Additions -------------------------------------------------- diff --git a/components/freertos/test_apps/freertos/sdkconfig.ci.code_coverage b/components/freertos/test_apps/freertos/sdkconfig.ci.code_coverage index 427f74c318..2f16a8edc8 100644 --- a/components/freertos/test_apps/freertos/sdkconfig.ci.code_coverage +++ b/components/freertos/test_apps/freertos/sdkconfig.ci.code_coverage @@ -96,8 +96,5 @@ CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y # Thread local storage deletion callbacks CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y -# Mutex owner check -CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y - # Port critical compliance check CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE=y From 1c278df88e8926e0392af513873c51c9eb45d7cf Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Wed, 1 Apr 2026 16:08:52 +0200 Subject: [PATCH 2/2] test(freertos): harden mutex owner assert reset test --- .../kernel/queue/test_freertos_mutex.c | 54 ++++++++++++++++--- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/components/freertos/test_apps/freertos/kernel/queue/test_freertos_mutex.c b/components/freertos/test_apps/freertos/kernel/queue/test_freertos_mutex.c index 598205bae8..6373ee790c 100644 --- a/components/freertos/test_apps/freertos/kernel/queue/test_freertos_mutex.c +++ b/components/freertos/test_apps/freertos/kernel/queue/test_freertos_mutex.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,35 +7,73 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" +#include +#include "esp_attr.h" +#include "soc/soc_caps.h" +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE +#include "hal/cache_ll.h" +#endif #include "unity.h" #include "test_utils.h" -#include "esp_system.h" /* If assertions aren't set to fail this code still crashes, but not with an abort... */ -#if CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER && !CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE +#if !CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE + +/* Persists across reset so stage 2 can confirm stage 1 reached the failure path. */ +static __NOINIT_ATTR bool s_restart_marker; +/* Reinitialized on reboot so stage 2 can distinguish a real restart from normal return. */ +static bool s_runtime_marker; static void mutex_release_task(void* arg) { SemaphoreHandle_t mutex = (SemaphoreHandle_t) arg; xSemaphoreGive(mutex); + /* The spawned task gives a mutex it does not own. Because the mutex is still held by the + main task, the give flows through xSemaphoreGive(), xQueueGenericSend(), + prvCopyDataToQueue(), and xTaskPriorityDisinherit(), where configASSERT(pxTCB == + pxCurrentTCB) fires and the panic resets the chip. */ TEST_FAIL_MESSAGE("should not be reached"); } -static void check_reset_reason_panic(void) +static void setup_restart_markers(void) { - TEST_ASSERT_EQUAL(ESP_RST_PANIC, esp_reset_reason()); + s_restart_marker = true; + s_runtime_marker = true; + +#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE + /* If internal data is behind a cache it might not be written to the physical memory when we crash + force a full writeback here to ensure this */ + cache_ll_writeback_all(CACHE_LL_LEVEL_INT_MEM, CACHE_TYPE_DATA, CACHE_LL_ID_ALL); +#endif +} + +static void check_system_restarted(void) +{ + /* s_restart_marker is NOINIT so it still holds the true value set in stage 1 across the + * software reset; s_runtime_marker is BSS so it is re-initialized to false on every boot. */ + TEST_ASSERT_TRUE(s_restart_marker); + TEST_ASSERT_FALSE(s_runtime_marker); + + s_restart_marker = false; } static void do_mutex_release_assert(void) { + setup_restart_markers(); + SemaphoreHandle_t mutex = xSemaphoreCreateMutex(); xSemaphoreTake(mutex, portMAX_DELAY); xTaskCreate(&mutex_release_task, "mutex_release", 2048, mutex, UNITY_FREERTOS_PRIORITY + 1, NULL); - vTaskDelay(1); + /* Stage 1 must not return to Unity before the releaser trips the panic. */ + vTaskSuspend(NULL); } -TEST_CASE_MULTIPLE_STAGES("mutex released not by owner causes an assert", "[freertos][reset=assert,SW_CPU_RESET]", +TEST_CASE_MULTIPLE_STAGES("mutex released not by owner causes an assert", "[freertos]", + /* On dual-core ESP32 the panic handler can deadlock on S32C1I if the + * stalled core was mid-atomic, causing an RTC WDT reset (ESP_RST_WDT) + * instead of ESP_RST_PANIC. Stage 2 therefore verifies a real reboot + * via NOINIT and BSS state instead of checking the reset reason. */ do_mutex_release_assert, - check_reset_reason_panic) + check_system_restarted) #endif