Skip to content

Commit

Permalink
arch/posix.h: implement ARCH_EXCEPT() with abort() for debug convenience
Browse files Browse the repository at this point in the history
Flush all messages and invoke `abort()` when a k_panic() or k_oops() is
hit in native_posix mode.

One of the main purposes of `native_posix` is to provide debug
convenience. When running in a debugger, `abort()` stops execution which
provides a backtrace and the ability to inspect all variables.

A good, sample use case is fuzzing failures in SOF, see an example in:
thesofproject/sof#8632

In such a case, this commit adds value even before using a
debugger. Without this commit, confusingly meaningless stack trace:

```
INFO: seed corpus: files: 1097 min: 1b max: 428b total: 90853b rss: 58Mb
Exiting due to fatal error
==314134== ERROR: libFuzzer: fuzz target exited
    #0 0x81d9637 in __sanitizer_print_stack_trace (zephyr.exe+0x81d9637)
    #1 0x80cc42b in fuzzer::PrintStackTrace() (zephyr.exe+0x80cc42b)
    zephyrproject-rtos#2 0x80ab79e in fuzzer::Fuzzer::ExitCallback() FuzzerLoop.cpp.o
    zephyrproject-rtos#3 0x80ab864 in fuzzer::Fuzzer::StaticExitCallback() (zephyr.exe+
    zephyrproject-rtos#4 0xf783dfe8  (/usr/lib32/libc.so.6+0x3dfe8)
    zephyrproject-rtos#5 0xf783e1e6 in exit (/usr/lib32/libc.so.6+0x3e1e6)
    zephyrproject-rtos#6 0x82a5488 in posix_exit boards/posix/native_posix/main.c:51:2

SUMMARY: libFuzzer: fuzz target exited
```

Thanks to this commit the `k_panic()` location is now immediately
available in test logs without even running anything locally:

```
INFO: seed corpus: files: 1097 min: 1b max: 428b total: 90853b rss: 58Mb
@ WEST_TOPDIR/sof/src/ipc/ipc3/handler.c:1623
ZEPHYR FATAL ERROR: 3
==315176== ERROR: libFuzzer: deadly signal
LLVMSymbolizer: error reading file: No such file or directory
    #0 0x81d9647 in __sanitizer_print_stack_trace (zephyr.exe+0x81d9647)
    #1 0x80cc43b in fuzzer::PrintStackTrace() (zephyr.exe+0x80cc43b)
    zephyrproject-rtos#2 0x80ab6be in fuzzer::Fuzzer::CrashCallback() FuzzerLoop.cpp.o
    zephyrproject-rtos#3 0x80ab77b in fuzzer::Fuzzer::StaticCrashSignalCallback()
    zephyrproject-rtos#4 0xf7f3159f  (linux-gate.so.1+0x59f)
    zephyrproject-rtos#5 0xf7f31578  (linux-gate.so.1+0x578)
    zephyrproject-rtos#6 0xf788ea16  (/usr/lib32/libc.so.6+0x8ea16)
    zephyrproject-rtos#7 0xf783b316 in raise (/usr/lib32/libc.so.6+0x3b316)
    zephyrproject-rtos#8 0xf7822120 in abort (/usr/lib32/libc.so.6+0x22120)
    zephyrproject-rtos#9 0x82afbde in ipc_cmd src/ipc/ipc3/handler.c:1623:2

NOTE: libFuzzer has rudimentary signal handlers.
      Combine libFuzzer with AddressSanitizer or similar for better
    crash reports.
SUMMARY: libFuzzer: deadly signal
```

The full stack trace is now immediately available when running
zephyr.exe in gdb:

