Page MenuHomePhabricator

Added sanitizer support for configure script: ASAN, TSAN and UBSAN
ClosedPublic

Authored by CCulianu on Aug 15 2017, 22:07.

Details

Summary

Instrumentation support is here! You can enable ASAN with ./configure --enable-asan, TSAN with --enable-tsan, and UBSAN with --enable-ubsan.

Note that TSAN has issues on some Linux kernel/gcc combinations.

See this link: http://llvm.org/viewvc/llvm-project?view=revision&revision=282152

(TSAN needs a 64bit compile and also may or may not work on some kernel combinations.. supposedly a newer GCC 5.x works better than GCC 6.3! :/ )

Test Plan

Regenerate the configure script with: ./autogen.sh

Then, to build with either of the instrumentations enabled:

./configure --enable-asan ; make check, also run python tests, etc
./configure --enable-tsan ; make check, also run python tests, etc
./configure --enable-ubsan ; macke check, also run python tests, etc

I haven't tested enabling multiple instrumentations. They should work in combination, though -- at least ASAN and UBSAN.

Diff Detail

Repository
rABC Bitcoin ABC
Branch
asan_tsan_ubasn
Lint
No Lint Coverage
Unit
No Test Coverage
Build Status
Buildable 737
Build 737: arc lint + arc unit

Event Timeline

CCulianu edited the summary of this revision. (Show Details)

Can you try to introduce errors on purpose in the code to check that the sanitizers are properly used ?

Can you try to introduce errors on purpose in the code to check that the sanitizers are properly used ?

Sure! I'll do that today and post the results here -- how does that sound?

Note: it works. Here is some sample output.

UBSAN Results

Compiled with:

./configure --enable-ubsan

Test Code:

BOOST_AUTO_TEST_CASE(ubsan) {
    int i = INT_MAX;
    long long int ll = INT_MAX * 10LL;

    BOOST_CHECK(i*10 == ll); // should trigger UBSAN overflow warning
}

Resulting output from test_bitcoin:

Entering test module "Bitcoin Test Suite"
test/util_tests.cpp(20): Entering test suite "calin_tests"
test/util_tests.cpp(40): Entering test case "ubsan"
test/util_tests.cpp:44:5: runtime error: signed integer overflow: 2147483647 * 10 cannot be represented in type 'int'

Conclusion: UBSAN detection success!


ASAN Results

Compiled with:

./configure --enable-asan

Test Code:

BOOST_AUTO_TEST_CASE(asan) {
    const int n = 2048;
    long *buf = new long[n];
    long *buf2 = (long *)malloc(sizeof(long)*n);

    for (int i = 0; i < n; ++i) {
        buf2[i] = buf[i] = n-i;
    }

    delete [] buf;

    BOOST_CHECK(buf[100] == buf2[100]); // should trigger ASAN use of freed memory warning

    free(buf2);
}

Resulting output from test_bitcoin:

Entering test module "Bitcoin Test Suite"
test/util_tests.cpp(20): Entering test suite "calin_tests"
test/util_tests.cpp(22): Entering test case "asan"
=================================================================
==27897==ERROR: AddressSanitizer: heap-use-after-free on address 0xf1505290 at pc 0x58806c25 bp 0xff9ef148 sp 0xff9ef138
READ of size 4 at 0xf1505290 thread T0
    #0 0x58806c24 in calin_tests::asan::test_method() test/util_tests.cpp:34
    #1 0x58806172 in asan_invoker test/util_tests.cpp:22
    #2 0x57dd45bd in boost::detail::function::void_function_invoker0<void (*)(), void>::invoke(boost::detail::function::function_buffer&) /home/calin/btc-abc.phab/bitcoin-abc/depends/i686-pc-linux-gnu/share/../include/boost/function/function_template.hpp:118
    #3 0x596175f1 in boost::detail::function::function_obj_invoker0<boost::detail::forward, int>::invoke(boost::detail::function::function_buffer&) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x306d5f1)
    #4 0x59616691 in boost::execution_monitor::catch_signals(boost::function<int ()> const&) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x306c691)
    #5 0x596167e3 in boost::execution_monitor::execute(boost::function<int ()> const&) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x306c7e3)
    #6 0x59617104 in boost::execution_monitor::vexecute(boost::function<void ()> const&) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x306d104)
    #7 0x595d843b in boost::unit_test::unit_test_monitor_t::execute_and_translate(boost::function<void ()> const&, unsigned int) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x302e43b)
    #8 0x595b424e in boost::unit_test::framework::state::execute_test_tree(unsigned long, unsigned int, boost::unit_test::framework::state::random_generator_helper const*) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x300a24e)
    #9 0x595b4779 in boost::unit_test::framework::state::execute_test_tree(unsigned long, unsigned int, boost::unit_test::framework::state::random_generator_helper const*) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x300a779)
    #10 0x595ad509 in boost::unit_test::framework::run(unsigned long, bool) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x3003509)
    #11 0x595d5b88 in boost::unit_test::unit_test_main(boost::unit_test::test_suite* (*)(int, char**), int, char**) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x302bb88)
    #12 0x57d16c0e in main (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x176cc0e)
    #13 0xf68e3275 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18275)
    #14 0x57d17c16  (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x176dc16)

