diff --git a/configure.ac b/configure.ac index 970cc5081..72cbde789 100644 --- a/configure.ac +++ b/configure.ac @@ -217,11 +217,17 @@ AC_ARG_ENABLE([debug-trace-errcodes], [ ENABLED_DEBUG_TRACE_ERRCODES=no ] ) -if test "$ENABLED_DEBUG_TRACE_ERRCODES" = "yes" +if test "$ENABLED_DEBUG_TRACE_ERRCODES" != "no" then AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_DEBUG_TRACE_ERROR_CODES" fi +if test "$ENABLED_DEBUG_TRACE_ERRCODES" = "backtrace" +then + AM_CFLAGS="$AM_CFLAGS -g -funwind-tables -DWOLFSSL_DEBUG_BACKTRACE_ERROR_CODES" + AM_LDFLAGS="$AM_LDFLAGS -lbacktrace" +fi + # Start without certificates enabled and enable if a certificate algorithm is # enabled ENABLED_CERTS="no" @@ -9981,7 +9987,7 @@ echo "" >> $OPTION_FILE echo "#endif /* WOLFSSL_OPTIONS_H */" >> $OPTION_FILE echo "" >> $OPTION_FILE -if test "$ENABLED_DEBUG_TRACE_ERRCODES" = "yes" +if test "$ENABLED_DEBUG_TRACE_ERRCODES" != "no" then support/gen-debug-trace-error-codes.sh || AC_MSG_ERROR([Header generation for debug-trace-errcodes failed.]) fi diff --git a/wolfcrypt/src/logging.c b/wolfcrypt/src/logging.c index f28a71ef7..7d7571ed8 100644 --- a/wolfcrypt/src/logging.c +++ b/wolfcrypt/src/logging.c @@ -1674,3 +1674,130 @@ void WOLFSSL_ERROR_MSG(const char* msg) } #endif /* DEBUG_WOLFSSL || WOLFSSL_NGINX || WOLFSSL_HAPROXY */ + +#ifdef WOLFSSL_DEBUG_BACKTRACE_ERROR_CODES + +#include + +#if BACKTRACE_SUPPORTED != 1 + #error WOLFSSL_DEBUG_BACKTRACE_ERROR_CODES is defined but BACKTRACE_SUPPORTED is 0. +#endif + +#if !defined(WOLFSSL_MUTEX_INITIALIZER) && defined(WOLFSSL_NO_ATOMICS) + #error WOLFSSL_DEBUG_BACKTRACE_ERROR_CODES requires WOLFSSL_MUTEX_INITIALIZER or wolfSSL_Atomic_Ints. +#endif + +#include + +static int backtrace_callback(void *data, uintptr_t pc, const char *filename, + int lineno, const char *function) +{ + if (function == NULL) + return 0; + /* the first callback is for the call to wc_print_backtrace() -- skip it. */ + if (*(int *)data == 0) { + *(int *)data = 1; + return 0; + } +#ifdef NO_STDIO_FILESYSTEM + printf(" #%d %p in %s %s:%d\n", (*(int *)data)++, (void *)pc, + function, filename, lineno); +#else + fprintf(stderr, " #%d %p in %s %s:%d\n", (*(int *)data)++, (void *)pc, + function, filename, lineno); +#endif + return 0; +} + +static void backtrace_error(void *data, const char *msg, int errnum) { + (void)data; +#ifdef NO_STDIO_FILESYSTEM + printf("ERR TRACE: error %d while backtracing: %s", errnum, msg); +#else + fprintf(stderr, "ERR TRACE: error %d while backtracing: %s", errnum, msg); +#endif +} + +static void backtrace_creation_error(void *data, const char *msg, int errnum) { + (void)data; +#ifdef NO_STDIO_FILESYSTEM + printf("ERR TRACE: internal error %d " + "while initializing backtrace facility: %s", errnum, msg); + printf("ERR TRACE: internal error " + "while initializing backtrace facility"); +#else + fprintf(stderr, "ERR TRACE: internal error %d " + "while initializing backtrace facility: %s", errnum, msg); +#endif +} + +static int backtrace_init(struct backtrace_state **backtrace_state) { +#ifdef WOLFSSL_MUTEX_INITIALIZER + static wolfSSL_Mutex backtrace_create_state_mutex = + WOLFSSL_MUTEX_INITIALIZER(backtrace_create_state_mutex); + if (wc_LockMutex(&backtrace_create_state_mutex) != 0) + return -1; +#elif defined(WOLFSSL_ATOMIC_OPS) + static wolfSSL_Atomic_Int init_count = 0; + if (wolfSSL_Atomic_Int_FetchAdd(&init_count, 1) != 1) + return -1; +#endif + if (*backtrace_state == NULL) { + /* passing a NULL filename to backtrace_create_state() tells + * libbacktrace to use a target-specific strategy to determine the + * executable. "libbacktrace supports ELF, PE/COFF, Mach-O, and XCOFF + * executables with DWARF debugging information. In other words, it + * supports GNU/Linux, *BSD, macOS, Windows, and AIX." + */ + *backtrace_state = backtrace_create_state( + NULL, 0, backtrace_creation_error, NULL); + } +#ifdef WOLFSSL_MUTEX_INITIALIZER + wc_UnLockMutex(&backtrace_create_state_mutex); +#endif + if (*backtrace_state == NULL) + return -1; + return 0; +} + +void wc_backtrace_render(void) { + static wolfSSL_Mutex backtrace_mutex WOLFSSL_MUTEX_INITIALIZER_CLAUSE(backtrace_mutex); + static struct backtrace_state *backtrace_state = NULL; + int depth = 0; + +#ifndef WOLFSSL_MUTEX_INITIALIZER + static wolfSSL_Atomic_Int init_count = 0; + if (init_count != 1) { + if (wolfSSL_Atomic_Int_FetchSub(&init_count, 1) != -1) + return; + if (wc_InitMutex(&backtrace_mutex) != 0) + return; + init_count = 1; + } +#endif + + /* backtrace_state can't be shared between threads even when + * BACKTRACE_SUPPORTS_THREADS == 1, so we serialize the render op. this + * helpfully mutexes the initialization too. + */ + if (wc_LockMutex(&backtrace_mutex) != 0) + return; + + if (backtrace_state == NULL) { + if (backtrace_init(&backtrace_state) < 0) { + wc_UnLockMutex(&backtrace_mutex); + return; + } + } + + /* note that the optimizer can produce misleading backtraces, even with + * -funwind-tables. in contrast, the macro-generated "ERR TRACE" message + * from WC_ERR_TRACE() always accurately identifies the error code point. + */ + backtrace_full(backtrace_state, 0, backtrace_callback, backtrace_error, + (void *)&depth); + + wc_UnLockMutex(&backtrace_mutex); +} + +#endif /* WOLFSSL_DEBUG_BACKTRACE_ERROR_CODES */ diff --git a/wolfssl/wolfcrypt/error-crypt.h b/wolfssl/wolfcrypt/error-crypt.h index 8fb71b3c6..413868ebb 100644 --- a/wolfssl/wolfcrypt/error-crypt.h +++ b/wolfssl/wolfcrypt/error-crypt.h @@ -298,17 +298,31 @@ WOLFSSL_ABI WOLFSSL_API const char* wc_GetErrorString(int error); #undef WOLFSSL_DEBUG_TRACE_ERROR_CODES #endif #ifdef WOLFSSL_DEBUG_TRACE_ERROR_CODES + extern void wc_backtrace_render(void); #define WC_NO_ERR_TRACE(label) (CONST_NUM_ERR_ ## label) + #ifndef WOLFSSL_DEBUG_BACKTRACE_RENDER_CLAUSE + #ifdef WOLFSSL_DEBUG_BACKTRACE_ERROR_CODES + #define WOLFSSL_DEBUG_BACKTRACE_RENDER_CLAUSE wc_backtrace_render() + #else + #define WOLFSSL_DEBUG_BACKTRACE_RENDER_CLAUSE (void)0 + #endif + #endif #ifndef WC_ERR_TRACE #ifdef NO_STDIO_FILESYSTEM #define WC_ERR_TRACE(label) \ - ( printf("ERR TRACE: %s L %d " #label " (%d)\n", \ - __FILE__, __LINE__, label), label) + ( printf("ERR TRACE: %s L %d %s (%d)\n", \ + __FILE__, __LINE__, #label, label), \ + WOLFSSL_DEBUG_BACKTRACE_RENDER_CLAUSE, \ + label \ + ) #else #define WC_ERR_TRACE(label) \ ( fprintf(stderr, \ - "ERR TRACE: %s L %d " #label " (%d)\n", \ - __FILE__, __LINE__, label), label) + "ERR TRACE: %s L %d %s (%d)\n", \ + __FILE__, __LINE__, #label, label), \ + WOLFSSL_DEBUG_BACKTRACE_RENDER_CLAUSE, \ + label \ + ) #endif #endif #include