Fatal Errors
Software Errors Triggered in Source Code
Zephyr provides several methods for inducing fatal error conditions through either build-time checks, conditionally compiled assertions, or deliberately invoked panic or oops conditions.
Failed runtime assertions (ZASSERT() and the legacy __ASSERT()) induce
a fatal error. See Assertions for details on the assertion APIs and how
their failure behavior is configured.
Kernel Oops
A kernel oops is a software triggered fatal error invoked by
k_oops(). This should be used to indicate an unrecoverable condition
in application logic.
The fatal error reason code generated will be K_ERR_KERNEL_OOPS.
Kernel Panic
A kernel error is a software triggered fatal error invoked by
k_panic(). This should be used to indicate that the Zephyr kernel is
in an unrecoverable state. Implementations of
k_sys_fatal_error_handler() should not return if the kernel
encounters a panic condition, as the entire system needs to be reset.
Threads running in user mode are not permitted to invoke k_panic(),
and doing so will generate a kernel oops instead. Otherwise, the fatal error
reason code generated will be K_ERR_KERNEL_PANIC.
Exceptions
Spurious Interrupts
If the CPU receives a hardware interrupt on an interrupt line that has not had
a handler installed with IRQ_CONNECT() or irq_connect_dynamic(),
then the kernel will generate a fatal error with the reason code
K_ERR_SPURIOUS_IRQ().
Stack Overflows
In the event that a thread pushes more data onto its execution stack than its
stack buffer provides, the kernel may be able to detect this situation and
generate a fatal error with a reason code of K_ERR_STACK_CHK_FAIL.
If a thread is running in user mode, then stack overflows are always caught, as the thread will simply not have permission to write to adjacent memory addresses outside of the stack buffer. Because this is enforced by the memory protection hardware, there is no risk of data corruption to memory that the thread would not otherwise be able to write to.
If a thread is running in supervisor mode, or if CONFIG_USERSPACE is
not enabled, depending on configuration stack overflows may or may not be
caught. CONFIG_HW_STACK_PROTECTION is supported on some
architectures and will catch stack overflows in supervisor mode, including
when handling a system call on behalf of a user thread. Typically this is
implemented via dedicated CPU features, or read-only MMU/MPU guard regions
placed immediately adjacent to the stack buffer. Stack overflows caught in this
way can detect the overflow, but cannot guarantee against data corruption and
should be treated as a very serious condition impacting the health of the
entire system.
If a platform lacks memory management hardware support,
CONFIG_STACK_SENTINEL is a software-only stack overflow detection
feature which periodically checks if a sentinel value at the end of the stack
buffer has been corrupted. It does not require hardware support, but provides
no protection against data corruption. Since the checks are typically done at
interrupt exit, the overflow may be detected a nontrivial amount of time after
the stack actually overflowed.
Finally, Zephyr supports GCC compiler stack canaries via
CONFIG_STACK_CANARIES. If enabled, the compiler will insert a canary
value randomly generated at boot into function stack frames, checking that the
canary has not been overwritten at function exit. If the check fails, the
compiler invokes __stack_chk_fail(), whose Zephyr implementation
invokes a fatal stack overflow error. An error in this case does not indicate
that the entire stack buffer has overflowed, but instead that the current
function stack frame has been corrupted. See the compiler documentation for
more details.
By default a single canary value, generated at boot, is shared by all threads.
When CONFIG_STACK_CANARIES_TLS is enabled, the canary is
instead stored in thread-local storage so that
each thread has its own value, making the canary location and value harder to
predict at the cost of additional per-thread setup.
As a complementary hardening measure, CONFIG_STACK_POINTER_RANDOM
applies a randomized offset to each thread’s initial stack pointer when the
thread is created. This is a limited form of address space layout randomization
that makes the location of any given stack frame non-deterministic, hindering
some classes of security attack, at the cost of consuming up to the configured
number of bytes from each thread’s stack region.
Other Exceptions
Any other type of unhandled CPU exception will generate an error code of
K_ERR_CPU_EXCEPTION.
Fatal Error Handling
The policy for what to do when encountering a fatal error is determined by the
implementation of the k_sys_fatal_error_handler() function. This
function has a default implementation with weak linkage that calls
LOG_PANIC() to dump all pending logging messages and then unconditionally
halts the system with k_fatal_halt().
Applications are free to implement their own error handling policy by
overriding the implementation of k_sys_fatal_error_handler().
If the implementation returns, the faulting thread will be aborted and
the system will otherwise continue to function. See the documentation for
this function for additional details and constraints.