0xf1505290 is located 400 bytes inside of 8192-byte region [0xf1505100,0xf1507100)
freed by thread T0 here:
    #0 0xf7217334 in operator delete[](void*) (/usr/lib32/libasan.so.3+0xc5334)
    #1 0x588068a2 in calin_tests::asan::test_method() test/util_tests.cpp:31
    #2 0x58806172 in asan_invoker test/util_tests.cpp:22
    #3 0x57dd45bd in boost::detail::function::void_function_invoker0<void (*)(), void>::invoke(boost::detail::function::function_buffer&) /home/calin/btc-abc.phab/bitcoin-abc/depends/i686-pc-linux-gnu/share/../include/boost/function/function_template.hpp:118
    #4 0x596175f1 in boost::detail::function::function_obj_invoker0<boost::detail::forward, int>::invoke(boost::detail::function::function_buffer&) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x306d5f1)
    #5 0x59617104 in boost::execution_monitor::vexecute(boost::function<void ()> const&) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x306d104)
    #6 0x595b424e in boost::unit_test::framework::state::execute_test_tree(unsigned long, unsigned int, boost::unit_test::framework::state::random_generator_helper const*) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x300a24e)
    #7 0x595b4779 in boost::unit_test::framework::state::execute_test_tree(unsigned long, unsigned int, boost::unit_test::framework::state::random_generator_helper const*) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x300a779)
    #8 0x595ad509 in boost::unit_test::framework::run(unsigned long, bool) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x3003509)
    #9 0x595d5b88 in boost::unit_test::unit_test_main(boost::unit_test::test_suite* (*)(int, char**), int, char**) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x302bb88)
    #10 0x57d16c0e in main (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x176cc0e)
    #11 0xf68e3275 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18275)

previously allocated by thread T0 here:
    #0 0xf7216ca4 in operator new[](unsigned int) (/usr/lib32/libasan.so.3+0xc4ca4)
    #1 0x588066fc in calin_tests::asan::test_method() test/util_tests.cpp:24
    #2 0x58806172 in asan_invoker test/util_tests.cpp:22
    #3 0x57dd45bd in boost::detail::function::void_function_invoker0<void (*)(), void>::invoke(boost::detail::function::function_buffer&) /home/calin/btc-abc.phab/bitcoin-abc/depends/i686-pc-linux-gnu/share/../include/boost/function/function_template.hpp:118
    #4 0x596175f1 in boost::detail::function::function_obj_invoker0<boost::detail::forward, int>::invoke(boost::detail::function::function_buffer&) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x306d5f1)
    #5 0x59617104 in boost::execution_monitor::vexecute(boost::function<void ()> const&) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x306d104)
    #6 0x595b424e in boost::unit_test::framework::state::execute_test_tree(unsigned long, unsigned int, boost::unit_test::framework::state::random_generator_helper const*) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x300a24e)
    #7 0x595b4779 in boost::unit_test::framework::state::execute_test_tree(unsigned long, unsigned int, boost::unit_test::framework::state::random_generator_helper const*) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x300a779)
    #8 0x595ad509 in boost::unit_test::framework::run(unsigned long, bool) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x3003509)
    #9 0x595d5b88 in boost::unit_test::unit_test_main(boost::unit_test::test_suite* (*)(int, char**), int, char**) (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x302bb88)
    #10 0x57d16c0e in main (/home/calin/btc-abc.phab/bitcoin-abc/build/bitcoin-i686-pc-linux-gnu/src/test/test_bitcoin+0x176cc0e)
    #11 0xf68e3275 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18275)

