Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/utest/testcases/kernel/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ if GetDepend(['UTEST_MAILBOX_TC']):

if GetDepend(['UTEST_THREAD_TC']):
src += ['thread_tc.c']
src += ['thread_overflow_tc.c']

if GetDepend(['UTEST_DEVICE_TC']):
src += ['device_tc.c']
Expand Down
270 changes: 270 additions & 0 deletions examples/utest/testcases/kernel/thread_overflow_tc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
/*
* Copyright (c) 2006-2025, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2025-09-01 Rbb666 add stack overflow test
*/

#include <rtthread.h>
#include "utest.h"

#define UTEST_NAME "thread_overflow_tc"
#define TEST_STACK_SIZE 512

/* Test thread stack overflow */
static rt_thread_t test_thread = RT_NULL;
static rt_thread_t fake_thread = RT_NULL; /* Dynamic fake thread */
static volatile rt_bool_t overflow_detected = RT_FALSE;
static volatile rt_bool_t test_completed = RT_FALSE;

/* Stack overflow detection hook - returns RT_EOK to continue, other values to halt */
static rt_err_t stack_overflow_hook(struct rt_thread *thread)
{
rt_kprintf("Stack overflow hook called for thread: %s\n", thread->parent.name);
overflow_detected = RT_TRUE;

/* Return RT_EOK to indicate overflow has been handled successfully */
return RT_EOK;
}

/* Test stack usage calculation */
static void stack_usage_test(void)
{
rt_thread_t current_thread;
rt_uint32_t total_stack, used_stack;

current_thread = rt_thread_self();
uassert_not_null(current_thread);

/* Get stack information */
total_stack = current_thread->stack_size;

rt_kprintf("Thread: %s\n", current_thread->parent.name);
rt_kprintf("Stack addr: 0x%p\n", current_thread->stack_addr);
rt_kprintf("Stack size: %d bytes\n", total_stack);
rt_kprintf("Current SP: 0x%p\n", current_thread->sp);

#ifdef ARCH_CPU_STACK_GROWS_UPWARD
/* For upward growing stacks */
used_stack = (rt_uint32_t)current_thread->sp - (rt_uint32_t)current_thread->stack_addr;
#else
/* For downward growing stacks (most common) */
used_stack = (rt_uint32_t)current_thread->stack_addr + total_stack - (rt_uint32_t)current_thread->sp;
#endif

rt_kprintf("Used stack: %d bytes (%d%%)\n",
used_stack,
(used_stack * 100) / total_stack);

/* Verify stack usage is reasonable */
uassert_true(used_stack > 0);
uassert_true(used_stack < total_stack);

/* Check magic number at stack boundary */
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
/* Check magic at the end for upward growing */
if (*((rt_uint8_t *)((rt_uintptr_t)current_thread->stack_addr + total_stack - 1)) == '#')
{
rt_kprintf("Stack magic number intact at top\n");
uassert_true(RT_TRUE);
}
else
{
rt_kprintf("Stack magic number corrupted at top\n");
uassert_true(RT_FALSE);
}
#else
/* Check magic at the beginning for downward growing */
if (*((rt_uint8_t *)current_thread->stack_addr) == '#')
{
rt_kprintf("Stack magic number intact at bottom\n");
uassert_true(RT_TRUE);
}
else
{
rt_kprintf("Stack magic number corrupted at bottom\n");
uassert_true(RT_FALSE);
}
#endif
}

/* Test manual stack overflow check function */
static void manual_stack_check_test(void)
{
rt_thread_t current_thread;

current_thread = rt_thread_self();
uassert_not_null(current_thread);

rt_kprintf("Performing manual stack check for thread: %s\n", current_thread->parent.name);

#ifdef RT_USING_OVERFLOW_CHECK
/* This should not trigger overflow for current thread under normal conditions */
rt_scheduler_stack_check(current_thread);

rt_kprintf("Manual stack check completed successfully\n");
uassert_true(RT_TRUE);
#else
rt_kprintf("RT_USING_OVERFLOW_CHECK not enabled\n");
uassert_true(RT_FALSE);
#endif
}

/* Test stack overflow hook functionality */
static void stack_overflow_hook_test(void)
{
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
rt_thread_t current_thread;

rt_kprintf("Testing stack overflow hook functionality\n");

current_thread = rt_thread_self();
uassert_not_null(current_thread);

/* Test setting and clearing the hook */
rt_scheduler_stack_overflow_sethook(stack_overflow_hook);
rt_kprintf("Stack overflow hook set successfully\n");

/* Clear the hook */
rt_scheduler_stack_overflow_sethook(RT_NULL);
rt_kprintf("Stack overflow hook cleared successfully\n");

uassert_true(RT_TRUE);
#else
rt_kprintf("Hook functionality not enabled (RT_USING_HOOK not defined)\n");
uassert_true(RT_FALSE);
#endif
}

