diff --git a/Documentation/debugging/stackcheck.rst b/Documentation/debugging/stackcheck.rst index 39e848782a7..064d85cf587 100644 --- a/Documentation/debugging/stackcheck.rst +++ b/Documentation/debugging/stackcheck.rst @@ -56,8 +56,7 @@ Stack Overflow Software Check During Context Switching 2. Check if the sp register is out of bounds. Usage: - Enable CONFIG_STACKCHECK_SOFTWARE - You can set the detection length by STACKCHECK_MARGIN + Set the detection length by STACKCHECK_MARGIN Stack Overflow Hardware Check ----------------------------- diff --git a/Kconfig b/Kconfig index a6cb1a255ec..9249de831e1 100644 --- a/Kconfig +++ b/Kconfig @@ -2455,23 +2455,13 @@ config STACK_COLORATION Only supported by a few architectures. -config STACKCHECK_SOFTWARE - bool "Software detection of stack overflow" - depends on STACK_COLORATION && DEBUG_ASSERTIONS - ---help--- - When switching contexts, it will detect whether a stack overflow occurs. - Two methods are used here. - The first is to check the legitimacy of the value of the sp register; - the second is to check the specified number of bytes at the bottom of the stack. - If either of these two methods fails, an ASSERT will be triggered. - config STACKCHECK_MARGIN int "Stack overflow check size (bytes)" - depends on STACKCHECK_SOFTWARE - default 16 + depends on DEBUG_ASSERTIONS + default -1 ---help--- Specifies the number of bytes at the end of the stack to check for overflow. - A value of 0 disables additional checking. Increase this value for stricter + A value of -1 disables additional checking. Increase this value for stricter overflow detection, at the cost of additional overhead. config STACK_CANARIES diff --git a/sched/irq/irq_dispatch.c b/sched/irq/irq_dispatch.c index 561eae68787..33f7d7a035a 100644 --- a/sched/irq/irq_dispatch.c +++ b/sched/irq/irq_dispatch.c @@ -158,7 +158,7 @@ void irq_dispatch(int irq, FAR void *context) } #endif -#if defined(CONFIG_STACKCHECK_SOFTWARE) && CONFIG_STACKCHECK_MARGIN > 0 +#if defined(CONFIG_STACKCHECK_MARGIN) && (CONFIG_STACKCHECK_MARGIN > 0) DEBUGASSERT(up_check_intstack(this_cpu(), CONFIG_STACKCHECK_MARGIN) == 0); #endif diff --git a/sched/sched/CMakeLists.txt b/sched/sched/CMakeLists.txt index 83df2678436..1edfe87fc61 100644 --- a/sched/sched/CMakeLists.txt +++ b/sched/sched/CMakeLists.txt @@ -49,6 +49,12 @@ set(SRCS sched_get_stateinfo.c sched_switchcontext.c) +if(DEFINED CONFIG_STACKCHECK_MARGIN) + if(NOT CONFIG_STACKCHECK_MARGIN EQUAL -1) + list(APPEND SRCS nxsched_checkstackoverflow.c) + endif() +endif() + if(CONFIG_PRIORITY_INHERITANCE) list(APPEND SRCS sched_reprioritize.c) endif() diff --git a/sched/sched/Make.defs b/sched/sched/Make.defs index be076eca23d..7cc19db557b 100644 --- a/sched/sched/Make.defs +++ b/sched/sched/Make.defs @@ -32,6 +32,12 @@ CSRCS += sched_idletask.c sched_self.c sched_get_stackinfo.c sched_get_tls.c CSRCS += sched_sysinfo.c sched_get_stateinfo.c sched_getcpu.c CSRCS += sched_switchcontext.c +ifneq ($(CONFIG_STACKCHECK_MARGIN),) + ifneq ($(CONFIG_STACKCHECK_MARGIN),-1) + CSRCS += nxsched_checkstackoverflow.c + endif +endif + ifeq ($(CONFIG_PRIORITY_INHERITANCE),y) CSRCS += sched_reprioritize.c endif diff --git a/sched/sched/nxsched_checkstackoverflow.c b/sched/sched/nxsched_checkstackoverflow.c new file mode 100644 index 00000000000..163623781d5 --- /dev/null +++ b/sched/sched/nxsched_checkstackoverflow.c @@ -0,0 +1,94 @@ +/**************************************************************************** + * sched/sched/nxsched_checkstackoverflow.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include + +#include "sched/sched.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxsched_checkstackoverflow + * + * Description: + * Verify that the specified thread has not overflowed its stack. + * + * Behavior depends on CONFIG_STACKCHECK_MARGIN: + * + * - CONFIG_STACKCHECK_MARGIN == 0: + * Perform a strict boundary check. The current stack pointer must + * remain within the allocated stack region [base, top]. + * + * - CONFIG_STACKCHECK_MARGIN > 0: + * Perform an architecture-specific check with a safety margin. + * The stack must not extend beyond the reserved margin area. + * + * - CONFIG_STACKCHECK_MARGIN < 0: + * Stack checking is disabled at build time. In this case, calls to + * nxsched_checkstackoverflow() are replaced with a no-op macro. + * + * Input Parameters: + * tcb - Pointer to the TCB of the thread to be checked. + * + * Returned Value: + * None. The function will trigger a DEBUGASSERT if a stack overflow + * condition is detected. + * + ****************************************************************************/ + +void nxsched_checkstackoverflow(FAR struct tcb_s *tcb) +{ +#if (CONFIG_STACKCHECK_MARGIN == 0) + /* Strict stack pointer check: + * SP must remain within the allocated stack boundaries. + */ + + if (tcb->xcp.regs != NULL) + { + uintptr_t sp = up_getusrsp(tcb->xcp.regs); + uintptr_t top = (uintptr_t)tcb->stack_base_ptr + tcb->adj_stack_size; + uintptr_t bot = (uintptr_t)tcb->stack_base_ptr; + + DEBUGASSERT(sp > bot && sp <= top); + } + +#elif defined(CONFIG_STACK_COLORATION) && (CONFIG_STACKCHECK_MARGIN > 0) + /* Margin-based stack check: + * Allow some reserved margin area before reporting overflow. + */ + + DEBUGASSERT(up_check_tcbstack(tcb, CONFIG_STACKCHECK_MARGIN) == 0); +#endif +} diff --git a/sched/sched/sched.h b/sched/sched/sched.h index 2ce145d1db7..9e0432b90f1 100644 --- a/sched/sched/sched.h +++ b/sched/sched/sched.h @@ -393,6 +393,13 @@ static inline_function FAR struct tcb_s *this_task(void) } #endif +#if defined(CONFIG_STACKCHECK_MARGIN) && \ + (CONFIG_STACKCHECK_MARGIN != -1) +void nxsched_checkstackoverflow(FAR struct tcb_s *tcb); +#else +# define nxsched_checkstackoverflow(tcb) +#endif + #ifdef CONFIG_SMP bool nxsched_switch_running(int cpu, bool switch_equal); void nxsched_process_delivered(int cpu); diff --git a/sched/sched/sched_switchcontext.c b/sched/sched/sched_switchcontext.c index 0db4e01b984..aa4c5e7e564 100644 --- a/sched/sched/sched_switchcontext.c +++ b/sched/sched/sched_switchcontext.c @@ -49,20 +49,7 @@ void nxsched_switch_context(FAR struct tcb_s *from, FAR struct tcb_s *to) { -#ifdef CONFIG_STACKCHECK_SOFTWARE - if (from->xcp.regs) - { - uintptr_t sp = up_getusrsp(from->xcp.regs); - uintptr_t top = (uintptr_t)from->stack_base_ptr + from->adj_stack_size; - uintptr_t bottom = (uintptr_t)from->stack_base_ptr; - DEBUGASSERT(sp > bottom && sp <= top); - } - -#if CONFIG_STACKCHECK_MARGIN > 0 - DEBUGASSERT(up_check_tcbstack(from, CONFIG_STACKCHECK_MARGIN) == 0); -#endif - -#endif + nxsched_checkstackoverflow(from); #ifdef CONFIG_SCHED_SPORADIC /* Perform sporadic schedule operations */