diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -203,26 +203,11 @@ [enable_debug=$enableval], [enable_debug=no]) -# Enable ASAN -AC_ARG_ENABLE([asan], - [AS_HELP_STRING([--enable-asan], - [enable address sanitizer compiler flags (implies --enable-debug, default is no)])], - [enable_asan=$enableval], - [enable_asan=no]) - -# Enable TSAN -AC_ARG_ENABLE([tsan], - [AS_HELP_STRING([--enable-tsan], - [enable thread sanitizer compiler flags; requires 64-bit target architecture (implies --enable-debug, default is no)])], - [enable_tsan=$enableval], - [enable_tsan=no]) - -# Enable UBSAN -AC_ARG_ENABLE([ubsan], - [AS_HELP_STRING([--enable-ubsan], - [enable undefined behavior sanitizer compiler flags (implies --enable-debug, default is no)])], - [enable_ubsan=$enableval], - [enable_ubsan=no]) +# Enable different -fsanitize options +AC_ARG_WITH([sanitizers], + [AS_HELP_STRING([--with-sanitizers], + [comma separated list of extra sanitizers to build with (default is none enabled)])], + [use_sanitizers=$withval]) # Turn warnings into errors AC_ARG_ENABLE([werror], @@ -234,62 +219,6 @@ AC_LANG_PUSH([C++]) AX_CHECK_COMPILE_FLAG([-Werror],[CXXFLAG_WERROR="-Werror"],[CXXFLAG_WERROR=""]) -if test "x$enable_asan" = xyes; then - enable_debug=yes - asan_failed=no - AX_CHECK_COMPILE_FLAG([-fsanitize=address], - [CXXFLAGS="$CXXFLAGS -fsanitize=address"], - [asan_failed=yes]) - AX_CHECK_LINK_FLAG([-fsanitize=address], - [LDFLAGS="$LDFLAGS -fsanitize=address"], - [asan_failed=yes]) - AX_CHECK_COMPILE_FLAG([-fno-omit-frame-pointer], - [CXXFLAGS="$CXXFLAGS -fno-omit-frame-pointer"], - [asan_failed=yes]) - - if test "x$asan_failed" = xyes; then - AC_MSG_ERROR("ASAN is not supported") - fi - # fix linkage in MacOs - [LIBTOOL_APP_LDFLAGS="$LIBTOOL_APP_LDFLAGS -fsanitize=address"], -fi - -if test "x$enable_tsan" = xyes; then - enable_debug=yes - tsan_failed=no - AX_CHECK_COMPILE_FLAG([-fsanitize=thread], - [CXXFLAGS="$CXXFLAGS -fsanitize=thread"], - [tsan_failed=yes]) - AX_CHECK_LINK_FLAG([-fsanitize=thread], - [LDFLAGS="$LDFLAGS -fsanitize=thread"], - [tsan_failed=yes]) - AX_CHECK_COMPILE_FLAG([-fno-omit-frame-pointer], - [CXXFLAGS="$CXXFLAGS -fno-omit-frame-pointer"], - [tsan_failed=yes]) - - if test "x$tsan_failed" = xyes; then - AC_MSG_ERROR("TSAN is not supported") - fi -fi - -if test "x$enable_ubsan" = xyes; then - enable_debug=yes - ubsan_failed=no - AX_CHECK_COMPILE_FLAG([-fsanitize=undefined], - [CXXFLAGS="$CXXFLAGS -fsanitize=undefined"], - [ubsan_failed=yes]) - AX_CHECK_LINK_FLAG([-fsanitize=undefined], - [LDFLAGS="$LDFLAGS -fsanitize=undefined"], - [ubsan_failed=yes]) - AX_CHECK_COMPILE_FLAG([-fno-omit-frame-pointer], - [CXXFLAGS="$CXXFLAGS -fno-omit-frame-pointer"], - [ubsan_failed=yes]) - - if test "x$ubsan_failed" = xyes; then - AC_MSG_ERROR("UBSAN is not supported") - fi -fi - if test "x$enable_debug" = xyes; then CPPFLAGS="$CPPFLAGS -DDEBUG -DDEBUG_LOCKORDER" if test "x$GCC" = xyes; then @@ -301,6 +230,26 @@ fi fi +if test x$use_sanitizers != x; then + # First check if the compiler accepts flags. If an incompatible pair like + # -fsanitize=address,thread is used here, this check will fail. This will also + # fail if a bad argument is passed, e.g. -fsanitize=undfeined + AX_CHECK_COMPILE_FLAG( + [[-fsanitize=$use_sanitizers]], + [[SANITIZER_CXXFLAGS=-fsanitize=$use_sanitizers]], + [AC_MSG_ERROR([compiler did not accept requested flags])]) + + # Some compilers (e.g. GCC) require additional libraries like libasan, + # libtsan, libubsan, etc. Make sure linking still works with the sanitize + # flag. This is a separate check so we can give a better error message when + # the sanitize flags are supported by the compiler but the actual sanitizer + # libs are missing. + AX_CHECK_LINK_FLAG( + [[-fsanitize=$use_sanitizers]], + [[SANITIZER_LDFLAGS=-fsanitize=$use_sanitizers]], + [AC_MSG_ERROR([linker did not accept requested flags, you are missing required libraries])]) +fi + ERROR_CXXFLAGS= if test "x$enable_werror" = "xyes"; then if test "x$CXXFLAG_WERROR" = "x"; then @@ -1344,6 +1293,8 @@ AC_SUBST(HARDENED_LDFLAGS) AC_SUBST(PIC_FLAGS) AC_SUBST(PIE_FLAGS) +AC_SUBST(SANITIZER_CXXFLAGS) +AC_SUBST(SANITIZER_LDFLAGS) AC_SUBST(SSE42_CXXFLAGS) AC_SUBST(SSE41_CXXFLAGS) AC_SUBST(AVX2_CXXFLAGS) @@ -1431,14 +1382,10 @@ echo " with bench = $use_bench" echo " with upnp = $use_upnp" echo " use asm = $use_asm" +echo " sanitizers = $use_sanitizers" echo " debug enabled = $enable_debug" echo " werror = $enable_werror" echo -echo " sanitizers " -echo " asan = $enable_asan" -echo " tsan = $enable_tsan" -echo " ubsan = $enable_ubsan" -echo echo " target os = $TARGET_OS" echo " build os = $BUILD_OS" echo diff --git a/doc/developer-notes.md b/doc/developer-notes.md --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -213,6 +213,57 @@ CXXFLAGS="-DDEBUG_LOCKORDER -g") inserts run-time checks to keep track of which locks are held, and adds warnings to the debug.log file if inconsistencies are detected. +**Sanitizers** + +Bitcoin can be compiled with various "sanitizers" enabled, which add +instrumentation for issues regarding things like memory safety, thread race +conditions, or undefined behavior. This is controlled with the +`--with-sanitizers` configure flag, which should be a comma separated list of +sanitizers to enable. The sanitizer list should correspond to supported +`-fsanitize=` options in your compiler. These sanitizers have runtime overhead, +so they are most useful when testing changes or producing debugging builds. + +Some examples: + +```bash +# Enable both the address sanitizer and the undefined behavior sanitizer +./configure --with-sanitizers=address,undefined + +# Enable the thread sanitizer +./configure --with-sanitizers=thread +``` + +If you are compiling with GCC you will typically need to install corresponding +"san" libraries to actually compile with these flags, e.g. libasan for the +address sanitizer, libtsan for the thread sanitizer, and libubsan for the +undefined sanitizer. If you are missing required libraries, the configure script +will fail with a linker error when testing the sanitizer flags. + +The test suite should pass cleanly with the `thread` and `undefined` sanitizers, +but there are a number of known problems when using the `address` sanitizer. The +address sanitizer is known to fail in +[sha256_sse4::Transform](/src/crypto/sha256_sse4.cpp) which makes it unusable +unless you also use `--disable-asm` when running configure. We would like to fix +sanitizer issues, so please send pull requests if you can fix any errors found +by the address sanitizer (or any other sanitizer). + +Not all sanitizer options can be enabled at the same time, e.g. trying to build +with `--with-sanitizers=address,thread` will fail in the configure script as +these sanitizers are mutually incompatible. Refer to your compiler manual to +learn more about these options and which sanitizers are supported by your +compiler. + +Additional resources: + + * [AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html) + * [LeakSanitizer](https://clang.llvm.org/docs/LeakSanitizer.html) + * [MemorySanitizer](https://clang.llvm.org/docs/MemorySanitizer.html) + * [ThreadSanitizer](https://clang.llvm.org/docs/ThreadSanitizer.html) + * [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) + * [GCC Instrumentation Options](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html) + * [Google Sanitizers Wiki](https://github.com/google/sanitizers/wiki) + * [Issue #12691: Enable -fsanitize flags in Travis](https://github.com/bitcoin/bitcoin/issues/12691) + Locking/mutex usage notes ------------------------- diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,8 +4,8 @@ DIST_SUBDIRS = secp256k1 univalue -AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) -AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) +AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(SANITIZER_LDFLAGS) +AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) $(SANITIZER_CXXFLAGS) AM_CPPFLAGS = $(HARDENED_CPPFLAGS) EXTRA_LIBRARIES =