SUMMARY: AddressSanitizer: heap-use-after-free test/util_tests.cpp:34 in calin_tests::asan::test_method()
Shadow bytes around the buggy address:
  0x3e2a0a00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x3e2a0a10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x3e2a0a20: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x3e2a0a30: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x3e2a0a40: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x3e2a0a50: fd fd[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x3e2a0a60: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x3e2a0a70: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x3e2a0a80: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x3e2a0a90: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x3e2a0aa0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==27897==ABORTING

Conclusion: ASAN detection success!

TSAN Results

Note I had to do this on a Mac because on Linux there are issues with TSAN and newer kernels, and I didn't feel like hunting down the correct GCC version and/or downgrading my kernel.

Compiled with:

./configure --enable-tsan

Test Code:

BOOST_AUTO_TEST_CASE(tsan) {
    long N = 0;
    static const long limit = 2000000000L;

    auto lambda = [](long & idx){
        for (long i = 0; idx < limit; ++i)
            idx = i-1;
    };

    std::thread t1(lambda, std::ref(N));
    std::thread t2(lambda, std::ref(N));

    t1.join();
    t2.join();

    BOOST_CHECK(N == limit);
}

Resulting output from test_bitcoin:

Entering test module "Bitcoin Test Suite"
test/util_tests.cpp:21: Entering test suite "calin_tests"
test/util_tests.cpp:23: Entering test case "tsan"
==================
WARNING: ThreadSanitizer: data race (pid=32582)
  Read of size 8 at 0x7fff5724cac0 by thread T2:
    #0 calin_tests::tsan::test_method()::$_0::operator()(long&) const util_tests.cpp:28 (test_bitcoin+0x00010059c584)
    #1 void* std::__1::__thread_proxy<std::__1::tuple<calin_tests::tsan::test_method()::$_0, std::__1::reference_wrapper<long> > >(void*) __functional_base:416 (test_bitcoin+0x00010059c391)

  Previous write of size 8 at 0x7fff5724cac0 by thread T1:
    #0 calin_tests::tsan::test_method()::$_0::operator()(long&) const util_tests.cpp:29 (test_bitcoin+0x00010059c5b4)
    #1 void* std::__1::__thread_proxy<std::__1::tuple<calin_tests::tsan::test_method()::$_0, std::__1::reference_wrapper<long> > >(void*) __functional_base:416 (test_bitcoin+0x00010059c391)

  Location is stack of main thread.

  Thread T2 (tid=10163645, running) created by main thread at:
    #0 pthread_create <null>:144 (libclang_rt.tsan_osx_dynamic.dylib+0x000000024410)
    #1 std::__1::thread::thread<calin_tests::tsan::test_method()::$_0&, std::__1::reference_wrapper<long>, void>(calin_tests::tsan::test_method()::$_0&&&, std::__1::reference_wrapper<long>&&) thread:369 (test_bitcoin+0x00010059bcd0)
    #2 std::__1::thread::thread<calin_tests::tsan::test_method()::$_0&, std::__1::reference_wrapper<long>, void>(calin_tests::tsan::test_method()::$_0&&&, std::__1::reference_wrapper<long>&&) thread:365 (test_bitcoin+0x000100552396)
    #3 calin_tests::tsan::test_method() util_tests.cpp:33 (test_bitcoin+0x0001005520a3)
    #4 calin_tests::tsan_invoker() util_tests.cpp:23 (test_bitcoin+0x000100551d53)
    #5 boost::detail::function::void_function_invoker0<void (*)(), void>::invoke(boost::detail::function::function_buffer&) function_template.hpp:118 (test_bitcoin+0x00010004f5de)
    #6 boost::detail::function::function_obj_invoker0<boost::detail::forward, int>::invoke(boost::detail::function::function_buffer&) <null>:52 (libboost_unit_test_framework-mt.dylib+0x000000008b6e)
    #7 start <null>:29 (libdyld.dylib+0x0000000035ac)

  Thread T1 (tid=10163644, running) created by main thread at:
    #0 pthread_create <null>:144 (libclang_rt.tsan_osx_dynamic.dylib+0x000000024410)
    #1 std::__1::thread::thread<calin_tests::tsan::test_method()::$_0&, std::__1::reference_wrapper<long>, void>(calin_tests::tsan::test_method()::$_0&&&, std::__1::reference_wrapper<long>&&) thread:369 (test_bitcoin+0x00010059bcd0)
    #2 std::__1::thread::thread<calin_tests::tsan::test_method()::$_0&, std::__1::reference_wrapper<long>, void>(calin_tests::tsan::test_method()::$_0&&&, std::__1::reference_wrapper<long>&&) thread:365 (test_bitcoin+0x000100552396)
    #3 calin_tests::tsan::test_method() util_tests.cpp:32 (test_bitcoin+0x000100552024)
    #4 calin_tests::tsan_invoker() util_tests.cpp:23 (test_bitcoin+0x000100551d53)
    #5 boost::detail::function::void_function_invoker0<void (*)(), void>::invoke(boost::detail::function::function_buffer&) function_template.hpp:118 (test_bitcoin+0x00010004f5de)
    #6 boost::detail::function::function_obj_invoker0<boost::detail::forward, int>::invoke(boost::detail::function::function_buffer&) <null>:52 (libboost_unit_test_framework-mt.dylib+0x000000008b6e)
    #7 start <null>:29 (libdyld.dylib+0x0000000035ac)

SUMMARY: ThreadSanitizer: data race util_tests.cpp:28 in calin_tests::tsan::test_method()::$_0::operator()(long&) const
==================

Conclusion: TSAN detection success!

CCulianu edited the test plan for this revision. (Show Details)

Added some useful output to end of configure script to indicate which sanitizers, if any, are enabled

This revision is now accepted and ready to land.Aug 16 2017, 21:25
This revision was automatically updated to reflect the committed changes.