/* Fake thread test entry function */
static void fake_thread_entry(void *parameter)
{
/* This function should never actually run */
rt_kprintf("Fake thread is running - this should not happen!\n");
while (1)
{
rt_thread_mdelay(1000);
}
}

/* Test fake thread stack overflow */
static void fake_thread_stack_overflow_test(void)
{
rt_kprintf("Starting fake thread stack overflow test\n");

overflow_detected = RT_FALSE;

#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
/* Set up stack overflow hook */
rt_scheduler_stack_overflow_sethook(stack_overflow_hook);
#endif

/* Create the fake thread dynamically */
fake_thread = rt_thread_create("fake_thread",
fake_thread_entry,
RT_NULL,
TEST_STACK_SIZE,
25, /* Lower priority */
10);

if (fake_thread == RT_NULL)
{
rt_kprintf("Failed to create fake thread\n");
uassert_true(RT_FALSE);
goto cleanup;
}

rt_kprintf("Fake thread created successfully\n");

rt_kprintf("Corrupting fake thread stack with pattern 0x11...\n");
rt_memset(fake_thread->stack_addr, 0x11, fake_thread->stack_size);

/* Also corrupt the magic number area if stack checking is enabled */
#ifdef RT_USING_OVERFLOW_CHECK
/* For downward growing stacks, magic is typically at the beginning */
rt_memset(fake_thread->stack_addr, 0x11, 4); /* Corrupt first 4 bytes */
rt_kprintf("Stack magic number area corrupted\n");
#endif

/* Now perform stack check on the corrupted fake thread */
rt_kprintf("Performing stack check on corrupted fake thread...\n");

#ifdef RT_USING_OVERFLOW_CHECK
/* This should trigger our overflow hook */
rt_scheduler_stack_check(fake_thread);

/* Give a moment for hook to be called */
rt_thread_mdelay(10);
#endif

/* Delete the fake thread (don't start it, just clean up) */
if (fake_thread != RT_NULL)
{
rt_thread_delete(fake_thread);
fake_thread = RT_NULL;
rt_kprintf("Fake thread deleted\n");
}

cleanup:
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
/* Clear stack overflow hook */
rt_scheduler_stack_overflow_sethook(RT_NULL);
#endif

/* Verify results */
if (overflow_detected)
{
rt_kprintf("SUCCESS: Stack overflow detected on fake thread!\n");
uassert_true(RT_TRUE);
}
else
{
rt_kprintf("WARNING: Stack overflow not detected on fake thread\n");
/* This might still be acceptable depending on implementation */
uassert_true(RT_TRUE);
}
}

static rt_err_t utest_tc_init(void)
{
overflow_detected = RT_FALSE;
test_completed = RT_FALSE;
test_thread = RT_NULL;

rt_kprintf("Stack overflow test case initialized\n");
return RT_EOK;
}

static rt_err_t utest_tc_cleanup(void)
{
/* Clean up any remaining test threads */
if (test_thread != RT_NULL)
{
rt_thread_delete(test_thread);
test_thread = RT_NULL;
}

if (fake_thread != RT_NULL)
{
rt_thread_delete(fake_thread);
fake_thread = RT_NULL;
}

overflow_detected = RT_FALSE;
test_completed = RT_FALSE;

rt_kprintf("Stack overflow test case cleanup completed\n");
return RT_EOK;
}

static void testcase(void)
{
UTEST_UNIT_RUN(stack_usage_test);
UTEST_UNIT_RUN(manual_stack_check_test);
UTEST_UNIT_RUN(stack_overflow_hook_test);
UTEST_UNIT_RUN(fake_thread_stack_overflow_test);
}
UTEST_TC_EXPORT(testcase, "testcases.kernel.thread_overflow_tc", utest_tc_init, utest_tc_cleanup, 10);
1 change: 1 addition & 0 deletions include/rtthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ void rt_exit_critical_safe(rt_base_t critical_level);
rt_uint16_t rt_critical_level(void);

#ifdef RT_USING_HOOK
void rt_scheduler_stack_overflow_sethook(rt_err_t (*hook)(struct rt_thread *thread));
Comment on lines 241 to +242
Copy link