```
./scripts/fuzz.sh  -- -DEXTRA_CFLAGS="-O0 -g3"

gdb build-fuzz/zephyr/zephyr.exe

run
backtrace

 zephyrproject-rtos#2  0xf783b317 in raise () from /usr/lib32/libc.so.6
 zephyrproject-rtos#3  0xf7822121 in abort () from /usr/lib32/libc.so.6
 zephyrproject-rtos#4  0x082afbdf in ipc_cmd (_hdr=0x8b...) at src/ipc/ipc3/handler.c:1623
 zephyrproject-rtos#5  0x082fbf4b in ipc_platform_do_cmd (ipc=0x8b161c0)
                                    at src/platform/posix/ipc.c:162
 zephyrproject-rtos#6  0x082e1e07 in ipc_do_cmd (data=0x8b161c0 <heapmem+1472>)
                                    at src/ipc/ipc-common.c:328
 zephyrproject-rtos#7  0x083696aa in task_run (task=0x8b161e8 <heapmem+1512>)
                                    at zephyr/include/rtos/task.h:94
 zephyrproject-rtos#8  0x083682dc in edf_work_handler (work=0x8b1621c <heapmem+1564>)
                                    at zephyr/edf_schedule.c:32
 zephyrproject-rtos#9  0x085245af in work_queue_main (workq_ptr=0x8b15b00 <edf_workq>,...)
                                         at zephyr/kernel/work.c:688
 zephyrproject-rtos#10 0x0823a6bc in z_thread_entry (entry=0x8523be0 <work_queue_main>,..
                                    at zephyr/lib/os/thread_entry.c:48
 zephyrproject-rtos#11 0x0829a6a1 in posix_arch_thread_entry (pa_thread_status=0x8630648 ..
                                  at zephyr/arch/posix/core/thread.c:56
 zephyrproject-rtos#12 0x0829c043 in posix_thread_starter (arg=0x4)
                              at zephyr/arch/posix/core/posix_core.c:293
 zephyrproject-rtos#13 0x080f6041 in asan_thread_start(void*) ()
 zephyrproject-rtos#14 0xf788c73c in ?? () from /usr/lib32/libc.so.6
```

Signed-off-by: Marc Herbert <[email protected]>
  • Loading branch information
marc-hb committed Feb 3, 2024
1 parent 9852e8e commit 682b2fe
Showing 1 changed file with 48 additions and 0 deletions.
48 changes: 48 additions & 0 deletions include/zephyr/arch/posix/arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#ifndef ZEPHYR_INCLUDE_ARCH_POSIX_ARCH_H_
#define ZEPHYR_INCLUDE_ARCH_POSIX_ARCH_H_

#include <stdlib.h>

/* Add include for DTS generated information */
#include <zephyr/devicetree.h>

Expand All @@ -38,6 +40,52 @@ extern "C" {
#define ARCH_STACK_PTR_ALIGN 4
#endif

/* "native_posix" should really use CONFIG_LOG_MODE_IMMEDIATE=y but better safe
* than sorry: debugging crashes is painful enough already, so try to be nice
* and flush all messages. The deferred logging code may also want to enjoy
* native_posix too.
*
* Other archs use Zephyr's elaborate "Fatal Errors" framework which takes care
* of flushing logs but native_posix is simpler so we have to do it ourselves.
*/
static inline void posix_arch_log_immediate(void)
{
#if defined(CONFIG_LOG) && !defined(CONFIG_LOG_MODE_MINIMAL)
/* We can't #include the higher-level "log_ctrl.h" in this low-level
* file here because this descends into .h dependency hell. So let's
* use a one-line forward declaration instead. This void->void
* prototype does not look like it's going to change much in the
* future.
*
* We can't just declare "void log_panic(void);" here because system
* calls are complicated. As ARCH_EXCEPT() can't be possibly used in
* user mode, log_panic() is equivalent to just z_impl_log_panic().
* Exceptionally invoke the latter directly.
*/
extern void z_impl_log_panic(void);
z_impl_log_panic();
#endif
}

/* Copied from kernel.h */
#define _EXCEPT_LOC() __ASSERT_PRINT("@ %s:%d\n", __FILE__, __LINE__)

/* Invoked by k_panic() and k_oops().
*
* _EXCEPT_LOC() and "ZEPHYR FATAL ERROR" mimic z_fatal_error()
*
* [Z]TEST trigger panics on purpose: don't exit and don't interfere.
*/
#if !defined(CONFIG_ZTEST) && !defined(CONFIG_TEST)
#define ARCH_EXCEPT(reason_p) do { \
posix_arch_log_immediate(); \
_EXCEPT_LOC(); \
printk("ZEPHYR FATAL ERROR: %u\n", reason_p); \
abort(); CODE_UNREACHABLE; \
} while (false)
#endif

/* Exception context */
struct __esf {
uint32_t dummy; /*maybe we will want to add something someday*/
};
Expand Down

0 comments on commit 682b2fe

Please sign in to comment.