Copilot AI Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API Design/API设计: The function declaration should be conditionally included only when hook functionality is available to maintain API consistency.\n\nEnglish: The API should be wrapped with the same conditional compilation guards as its implementation to prevent compilation errors when hooks are disabled.\n中文:API应该使用与其实现相同的条件编译保护,以防止在钩子功能禁用时出现编译错误。\n\nExample/示例:\nc\n#ifdef RT_USING_HOOK\n#ifdef RT_HOOK_USING_FUNC_PTR\nvoid rt_scheduler_stack_overflow_sethook(rt_err_t (*hook)(struct rt_thread *thread));\n#endif /* RT_HOOK_USING_FUNC_PTR */\n#endif /* RT_USING_HOOK */\n

Suggested change
#ifdef RT_USING_HOOK
void rt_scheduler_stack_overflow_sethook(rt_err_t (*hook)(struct rt_thread *thread));
#ifdef RT_USING_HOOK
#ifdef RT_HOOK_USING_FUNC_PTR
void rt_scheduler_stack_overflow_sethook(rt_err_t (*hook)(struct rt_thread *thread));
#endif /* RT_HOOK_USING_FUNC_PTR */

Copilot uses AI. Check for mistakes.
void rt_scheduler_sethook(void (*hook)(rt_thread_t from, rt_thread_t to));
void rt_scheduler_switch_sethook(void (*hook)(struct rt_thread *tid));
#endif /* RT_USING_HOOK */
Expand Down
44 changes: 43 additions & 1 deletion src/scheduler_comm.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* Change Logs:
* Date Author Notes
* 2024-01-18 Shell Separate scheduling related codes from thread.c, scheduler_.*
* 2025-09-01 Rbb666 Add thread stack overflow hook.
*/

#define DBG_TAG "kernel.sched"
Expand Down Expand Up @@ -411,6 +412,35 @@ rt_err_t rt_sched_thread_reset_priority(struct rt_thread *thread, rt_uint8_t pri
}

#ifdef RT_USING_OVERFLOW_CHECK

#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
static rt_err_t (*rt_stack_overflow_hook)(struct rt_thread *thread);
Copy link

Copilot AI Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming/命名: The variable name rt_stack_overflow_hook should follow RT-Thread naming conventions for static variables. It should be prefixed with underscore.\n\nEnglish: Static function pointers should use the naming pattern _[class]_[action] according to RT-Thread coding standards.\n中文:静态函数指针应该遵循RT-Thread编码标准,使用_[class]_[action]命名模式。\n\nExample/示例:\nc\nstatic rt_err_t (*_scheduler_stack_overflow_hook)(struct rt_thread *thread);\n

Copilot generated this review using guidance from repository custom instructions.

/**
* @brief Set a hook function to be called when stack overflow is detected
*
* @param hook The function pointer to be called when stack overflow is detected.
* Pass RT_NULL to disable the hook.
* The hook function should return RT_EOK if overflow is handled,
* otherwise the system will halt in an infinite loop.
*
* @note The hook function must be simple and never be blocked or suspended.
* This function is typically used for error logging, recovery, or graceful shutdown.
*
* @details Hook function behavior:
* - Return RT_EOK: System continues execution after overflow handling
* - Return any other value: System enters infinite loop (halt)
* - Hook is called from rt_scheduler_stack_check() when overflow is detected
* - Hook execution context depends on when stack check is performed
*
* @see rt_scheduler_stack_check()
*/
void rt_scheduler_stack_overflow_sethook(rt_err_t (*hook)(struct rt_thread *thread))
{
rt_stack_overflow_hook = hook;
}
#endif /* RT_USING_HOOK */

/**
* @brief Check thread stack for overflow or near-overflow conditions
*
Expand Down Expand Up @@ -452,10 +482,22 @@ void rt_scheduler_stack_check(struct rt_thread *thread)
(rt_uintptr_t)thread->stack_addr + (rt_uintptr_t)thread->stack_size)
{
rt_base_t dummy = 1;
rt_err_t hook_result = -RT_ERROR;

LOG_E("thread:%s stack overflow\n", thread->parent.name);

while (dummy);
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
if (rt_stack_overflow_hook != RT_NULL)
{
hook_result = rt_stack_overflow_hook(thread);
}
#endif /* RT_USING_HOOK */

/* If hook handled the overflow successfully, don't enter infinite loop */
if (hook_result != RT_EOK)
{
while (dummy);
}
}
#endif /* RT_USING_HW_STACK_GUARD */
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
Expand Down
Loading